@earendil-works/pi-coding-agent 0.84.3 → 0.85.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 (533) hide show
  1. package/CHANGELOG.md +98 -2
  2. package/README.md +8 -5
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +10 -7
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/bun/runtime-setup.d.ts +2 -0
  7. package/dist/bun/runtime-setup.d.ts.map +1 -0
  8. package/dist/bun/runtime-setup.js +9 -0
  9. package/dist/bun/runtime-setup.js.map +1 -0
  10. package/dist/bundle/chunks/anthropic-messages-QAQLEQIY.js +6 -0
  11. package/dist/bundle/chunks/{azure-openai-responses-5ENVCQ7U.js → azure-openai-responses-Q7MJBRE6.js} +1 -1
  12. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  13. package/dist/bundle/chunks/{chunk-GPPBJGBU.js → chunk-2KVJKXS2.js} +1 -1
  14. package/dist/bundle/chunks/chunk-7ZC4UXSL.js +2 -0
  15. package/dist/bundle/chunks/chunk-EQ6DR6XK.js +5 -0
  16. package/dist/bundle/chunks/{chunk-MNAIPA3J.js → chunk-IDDQWTHI.js} +1 -1
  17. package/dist/bundle/chunks/chunk-LZKJTKCT.js +2 -0
  18. package/dist/bundle/chunks/{chunk-NBBFIJUL.js → chunk-OUXLLA64.js} +2 -2
  19. package/dist/bundle/chunks/chunk-R7UUJ4C2.js +42 -0
  20. package/dist/bundle/chunks/chunk-USJZMAIR.js +2 -0
  21. package/dist/bundle/chunks/chunk-WBE4J4AU.js +9 -0
  22. package/dist/bundle/chunks/chunk-WZB2R5YO.js +1558 -0
  23. package/dist/bundle/chunks/github-copilot.js +1 -1
  24. package/dist/bundle/chunks/{google-generative-ai-XDKMGBCJ.js → google-generative-ai-BSMTYDAM.js} +1 -1
  25. package/dist/bundle/chunks/{google-vertex-D5FGEO3Y.js → google-vertex-5C3HASOJ.js} +1 -1
  26. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +13 -0
  27. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  28. package/dist/bundle/chunks/{mistral-conversations-YK73UAOZ.js → mistral-conversations-4M6BZBX6.js} +2 -2
  29. package/dist/bundle/chunks/node-EM5FIVUN.js +16 -0
  30. package/dist/bundle/chunks/node-TWVVAGPO.js +2 -0
  31. package/dist/bundle/chunks/openai-codex-responses-YJOO52RW.js +10 -0
  32. package/dist/bundle/chunks/openai-completions-QOT3QX7D.js +7 -0
  33. package/dist/bundle/chunks/openai-responses-U7LNPQN4.js +2 -0
  34. package/dist/bundle/chunks/{pi-messages-TPFH44NP.js → pi-messages-EGZIFQGG.js} +1 -1
  35. package/dist/bundle/cli.js +1 -1
  36. package/dist/bundle/client.js +1 -1
  37. package/dist/bundle/coordinator.js +3 -0
  38. package/dist/bundle/index.js +1 -1
  39. package/dist/bundle/rpc-entry.js +1 -1
  40. package/dist/cli/args.d.ts.map +1 -1
  41. package/dist/cli/args.js +2 -0
  42. package/dist/cli/args.js.map +1 -1
  43. package/dist/cli/config-selector.d.ts.map +1 -1
  44. package/dist/cli/config-selector.js +2 -1
  45. package/dist/cli/config-selector.js.map +1 -1
  46. package/dist/cli/experimental/cli.d.ts +7 -3
  47. package/dist/cli/experimental/cli.d.ts.map +1 -1
  48. package/dist/cli/experimental/cli.js +6 -2
  49. package/dist/cli/experimental/cli.js.map +1 -1
  50. package/dist/cli/experimental/command-options.d.ts +23 -12
  51. package/dist/cli/experimental/command-options.d.ts.map +1 -1
  52. package/dist/cli/experimental/command-options.js +69 -20
  53. package/dist/cli/experimental/command-options.js.map +1 -1
  54. package/dist/cli/experimental/command.d.ts +10 -3
  55. package/dist/cli/experimental/command.d.ts.map +1 -1
  56. package/dist/cli/experimental/command.js +28 -14
  57. package/dist/cli/experimental/command.js.map +1 -1
  58. package/dist/cli/experimental/commands/client.d.ts +8 -2
  59. package/dist/cli/experimental/commands/client.d.ts.map +1 -1
  60. package/dist/cli/experimental/commands/client.js +43 -5
  61. package/dist/cli/experimental/commands/client.js.map +1 -1
  62. package/dist/cli/experimental/commands/server.d.ts +7 -3
  63. package/dist/cli/experimental/commands/server.d.ts.map +1 -1
  64. package/dist/cli/experimental/commands/server.js +27 -8
  65. package/dist/cli/experimental/commands/server.js.map +1 -1
  66. package/dist/cli/startup-ui.d.ts.map +1 -1
  67. package/dist/cli/startup-ui.js +2 -1
  68. package/dist/cli/startup-ui.js.map +1 -1
  69. package/dist/cli.d.ts.map +1 -1
  70. package/dist/cli.js +23 -14
  71. package/dist/cli.js.map +1 -1
  72. package/dist/client/index.d.ts +1 -2
  73. package/dist/client/index.d.ts.map +1 -1
  74. package/dist/client/index.js +1 -2
  75. package/dist/client/index.js.map +1 -1
  76. package/dist/config.d.ts +1 -0
  77. package/dist/config.d.ts.map +1 -1
  78. package/dist/config.js +1 -0
  79. package/dist/config.js.map +1 -1
  80. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  81. package/dist/core/agent-session-runtime.js +15 -7
  82. package/dist/core/agent-session-runtime.js.map +1 -1
  83. package/dist/core/agent-session.d.ts +14 -7
  84. package/dist/core/agent-session.d.ts.map +1 -1
  85. package/dist/core/agent-session.js +91 -15
  86. package/dist/core/agent-session.js.map +1 -1
  87. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  88. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  89. package/dist/core/compaction/branch-summarization.js +6 -4
  90. package/dist/core/compaction/branch-summarization.js.map +1 -1
  91. package/dist/core/compaction/compaction.d.ts +5 -0
  92. package/dist/core/compaction/compaction.d.ts.map +1 -1
  93. package/dist/core/compaction/compaction.js +19 -5
  94. package/dist/core/compaction/compaction.js.map +1 -1
  95. package/dist/core/extensions/index.d.ts +1 -1
  96. package/dist/core/extensions/index.d.ts.map +1 -1
  97. package/dist/core/extensions/index.js.map +1 -1
  98. package/dist/core/extensions/loader.d.ts.map +1 -1
  99. package/dist/core/extensions/loader.js +1 -2
  100. package/dist/core/extensions/loader.js.map +1 -1
  101. package/dist/core/extensions/runner.d.ts +5 -0
  102. package/dist/core/extensions/runner.d.ts.map +1 -1
  103. package/dist/core/extensions/runner.js +45 -1
  104. package/dist/core/extensions/runner.js.map +1 -1
  105. package/dist/core/extensions/types.d.ts +26 -3
  106. package/dist/core/extensions/types.d.ts.map +1 -1
  107. package/dist/core/extensions/types.js.map +1 -1
  108. package/dist/core/http-dispatcher.d.ts.map +1 -1
  109. package/dist/core/http-dispatcher.js +2 -0
  110. package/dist/core/http-dispatcher.js.map +1 -1
  111. package/dist/core/messages.d.ts +1 -1
  112. package/dist/core/messages.d.ts.map +1 -1
  113. package/dist/core/messages.js.map +1 -1
  114. package/dist/core/model-config.d.ts +15 -0
  115. package/dist/core/model-config.d.ts.map +1 -1
  116. package/dist/core/model-config.js +3 -0
  117. package/dist/core/model-config.js.map +1 -1
  118. package/dist/core/model-runtime.d.ts +1 -0
  119. package/dist/core/model-runtime.d.ts.map +1 -1
  120. package/dist/core/model-runtime.js +5 -2
  121. package/dist/core/model-runtime.js.map +1 -1
  122. package/dist/core/provider-composer.d.ts.map +1 -1
  123. package/dist/core/provider-composer.js +8 -2
  124. package/dist/core/provider-composer.js.map +1 -1
  125. package/dist/core/session-manager.d.ts +3 -2
  126. package/dist/core/session-manager.d.ts.map +1 -1
  127. package/dist/core/session-manager.js +44 -15
  128. package/dist/core/session-manager.js.map +1 -1
  129. package/dist/core/settings-manager.d.ts +8 -1
  130. package/dist/core/settings-manager.d.ts.map +1 -1
  131. package/dist/core/settings-manager.js +17 -0
  132. package/dist/core/settings-manager.js.map +1 -1
  133. package/dist/core/skills.d.ts +1 -1
  134. package/dist/core/skills.d.ts.map +1 -1
  135. package/dist/core/skills.js +4 -2
  136. package/dist/core/skills.js.map +1 -1
  137. package/dist/core/system-prompt.d.ts.map +1 -1
  138. package/dist/core/system-prompt.js +8 -9
  139. package/dist/core/system-prompt.js.map +1 -1
  140. package/dist/core/tools/bash.d.ts.map +1 -1
  141. package/dist/core/tools/bash.js +3 -117
  142. package/dist/core/tools/bash.js.map +1 -1
  143. package/dist/core/tools/edit.d.ts +1 -12
  144. package/dist/core/tools/edit.d.ts.map +1 -1
  145. package/dist/core/tools/edit.js +5 -163
  146. package/dist/core/tools/edit.js.map +1 -1
  147. package/dist/core/tools/find.d.ts.map +1 -1
  148. package/dist/core/tools/find.js +4 -55
  149. package/dist/core/tools/find.js.map +1 -1
  150. package/dist/core/tools/grep.d.ts.map +1 -1
  151. package/dist/core/tools/grep.js +4 -60
  152. package/dist/core/tools/grep.js.map +1 -1
  153. package/dist/core/tools/ls.d.ts.map +1 -1
  154. package/dist/core/tools/ls.js +4 -49
  155. package/dist/core/tools/ls.js.map +1 -1
  156. package/dist/core/tools/read.d.ts.map +1 -1
  157. package/dist/core/tools/read.js +4 -120
  158. package/dist/core/tools/read.js.map +1 -1
  159. package/dist/core/tools/renderers/bash.d.ts +12 -0
  160. package/dist/core/tools/renderers/bash.d.ts.map +1 -0
  161. package/dist/core/tools/renderers/bash.js +130 -0
  162. package/dist/core/tools/renderers/bash.js.map +1 -0
  163. package/dist/core/tools/renderers/edit.d.ts +23 -0
  164. package/dist/core/tools/renderers/edit.d.ts.map +1 -0
  165. package/dist/core/tools/renderers/edit.js +167 -0
  166. package/dist/core/tools/renderers/edit.js.map +1 -0
  167. package/dist/core/tools/renderers/find.d.ts +10 -0
  168. package/dist/core/tools/renderers/find.d.ts.map +1 -0
  169. package/dist/core/tools/renderers/find.js +64 -0
  170. package/dist/core/tools/renderers/find.js.map +1 -0
  171. package/dist/core/tools/renderers/grep.d.ts +10 -0
  172. package/dist/core/tools/renderers/grep.d.ts.map +1 -0
  173. package/dist/core/tools/renderers/grep.js +69 -0
  174. package/dist/core/tools/renderers/grep.js.map +1 -0
  175. package/dist/core/tools/renderers/index.d.ts +28 -0
  176. package/dist/core/tools/renderers/index.d.ts.map +1 -0
  177. package/dist/core/tools/renderers/index.js +47 -0
  178. package/dist/core/tools/renderers/index.js.map +1 -0
  179. package/dist/core/tools/renderers/ls.d.ts +10 -0
  180. package/dist/core/tools/renderers/ls.d.ts.map +1 -0
  181. package/dist/core/tools/renderers/ls.js +58 -0
  182. package/dist/core/tools/renderers/ls.js.map +1 -0
  183. package/dist/core/tools/renderers/read.d.ts +11 -0
  184. package/dist/core/tools/renderers/read.d.ts.map +1 -0
  185. package/dist/core/tools/renderers/read.js +129 -0
  186. package/dist/core/tools/renderers/read.js.map +1 -0
  187. package/dist/core/tools/renderers/write.d.ts +10 -0
  188. package/dist/core/tools/renderers/write.d.ts.map +1 -0
  189. package/dist/core/tools/renderers/write.js +153 -0
  190. package/dist/core/tools/renderers/write.js.map +1 -0
  191. package/dist/core/tools/write.d.ts.map +1 -1
  192. package/dist/core/tools/write.js +5 -146
  193. package/dist/core/tools/write.js.map +1 -1
  194. package/dist/experimental/client-runtime.d.ts +43 -0
  195. package/dist/experimental/client-runtime.d.ts.map +1 -0
  196. package/dist/experimental/client-runtime.js +183 -0
  197. package/dist/experimental/client-runtime.js.map +1 -0
  198. package/dist/experimental/client-tui-chat.d.ts +14 -0
  199. package/dist/experimental/client-tui-chat.d.ts.map +1 -0
  200. package/dist/experimental/client-tui-chat.js +171 -0
  201. package/dist/experimental/client-tui-chat.js.map +1 -0
  202. package/dist/experimental/client-tui.d.ts +37 -0
  203. package/dist/experimental/client-tui.d.ts.map +1 -0
  204. package/dist/experimental/client-tui.js +702 -0
  205. package/dist/experimental/client-tui.js.map +1 -0
  206. package/dist/experimental/client.d.ts +25 -0
  207. package/dist/experimental/client.d.ts.map +1 -0
  208. package/dist/experimental/client.js +99 -0
  209. package/dist/experimental/client.js.map +1 -0
  210. package/dist/experimental/coordinator-entry.d.ts +3 -0
  211. package/dist/experimental/coordinator-entry.d.ts.map +1 -0
  212. package/dist/experimental/coordinator-entry.js +11 -0
  213. package/dist/experimental/coordinator-entry.js.map +1 -0
  214. package/dist/experimental/coordinator.d.ts +38 -0
  215. package/dist/experimental/coordinator.d.ts.map +1 -0
  216. package/dist/experimental/coordinator.js +570 -0
  217. package/dist/experimental/coordinator.js.map +1 -0
  218. package/dist/experimental/mini/main.d.ts +7 -0
  219. package/dist/experimental/mini/main.d.ts.map +1 -0
  220. package/dist/experimental/mini/main.js +21 -0
  221. package/dist/experimental/mini/main.js.map +1 -0
  222. package/dist/experimental/mini/server/entry.d.ts +3 -0
  223. package/dist/experimental/mini/server/entry.d.ts.map +1 -0
  224. package/dist/experimental/mini/server/entry.js +11 -0
  225. package/dist/experimental/mini/server/entry.js.map +1 -0
  226. package/dist/experimental/mini/server/run.d.ts +13 -0
  227. package/dist/experimental/mini/server/run.d.ts.map +1 -0
  228. package/dist/experimental/mini/server/run.js +150 -0
  229. package/dist/experimental/mini/server/run.js.map +1 -0
  230. package/dist/experimental/mini/shared/protocol.d.ts +127 -0
  231. package/dist/experimental/mini/shared/protocol.d.ts.map +1 -0
  232. package/dist/experimental/mini/shared/protocol.js +13 -0
  233. package/dist/experimental/mini/shared/protocol.js.map +1 -0
  234. package/dist/experimental/mini/shared/rpc.d.ts +49 -0
  235. package/dist/experimental/mini/shared/rpc.d.ts.map +1 -0
  236. package/dist/experimental/mini/shared/rpc.js +171 -0
  237. package/dist/experimental/mini/shared/rpc.js.map +1 -0
  238. package/dist/experimental/mini/shared/transport.d.ts +35 -0
  239. package/dist/experimental/mini/shared/transport.d.ts.map +1 -0
  240. package/dist/experimental/mini/shared/transport.js +94 -0
  241. package/dist/experimental/mini/shared/transport.js.map +1 -0
  242. package/dist/experimental/mini/tui/run.d.ts +9 -0
  243. package/dist/experimental/mini/tui/run.d.ts.map +1 -0
  244. package/dist/experimental/mini/tui/run.js +62 -0
  245. package/dist/experimental/mini/tui/run.js.map +1 -0
  246. package/dist/experimental/mini/tui/session.d.ts +26 -0
  247. package/dist/experimental/mini/tui/session.d.ts.map +1 -0
  248. package/dist/experimental/mini/tui/session.js +94 -0
  249. package/dist/experimental/mini/tui/session.js.map +1 -0
  250. package/dist/experimental/mini/tui/view.d.ts +10 -0
  251. package/dist/experimental/mini/tui/view.d.ts.map +1 -0
  252. package/dist/experimental/mini/tui/view.js +459 -0
  253. package/dist/experimental/mini/tui/view.js.map +1 -0
  254. package/dist/experimental/mini/worker/entry.d.ts +3 -0
  255. package/dist/experimental/mini/worker/entry.d.ts.map +1 -0
  256. package/dist/experimental/mini/worker/entry.js +10 -0
  257. package/dist/experimental/mini/worker/entry.js.map +1 -0
  258. package/dist/experimental/mini/worker/lane-service.d.ts +44 -0
  259. package/dist/experimental/mini/worker/lane-service.d.ts.map +1 -0
  260. package/dist/experimental/mini/worker/lane-service.js +97 -0
  261. package/dist/experimental/mini/worker/lane-service.js.map +1 -0
  262. package/dist/experimental/mini/worker/models-service.d.ts +19 -0
  263. package/dist/experimental/mini/worker/models-service.d.ts.map +1 -0
  264. package/dist/experimental/mini/worker/models-service.js +136 -0
  265. package/dist/experimental/mini/worker/models-service.js.map +1 -0
  266. package/dist/experimental/mini/worker/run.d.ts +14 -0
  267. package/dist/experimental/mini/worker/run.d.ts.map +1 -0
  268. package/dist/experimental/mini/worker/run.js +88 -0
  269. package/dist/experimental/mini/worker/run.js.map +1 -0
  270. package/dist/experimental/plugin.d.ts +4 -0
  271. package/dist/experimental/plugin.d.ts.map +1 -0
  272. package/dist/experimental/plugin.js +4 -0
  273. package/dist/experimental/plugin.js.map +1 -0
  274. package/dist/experimental/plugins/bundled.d.ts +7 -0
  275. package/dist/experimental/plugins/bundled.d.ts.map +1 -0
  276. package/dist/experimental/plugins/bundled.js +48 -0
  277. package/dist/experimental/plugins/bundled.js.map +1 -0
  278. package/dist/experimental/plugins/package.d.ts +18 -0
  279. package/dist/experimental/plugins/package.d.ts.map +1 -0
  280. package/dist/experimental/plugins/package.js +113 -0
  281. package/dist/experimental/plugins/package.js.map +1 -0
  282. package/dist/experimental/process.d.ts +20 -0
  283. package/dist/experimental/process.d.ts.map +1 -0
  284. package/dist/experimental/process.js +85 -0
  285. package/dist/experimental/process.js.map +1 -0
  286. package/dist/experimental/radius-auth.d.ts +17 -0
  287. package/dist/experimental/radius-auth.d.ts.map +1 -0
  288. package/dist/experimental/radius-auth.js +52 -0
  289. package/dist/experimental/radius-auth.js.map +1 -0
  290. package/dist/experimental/radius-relay.d.ts +90 -0
  291. package/dist/experimental/radius-relay.d.ts.map +1 -0
  292. package/dist/experimental/radius-relay.js +582 -0
  293. package/dist/experimental/radius-relay.js.map +1 -0
  294. package/dist/experimental/server.d.ts +75 -0
  295. package/dist/experimental/server.d.ts.map +1 -0
  296. package/dist/experimental/server.js +682 -0
  297. package/dist/experimental/server.js.map +1 -0
  298. package/dist/experimental/services/agent-controller-provider.d.ts +4 -0
  299. package/dist/experimental/services/agent-controller-provider.d.ts.map +1 -0
  300. package/dist/experimental/services/agent-controller-provider.js +84 -0
  301. package/dist/experimental/services/agent-controller-provider.js.map +1 -0
  302. package/dist/experimental/services/agent-controller.d.ts +57 -0
  303. package/dist/experimental/services/agent-controller.d.ts.map +1 -0
  304. package/dist/experimental/services/agent-controller.js +3 -0
  305. package/dist/experimental/services/agent-controller.js.map +1 -0
  306. package/dist/experimental/services/connection.d.ts +40 -0
  307. package/dist/experimental/services/connection.d.ts.map +1 -0
  308. package/dist/experimental/services/connection.js +300 -0
  309. package/dist/experimental/services/connection.js.map +1 -0
  310. package/dist/experimental/services/models-provider.d.ts +16 -0
  311. package/dist/experimental/services/models-provider.d.ts.map +1 -0
  312. package/dist/experimental/services/models-provider.js +116 -0
  313. package/dist/experimental/services/models-provider.js.map +1 -0
  314. package/dist/experimental/services/models.d.ts +36 -0
  315. package/dist/experimental/services/models.d.ts.map +1 -0
  316. package/dist/experimental/services/models.js +3 -0
  317. package/dist/experimental/services/models.js.map +1 -0
  318. package/dist/experimental/services/plugins.d.ts +16 -0
  319. package/dist/experimental/services/plugins.d.ts.map +1 -0
  320. package/dist/experimental/services/plugins.js +4 -0
  321. package/dist/experimental/services/plugins.js.map +1 -0
  322. package/dist/experimental/services/presentation-ui.d.ts +13 -0
  323. package/dist/experimental/services/presentation-ui.d.ts.map +1 -0
  324. package/dist/experimental/services/presentation-ui.js +3 -0
  325. package/dist/experimental/services/presentation-ui.js.map +1 -0
  326. package/dist/experimental/services/server.d.ts +19 -0
  327. package/dist/experimental/services/server.d.ts.map +1 -0
  328. package/dist/experimental/services/server.js +104 -0
  329. package/dist/experimental/services/server.js.map +1 -0
  330. package/dist/experimental/services/sessions.d.ts +28 -0
  331. package/dist/experimental/services/sessions.d.ts.map +1 -0
  332. package/dist/experimental/services/sessions.js +4 -0
  333. package/dist/experimental/services/sessions.js.map +1 -0
  334. package/dist/experimental/services/slash-commands-provider.d.ts +14 -0
  335. package/dist/experimental/services/slash-commands-provider.d.ts.map +1 -0
  336. package/dist/experimental/services/slash-commands-provider.js +200 -0
  337. package/dist/experimental/services/slash-commands-provider.js.map +1 -0
  338. package/dist/experimental/services/slash-commands.d.ts +24 -0
  339. package/dist/experimental/services/slash-commands.d.ts.map +1 -0
  340. package/dist/experimental/services/slash-commands.js +3 -0
  341. package/dist/experimental/services/slash-commands.js.map +1 -0
  342. package/dist/experimental/services/transcript-provider.d.ts +12 -0
  343. package/dist/experimental/services/transcript-provider.d.ts.map +1 -0
  344. package/dist/experimental/services/transcript-provider.js +109 -0
  345. package/dist/experimental/services/transcript-provider.js.map +1 -0
  346. package/dist/experimental/services/transcript.d.ts +13 -0
  347. package/dist/experimental/services/transcript.d.ts.map +1 -0
  348. package/dist/experimental/services/transcript.js +3 -0
  349. package/dist/experimental/services/transcript.js.map +1 -0
  350. package/dist/experimental/services/worker.d.ts +28 -0
  351. package/dist/experimental/services/worker.d.ts.map +1 -0
  352. package/dist/experimental/services/worker.js +107 -0
  353. package/dist/experimental/services/worker.js.map +1 -0
  354. package/dist/experimental/session-worker-manager.d.ts +24 -0
  355. package/dist/experimental/session-worker-manager.d.ts.map +1 -0
  356. package/dist/experimental/session-worker-manager.js +691 -0
  357. package/dist/experimental/session-worker-manager.js.map +1 -0
  358. package/dist/experimental/session-worker.d.ts +188 -0
  359. package/dist/experimental/session-worker.d.ts.map +1 -0
  360. package/dist/experimental/session-worker.js +785 -0
  361. package/dist/experimental/session-worker.js.map +1 -0
  362. package/dist/experimental/source-resolver.d.ts +2 -0
  363. package/dist/experimental/source-resolver.d.ts.map +1 -0
  364. package/dist/experimental/source-resolver.js +71 -0
  365. package/dist/experimental/source-resolver.js.map +1 -0
  366. package/dist/extensions/llama/client.d.ts +6 -0
  367. package/dist/extensions/llama/client.d.ts.map +1 -1
  368. package/dist/extensions/llama/client.js +7 -0
  369. package/dist/extensions/llama/client.js.map +1 -1
  370. package/dist/extensions/llama/provider.d.ts +3 -1
  371. package/dist/extensions/llama/provider.d.ts.map +1 -1
  372. package/dist/extensions/llama/provider.js +27 -6
  373. package/dist/extensions/llama/provider.js.map +1 -1
  374. package/dist/index.d.ts +3 -2
  375. package/dist/index.d.ts.map +1 -1
  376. package/dist/index.js +1 -0
  377. package/dist/index.js.map +1 -1
  378. package/dist/main.d.ts.map +1 -1
  379. package/dist/main.js +128 -1
  380. package/dist/main.js.map +1 -1
  381. package/dist/modes/interactive/chat-viewport.d.ts +20 -0
  382. package/dist/modes/interactive/chat-viewport.d.ts.map +1 -0
  383. package/dist/modes/interactive/chat-viewport.js +28 -0
  384. package/dist/modes/interactive/chat-viewport.js.map +1 -0
  385. package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
  386. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/assistant-message.js +18 -10
  388. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  389. package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
  390. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/custom-editor.js +39 -1
  392. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  393. package/dist/modes/interactive/components/index.d.ts +1 -1
  394. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  395. package/dist/modes/interactive/components/index.js.map +1 -1
  396. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  397. package/dist/modes/interactive/components/model-selector.js +5 -14
  398. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  399. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/scoped-models-selector.js +13 -15
  401. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  402. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  403. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  404. package/dist/modes/interactive/components/settings-selector.js +24 -10
  405. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  406. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  407. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  408. package/dist/modes/interactive/components/status-indicator.js +10 -3
  409. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  410. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  411. package/dist/modes/interactive/components/thinking-selector.js +2 -2
  412. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  413. package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
  414. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  415. package/dist/modes/interactive/components/tool-execution.js +35 -33
  416. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  417. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  418. package/dist/modes/interactive/components/trust-selector.js +2 -2
  419. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  420. package/dist/modes/interactive/interactive-mode.d.ts +11 -14
  421. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  422. package/dist/modes/interactive/interactive-mode.js +140 -101
  423. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  424. package/dist/modes/interactive/session-share.d.ts.map +1 -1
  425. package/dist/modes/interactive/session-share.js +8 -12
  426. package/dist/modes/interactive/session-share.js.map +1 -1
  427. package/dist/modes/interactive/theme/dark.json +2 -1
  428. package/dist/modes/interactive/theme/light.json +2 -1
  429. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  430. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  431. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  432. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  433. package/dist/modes/interactive/theme/theme-json.d.ts +84 -0
  434. package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -0
  435. package/dist/modes/interactive/theme/theme-json.js +130 -0
  436. package/dist/modes/interactive/theme/theme-json.js.map +1 -0
  437. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  438. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  439. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  440. package/dist/modes/interactive/theme/theme.js +18 -119
  441. package/dist/modes/interactive/theme/theme.js.map +1 -1
  442. package/dist/modes/interactive/tui-renderer.d.ts +21 -0
  443. package/dist/modes/interactive/tui-renderer.d.ts.map +1 -0
  444. package/dist/modes/interactive/tui-renderer.js +66 -0
  445. package/dist/modes/interactive/tui-renderer.js.map +1 -0
  446. package/dist/modes/rpc/rpc-client.d.ts +7 -0
  447. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  448. package/dist/modes/rpc/rpc-client.js +7 -0
  449. package/dist/modes/rpc/rpc-client.js.map +1 -1
  450. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  451. package/dist/modes/rpc/rpc-mode.js +3 -0
  452. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  453. package/dist/modes/rpc/rpc-types.d.ts +12 -0
  454. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  455. package/dist/modes/rpc/rpc-types.js.map +1 -1
  456. package/dist/utils/exif-orientation.d.ts.map +1 -1
  457. package/dist/utils/exif-orientation.js +2 -3
  458. package/dist/utils/exif-orientation.js.map +1 -1
  459. package/dist/utils/shell.d.ts.map +1 -1
  460. package/dist/utils/shell.js +6 -4
  461. package/dist/utils/shell.js.map +1 -1
  462. package/dist/utils/tools-manager.d.ts +1 -0
  463. package/dist/utils/tools-manager.d.ts.map +1 -1
  464. package/dist/utils/tools-manager.js +41 -19
  465. package/dist/utils/tools-manager.js.map +1 -1
  466. package/docs/compaction.md +2 -0
  467. package/docs/custom-provider.md +7 -7
  468. package/docs/environment-variables.md +5 -0
  469. package/docs/extensions.md +23 -2
  470. package/docs/keybindings.md +4 -2
  471. package/docs/models.md +3 -0
  472. package/docs/quickstart.md +1 -1
  473. package/docs/rpc.md +24 -1
  474. package/docs/sdk.md +5 -0
  475. package/docs/settings.md +17 -9
  476. package/docs/skills.md +1 -1
  477. package/docs/terminal-setup.md +27 -0
  478. package/docs/themes.md +8 -6
  479. package/docs/tui.md +20 -1
  480. package/docs/usage.md +4 -3
  481. package/examples/README.md +3 -0
  482. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  483. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  484. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  485. package/examples/extensions/dynamic-resources/dynamic.json +2 -0
  486. package/examples/extensions/gondolin/package-lock.json +2 -2
  487. package/examples/extensions/gondolin/package.json +1 -1
  488. package/examples/extensions/sandbox/package-lock.json +2 -2
  489. package/examples/extensions/sandbox/package.json +1 -1
  490. package/examples/extensions/with-deps/package-lock.json +2 -2
  491. package/examples/extensions/with-deps/package.json +1 -1
  492. package/examples/plugins/pi-example-plugin/README.md +38 -0
  493. package/examples/plugins/pi-example-plugin/package.json +10 -0
  494. package/examples/plugins/pi-example-plugin/src/contract.ts +13 -0
  495. package/examples/plugins/pi-example-plugin/src/session.ts +23 -0
  496. package/examples/plugins/pi-example-plugin/src/tui.ts +31 -0
  497. package/npm-shrinkwrap.json +523 -28
  498. package/package.json +12 -6
  499. package/dist/bun/register-bedrock.d.ts +0 -2
  500. package/dist/bun/register-bedrock.d.ts.map +0 -1
  501. package/dist/bun/register-bedrock.js +0 -4
  502. package/dist/bun/register-bedrock.js.map +0 -1
  503. package/dist/bundle/chunks/anthropic-messages-JWX2WP65.js +0 -45
  504. package/dist/bundle/chunks/chunk-E5KXRMZK.js +0 -1547
  505. package/dist/bundle/chunks/chunk-JUUC5AZY.js +0 -2
  506. package/dist/bundle/chunks/dist-RDWOYWHR.js +0 -13
  507. package/dist/bundle/chunks/openai-codex-responses-GJVBJXLB.js +0 -8
  508. package/dist/bundle/chunks/openai-completions-JD4WAC3R.js +0 -7
  509. package/dist/bundle/chunks/openai-responses-BSOJMSEV.js +0 -2
  510. package/dist/cli/experimental/auth.d.ts +0 -16
  511. package/dist/cli/experimental/auth.d.ts.map +0 -1
  512. package/dist/cli/experimental/auth.js +0 -13
  513. package/dist/cli/experimental/auth.js.map +0 -1
  514. package/dist/cli/experimental/commands/pi.d.ts +0 -15
  515. package/dist/cli/experimental/commands/pi.d.ts.map +0 -1
  516. package/dist/cli/experimental/commands/pi.js +0 -28
  517. package/dist/cli/experimental/commands/pi.js.map +0 -1
  518. package/dist/cli/experimental/transport-address.d.ts +0 -10
  519. package/dist/cli/experimental/transport-address.d.ts.map +0 -1
  520. package/dist/cli/experimental/transport-address.js +0 -38
  521. package/dist/cli/experimental/transport-address.js.map +0 -1
  522. package/dist/client/remote-session.d.ts +0 -53
  523. package/dist/client/remote-session.d.ts.map +0 -1
  524. package/dist/client/remote-session.js +0 -340
  525. package/dist/client/remote-session.js.map +0 -1
  526. package/dist/client/transcript.d.ts +0 -12
  527. package/dist/client/transcript.d.ts.map +0 -1
  528. package/dist/client/transcript.js +0 -98
  529. package/dist/client/transcript.js.map +0 -1
  530. package/dist/server/create-harness.d.ts +0 -26
  531. package/dist/server/create-harness.d.ts.map +0 -1
  532. package/dist/server/create-harness.js +0 -106
  533. package/dist/server/create-harness.js.map +0 -1
@@ -0,0 +1,183 @@
1
+ import { basename } from "node:path";
2
+ import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
3
+ import { Client, ServerError } from "@earendil-works/pi-client";
4
+ import { createUnixTransportFactory, discoverUnixServers } from "@earendil-works/pi-client/unix";
5
+ import { isServerId } from "@earendil-works/pi-protocol";
6
+ import { RadiusRelayAuthResolver } from "./radius-auth.js";
7
+ import { createRadiusClientTransportFactory, RadiusClientReconnect } from "./radius-relay.js";
8
+ import { activateServer, ENV_SERVER_ID, resolveServerDirectory, resolveSessionDirectory } from "./server.js";
9
+ import { AgentController } from "./services/agent-controller.js";
10
+ import { createServerServiceSource, createSessionServiceSource, } from "./services/connection.js";
11
+ import { Models } from "./services/models.js";
12
+ import { PresentationPlugins } from "./services/plugins.js";
13
+ import { SessionDirectory, SessionManagement } from "./services/sessions.js";
14
+ import { Transcript } from "./services/transcript.js";
15
+ /** Open live server/session service namespaces for one experimental presentation. */
16
+ export async function openClientRuntime(command, options = {}) {
17
+ if (command.auth !== undefined && command.connect?.transport !== "radius") {
18
+ throw new Error("Authentication is only supported for experimental Radius connections");
19
+ }
20
+ if (command.provider !== undefined && command.model === undefined) {
21
+ throw new Error("Server model provider requires a model");
22
+ }
23
+ if (command.connect && command.model !== undefined) {
24
+ throw new Error("Model selection is only valid when automatically activating a new server");
25
+ }
26
+ if (command.connect?.transport === "radius" && command.pluginPackages !== undefined) {
27
+ throw new Error("Plugin package paths can only be configured on a local Unix server");
28
+ }
29
+ const directory = resolveServerDirectory(options.directory);
30
+ let routes;
31
+ let activatedClient;
32
+ if (command.connect) {
33
+ routes = [
34
+ command.connect.transport === "radius"
35
+ ? { transport: "radius", serverId: command.connect.serverId }
36
+ : { transport: "unix", ...routeFromExplicitPath(command.connect.path) },
37
+ ];
38
+ }
39
+ else {
40
+ routes = (await discoverUnixServers({ directory })).map((route) => ({ transport: "unix", ...route }));
41
+ if (routes.length > 0 && command.model !== undefined) {
42
+ throw new Error("Model selection is only valid when automatically activating a new server");
43
+ }
44
+ if (routes.length === 0) {
45
+ const activated = await activateServer({
46
+ directory,
47
+ requestedServerId: process.env[ENV_SERVER_ID],
48
+ sessionDir: resolveSessionDirectory(),
49
+ provider: command.provider,
50
+ model: command.model,
51
+ });
52
+ routes = [{ transport: "unix", ...activated.route }];
53
+ activatedClient = activated.client;
54
+ }
55
+ }
56
+ if (command.pluginPackages !== undefined && routes.length !== 1) {
57
+ throw new Error("Plugin selection requires exactly one local server");
58
+ }
59
+ const clients = [];
60
+ const reconnectors = [];
61
+ const serviceSources = [];
62
+ const servers = [];
63
+ let disposed = false;
64
+ const dispose = async () => {
65
+ if (disposed)
66
+ return;
67
+ disposed = true;
68
+ const reconnectResults = await Promise.allSettled(reconnectors.map((reconnector) => reconnector.dispose()));
69
+ const sourceResults = await Promise.allSettled(serviceSources.map((source) => source.dispose(BACKGROUND_CONTEXT)));
70
+ const clientResults = await Promise.allSettled(clients.map((client) => client.dispose()));
71
+ const errors = [...reconnectResults, ...sourceResults, ...clientResults].flatMap((result) => result.status === "rejected" ? [result.reason] : []);
72
+ if (errors.length === 1)
73
+ throw errors[0];
74
+ if (errors.length > 1)
75
+ throw new AggregateError(errors, "Failed to dispose experimental client runtime");
76
+ };
77
+ try {
78
+ for (const route of routes) {
79
+ let client = activatedClient;
80
+ if (client === undefined) {
81
+ try {
82
+ client = await Client.connect({
83
+ serverId: route.serverId,
84
+ transportFactory: route.transport === "unix"
85
+ ? createUnixTransportFactory({ path: route.path })
86
+ : createRadiusClientTransportFactory({
87
+ serverId: route.serverId,
88
+ auth: new RadiusRelayAuthResolver(command.auth),
89
+ }),
90
+ });
91
+ }
92
+ catch (error) {
93
+ if (command.connect !== undefined ||
94
+ route.transport !== "unix" ||
95
+ !(error instanceof ServerError) ||
96
+ error.code !== "version") {
97
+ throw error;
98
+ }
99
+ client = (await activateServer({
100
+ directory,
101
+ requestedServerId: route.serverId,
102
+ sessionDir: resolveSessionDirectory(),
103
+ })).client;
104
+ }
105
+ }
106
+ activatedClient = undefined;
107
+ clients.push(client);
108
+ const server = createServerServiceSource(client);
109
+ serviceSources.push(server);
110
+ if (route.transport === "radius") {
111
+ const reconnectServices = server.open({
112
+ services: [SessionManagement],
113
+ assertAccess() { },
114
+ onError() { },
115
+ });
116
+ const reconnectManagement = reconnectServices.use(SessionManagement);
117
+ reconnectors.push(new RadiusClientReconnect(client, async (sessionId) => {
118
+ await reconnectServices.ready(BACKGROUND_CONTEXT);
119
+ await reconnectManagement.attach(sessionId, BACKGROUND_CONTEXT);
120
+ }));
121
+ }
122
+ const session = createSessionServiceSource(client);
123
+ serviceSources.push(session);
124
+ servers.push({ route, client, server, session });
125
+ }
126
+ return { servers, dispose };
127
+ }
128
+ catch (error) {
129
+ try {
130
+ await dispose();
131
+ }
132
+ catch (cleanupError) {
133
+ throw new AggregateError([error, cleanupError], "Experimental client startup and cleanup failed");
134
+ }
135
+ throw error;
136
+ }
137
+ }
138
+ /** Acquire and connect the built-in service facades used by the non-interactive client. */
139
+ export async function activateBuiltinClientServices(server) {
140
+ const serverServices = server.server.open({
141
+ services: [SessionDirectory, SessionManagement, PresentationPlugins],
142
+ assertAccess() { },
143
+ onError() { },
144
+ });
145
+ const sessionServices = server.session.open({
146
+ services: [Models, AgentController, Transcript],
147
+ assertAccess() { },
148
+ onError() { },
149
+ });
150
+ const directory = serverServices.use(SessionDirectory);
151
+ const remoteManagement = serverServices.use(SessionManagement);
152
+ const management = {
153
+ create: (options, context) => remoteManagement.create(options, context),
154
+ async remove(sessionId, context) {
155
+ const removesCurrentAttachment = server.client.attachment?.sessionId === sessionId;
156
+ await remoteManagement.remove(sessionId, context);
157
+ if (removesCurrentAttachment)
158
+ await server.session.whenDetached(context);
159
+ },
160
+ async attach(sessionId, context) {
161
+ await remoteManagement.attach(sessionId, context);
162
+ await server.session.whenAttached(sessionId, context);
163
+ },
164
+ async detach(context) {
165
+ await remoteManagement.detach(context);
166
+ await server.session.whenDetached(context);
167
+ },
168
+ };
169
+ const plugins = serverServices.use(PresentationPlugins);
170
+ const models = sessionServices.use(Models);
171
+ const agent = sessionServices.use(AgentController);
172
+ const transcript = sessionServices.use(Transcript);
173
+ await Promise.all([serverServices.ready(BACKGROUND_CONTEXT), sessionServices.ready(BACKGROUND_CONTEXT)]);
174
+ return { ...server, directory, management, plugins, models, agent, transcript };
175
+ }
176
+ function routeFromExplicitPath(path) {
177
+ const name = basename(path);
178
+ const serverId = name.endsWith(".sock") ? name.slice(0, -".sock".length) : "";
179
+ if (!isServerId(serverId))
180
+ throw new Error("--connect path must end with <uuidv4-server-id>.sock");
181
+ return { serverId, path };
182
+ }
183
+ //# sourceMappingURL=client-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-runtime.js","sourceRoot":"","sources":["../../src/experimental/client-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,mBAAmB,EAAwB,MAAM,gCAAgC,CAAC;AACvH,OAAO,EAAE,UAAU,EAAiB,MAAM,6BAA6B,CAAC;AAExE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,kCAAkC,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC7G,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EACN,yBAAyB,EACzB,0BAA0B,GAG1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAgCtD,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,OAAsB,EACtB,OAAO,GAA6B,EAAE,EACb;IACzB,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO,EAAE,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3E,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,EAAE,SAAS,KAAK,QAAQ,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACrF,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,SAAS,GAAG,sBAAsB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5D,IAAI,MAA4B,CAAC;IACjC,IAAI,eAAmC,CAAC;IACxC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,GAAG;YACR,OAAO,CAAC,OAAO,CAAC,SAAS,KAAK,QAAQ;gBACrC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE;gBAC7D,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;SACxE,CAAC;IACH,CAAC;SAAM,CAAC;QACP,MAAM,GAAG,CAAC,MAAM,mBAAmB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;QACtG,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;QAC7F,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC;gBACtC,SAAS;gBACT,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;gBAC7C,UAAU,EAAE,uBAAuB,EAAE;gBACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;aACpB,CAAC,CAAC;YACH,MAAM,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;YACrD,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC;QACpC,CAAC;IACF,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,YAAY,GAA4B,EAAE,CAAC;IACjD,MAAM,cAAc,GAAsD,EAAE,CAAC;IAC7E,MAAM,OAAO,GAA0B,EAAE,CAAC;IAC1C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE,CAAC;QAC1C,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5G,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,UAAU,CAC7C,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAClE,CAAC;QACF,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC1F,MAAM,MAAM,GAAG,CAAC,GAAG,gBAAgB,EAAE,GAAG,aAAa,EAAE,GAAG,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAC3F,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CACnD,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,cAAc,CAAC,MAAM,EAAE,+CAA+C,CAAC,CAAC;IAAA,CACzG,CAAC;IAEF,IAAI,CAAC;QACJ,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC5B,IAAI,MAAM,GAAG,eAAe,CAAC;YAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC;oBACJ,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;wBAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,gBAAgB,EACf,KAAK,CAAC,SAAS,KAAK,MAAM;4BACzB,CAAC,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;4BAClD,CAAC,CAAC,kCAAkC,CAAC;gCACnC,QAAQ,EAAE,KAAK,CAAC,QAAQ;gCACxB,IAAI,EAAE,IAAI,uBAAuB,CAAC,OAAO,CAAC,IAAI,CAAC;6BAC/C,CAAC;qBACL,CAAC,CAAC;gBACJ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,IACC,OAAO,CAAC,OAAO,KAAK,SAAS;wBAC7B,KAAK,CAAC,SAAS,KAAK,MAAM;wBAC1B,CAAC,CAAC,KAAK,YAAY,WAAW,CAAC;wBAC/B,KAAK,CAAC,IAAI,KAAK,SAAS,EACvB,CAAC;wBACF,MAAM,KAAK,CAAC;oBACb,CAAC;oBACD,MAAM,GAAG,CACR,MAAM,cAAc,CAAC;wBACpB,SAAS;wBACT,iBAAiB,EAAE,KAAK,CAAC,QAAQ;wBACjC,UAAU,EAAE,uBAAuB,EAAE;qBACrC,CAAC,CACF,CAAC,MAAM,CAAC;gBACV,CAAC;YACF,CAAC;YACD,eAAe,GAAG,SAAS,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;YACjD,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,KAAK,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAClC,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC;oBACrC,QAAQ,EAAE,CAAC,iBAAiB,CAAC;oBAC7B,YAAY,GAAG,EAAC,CAAC;oBACjB,OAAO,GAAG,EAAC,CAAC;iBACZ,CAAC,CAAC;gBACH,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBACrE,YAAY,CAAC,IAAI,CAChB,IAAI,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC;oBACtD,MAAM,iBAAiB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;oBAClD,MAAM,mBAAmB,CAAC,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;gBAAA,CAChE,CAAC,CACF,CAAC;YACH,CAAC;YACD,MAAM,OAAO,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;YACnD,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC;YACJ,MAAM,OAAO,EAAE,CAAC;QACjB,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,gDAAgD,CAAC,CAAC;QACnG,CAAC;QACD,MAAM,KAAK,CAAC;IACb,CAAC;AAAA,CACD;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAClD,MAA2B,EACa;IACxC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QACzC,QAAQ,EAAE,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,CAAC;QACpE,YAAY,GAAG,EAAC,CAAC;QACjB,OAAO,GAAG,EAAC,CAAC;KACZ,CAAC,CAAC;IACH,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QAC3C,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,CAAC;QAC/C,YAAY,GAAG,EAAC,CAAC;QACjB,OAAO,GAAG,EAAC,CAAC;KACZ,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACvD,MAAM,gBAAgB,GAAG,cAAc,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAsB;QACrC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;QACvE,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE;YAChC,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,KAAK,SAAS,CAAC;YACnF,MAAM,gBAAgB,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAClD,IAAI,wBAAwB;gBAAE,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAAA,CACzE;QACD,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE;YAChC,MAAM,gBAAgB,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAAA,CACtD;QACD,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE;YACrB,MAAM,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACvC,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAAA,CAC3C;KACD,CAAC;IACF,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IACzG,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AAAA,CAChF;AAED,SAAS,qBAAqB,CAAC,IAAY,EAAmB;IAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IACnG,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAAA,CAC1B","sourcesContent":["import { basename } from \"node:path\";\nimport { BACKGROUND_CONTEXT } from \"@earendil-works/chord/context\";\nimport { Client, ServerError } from \"@earendil-works/pi-client\";\nimport { createUnixTransportFactory, discoverUnixServers, type UnixServerRoute } from \"@earendil-works/pi-client/unix\";\nimport { isServerId, type ServerId } from \"@earendil-works/pi-protocol\";\nimport type { ClientCommand } from \"../cli/experimental/commands/client.ts\";\nimport { RadiusRelayAuthResolver } from \"./radius-auth.ts\";\nimport { createRadiusClientTransportFactory, RadiusClientReconnect } from \"./radius-relay.ts\";\nimport { activateServer, ENV_SERVER_ID, resolveServerDirectory, resolveSessionDirectory } from \"./server.ts\";\nimport { AgentController } from \"./services/agent-controller.ts\";\nimport {\n\tcreateServerServiceSource,\n\tcreateSessionServiceSource,\n\ttype ServerServiceSource,\n\ttype SessionServiceSource,\n} from \"./services/connection.ts\";\nimport { Models } from \"./services/models.ts\";\nimport { PresentationPlugins } from \"./services/plugins.ts\";\nimport { SessionDirectory, SessionManagement } from \"./services/sessions.ts\";\nimport { Transcript } from \"./services/transcript.ts\";\n\nexport type ClientRuntimeRoute =\n\t| ({ readonly transport: \"unix\" } & UnixServerRoute)\n\t| { readonly transport: \"radius\"; readonly serverId: ServerId };\n\nexport interface ClientRuntimeServer {\n\treadonly route: ClientRuntimeRoute;\n\treadonly client: Client;\n\treadonly server: ServerServiceSource;\n\treadonly session: SessionServiceSource;\n}\n\nexport interface ActivatedClientRuntimeServer extends ClientRuntimeServer {\n\treadonly directory: SessionDirectory;\n\treadonly management: SessionManagement;\n\treadonly plugins: PresentationPlugins;\n\treadonly models: Models;\n\treadonly agent: AgentController;\n\treadonly transcript: Transcript;\n}\n\nexport interface ClientRuntime {\n\treadonly servers: readonly ClientRuntimeServer[];\n\tdispose(): Promise<void>;\n}\n\nexport interface OpenClientRuntimeOptions {\n\t/** Directory searched when --connect is omitted. Defaults to PI_SERVER_DIR or ~/.pi/server. */\n\treadonly directory?: string;\n}\n\n/** Open live server/session service namespaces for one experimental presentation. */\nexport async function openClientRuntime(\n\tcommand: ClientCommand,\n\toptions: OpenClientRuntimeOptions = {},\n): Promise<ClientRuntime> {\n\tif (command.auth !== undefined && command.connect?.transport !== \"radius\") {\n\t\tthrow new Error(\"Authentication is only supported for experimental Radius connections\");\n\t}\n\tif (command.provider !== undefined && command.model === undefined) {\n\t\tthrow new Error(\"Server model provider requires a model\");\n\t}\n\tif (command.connect && command.model !== undefined) {\n\t\tthrow new Error(\"Model selection is only valid when automatically activating a new server\");\n\t}\n\tif (command.connect?.transport === \"radius\" && command.pluginPackages !== undefined) {\n\t\tthrow new Error(\"Plugin package paths can only be configured on a local Unix server\");\n\t}\n\tconst directory = resolveServerDirectory(options.directory);\n\tlet routes: ClientRuntimeRoute[];\n\tlet activatedClient: Client | undefined;\n\tif (command.connect) {\n\t\troutes = [\n\t\t\tcommand.connect.transport === \"radius\"\n\t\t\t\t? { transport: \"radius\", serverId: command.connect.serverId }\n\t\t\t\t: { transport: \"unix\", ...routeFromExplicitPath(command.connect.path) },\n\t\t];\n\t} else {\n\t\troutes = (await discoverUnixServers({ directory })).map((route) => ({ transport: \"unix\", ...route }));\n\t\tif (routes.length > 0 && command.model !== undefined) {\n\t\t\tthrow new Error(\"Model selection is only valid when automatically activating a new server\");\n\t\t}\n\t\tif (routes.length === 0) {\n\t\t\tconst activated = await activateServer({\n\t\t\t\tdirectory,\n\t\t\t\trequestedServerId: process.env[ENV_SERVER_ID],\n\t\t\t\tsessionDir: resolveSessionDirectory(),\n\t\t\t\tprovider: command.provider,\n\t\t\t\tmodel: command.model,\n\t\t\t});\n\t\t\troutes = [{ transport: \"unix\", ...activated.route }];\n\t\t\tactivatedClient = activated.client;\n\t\t}\n\t}\n\tif (command.pluginPackages !== undefined && routes.length !== 1) {\n\t\tthrow new Error(\"Plugin selection requires exactly one local server\");\n\t}\n\n\tconst clients: Client[] = [];\n\tconst reconnectors: RadiusClientReconnect[] = [];\n\tconst serviceSources: Array<ServerServiceSource | SessionServiceSource> = [];\n\tconst servers: ClientRuntimeServer[] = [];\n\tlet disposed = false;\n\tconst dispose = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tconst reconnectResults = await Promise.allSettled(reconnectors.map((reconnector) => reconnector.dispose()));\n\t\tconst sourceResults = await Promise.allSettled(\n\t\t\tserviceSources.map((source) => source.dispose(BACKGROUND_CONTEXT)),\n\t\t);\n\t\tconst clientResults = await Promise.allSettled(clients.map((client) => client.dispose()));\n\t\tconst errors = [...reconnectResults, ...sourceResults, ...clientResults].flatMap((result) =>\n\t\t\tresult.status === \"rejected\" ? [result.reason] : [],\n\t\t);\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose experimental client runtime\");\n\t};\n\n\ttry {\n\t\tfor (const route of routes) {\n\t\t\tlet client = activatedClient;\n\t\t\tif (client === undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tclient = await Client.connect({\n\t\t\t\t\t\tserverId: route.serverId,\n\t\t\t\t\t\ttransportFactory:\n\t\t\t\t\t\t\troute.transport === \"unix\"\n\t\t\t\t\t\t\t\t? createUnixTransportFactory({ path: route.path })\n\t\t\t\t\t\t\t\t: createRadiusClientTransportFactory({\n\t\t\t\t\t\t\t\t\t\tserverId: route.serverId,\n\t\t\t\t\t\t\t\t\t\tauth: new RadiusRelayAuthResolver(command.auth),\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcommand.connect !== undefined ||\n\t\t\t\t\t\troute.transport !== \"unix\" ||\n\t\t\t\t\t\t!(error instanceof ServerError) ||\n\t\t\t\t\t\terror.code !== \"version\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t\tclient = (\n\t\t\t\t\t\tawait activateServer({\n\t\t\t\t\t\t\tdirectory,\n\t\t\t\t\t\t\trequestedServerId: route.serverId,\n\t\t\t\t\t\t\tsessionDir: resolveSessionDirectory(),\n\t\t\t\t\t\t})\n\t\t\t\t\t).client;\n\t\t\t\t}\n\t\t\t}\n\t\t\tactivatedClient = undefined;\n\t\t\tclients.push(client);\n\t\t\tconst server = createServerServiceSource(client);\n\t\t\tserviceSources.push(server);\n\t\t\tif (route.transport === \"radius\") {\n\t\t\t\tconst reconnectServices = server.open({\n\t\t\t\t\tservices: [SessionManagement],\n\t\t\t\t\tassertAccess() {},\n\t\t\t\t\tonError() {},\n\t\t\t\t});\n\t\t\t\tconst reconnectManagement = reconnectServices.use(SessionManagement);\n\t\t\t\treconnectors.push(\n\t\t\t\t\tnew RadiusClientReconnect(client, async (sessionId) => {\n\t\t\t\t\t\tawait reconnectServices.ready(BACKGROUND_CONTEXT);\n\t\t\t\t\t\tawait reconnectManagement.attach(sessionId, BACKGROUND_CONTEXT);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst session = createSessionServiceSource(client);\n\t\t\tserviceSources.push(session);\n\t\t\tservers.push({ route, client, server, session });\n\t\t}\n\t\treturn { servers, dispose };\n\t} catch (error) {\n\t\ttry {\n\t\t\tawait dispose();\n\t\t} catch (cleanupError) {\n\t\t\tthrow new AggregateError([error, cleanupError], \"Experimental client startup and cleanup failed\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/** Acquire and connect the built-in service facades used by the non-interactive client. */\nexport async function activateBuiltinClientServices(\n\tserver: ClientRuntimeServer,\n): Promise<ActivatedClientRuntimeServer> {\n\tconst serverServices = server.server.open({\n\t\tservices: [SessionDirectory, SessionManagement, PresentationPlugins],\n\t\tassertAccess() {},\n\t\tonError() {},\n\t});\n\tconst sessionServices = server.session.open({\n\t\tservices: [Models, AgentController, Transcript],\n\t\tassertAccess() {},\n\t\tonError() {},\n\t});\n\tconst directory = serverServices.use(SessionDirectory);\n\tconst remoteManagement = serverServices.use(SessionManagement);\n\tconst management: SessionManagement = {\n\t\tcreate: (options, context) => remoteManagement.create(options, context),\n\t\tasync remove(sessionId, context) {\n\t\t\tconst removesCurrentAttachment = server.client.attachment?.sessionId === sessionId;\n\t\t\tawait remoteManagement.remove(sessionId, context);\n\t\t\tif (removesCurrentAttachment) await server.session.whenDetached(context);\n\t\t},\n\t\tasync attach(sessionId, context) {\n\t\t\tawait remoteManagement.attach(sessionId, context);\n\t\t\tawait server.session.whenAttached(sessionId, context);\n\t\t},\n\t\tasync detach(context) {\n\t\t\tawait remoteManagement.detach(context);\n\t\t\tawait server.session.whenDetached(context);\n\t\t},\n\t};\n\tconst plugins = serverServices.use(PresentationPlugins);\n\tconst models = sessionServices.use(Models);\n\tconst agent = sessionServices.use(AgentController);\n\tconst transcript = sessionServices.use(Transcript);\n\tawait Promise.all([serverServices.ready(BACKGROUND_CONTEXT), sessionServices.ready(BACKGROUND_CONTEXT)]);\n\treturn { ...server, directory, management, plugins, models, agent, transcript };\n}\n\nfunction routeFromExplicitPath(path: string): UnixServerRoute {\n\tconst name = basename(path);\n\tconst serverId = name.endsWith(\".sock\") ? name.slice(0, -\".sock\".length) : \"\";\n\tif (!isServerId(serverId)) throw new Error(\"--connect path must end with <uuidv4-server-id>.sock\");\n\treturn { serverId, path };\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { LaneSnapshot } from "@earendil-works/pi-agent-core";
2
+ import { Container, type TUI } from "@earendil-works/pi-tui";
3
+ /** Snapshot-driven transcript used by the service-only experimental presentation. */
4
+ export declare class ExperimentalChatView {
5
+ #private;
6
+ readonly transcript: Container;
7
+ readonly pendingMessages: Container;
8
+ readonly status: Container;
9
+ constructor(ui: TUI, cwd: string);
10
+ apply(snapshot: LaneSnapshot): void;
11
+ refreshTheme(snapshot: LaneSnapshot): void;
12
+ dispose(): void;
13
+ }
14
+ //# sourceMappingURL=client-tui-chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-tui-chat.d.ts","sourceRoot":"","sources":["../../src/experimental/client-tui-chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAEvF,OAAO,EAAE,SAAS,EAA+B,KAAK,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAiB1F,qFAAqF;AACrF,qBAAa,oBAAoB;;IAGhC,QAAQ,CAAC,UAAU,YAAmB;IACtC,QAAQ,CAAC,eAAe,YAAmB;IAC3C,QAAQ,CAAC,MAAM,YAAmB;IASlC,YAAY,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAG/B;IAED,KAAK,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAiBlC;IAED,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAWzC;IAED,OAAO,IAAI,IAAI,CAEd;CA8GD","sourcesContent":["import type { AgentMessage, Entry, LaneSnapshot } from \"@earendil-works/pi-agent-core\";\nimport type { AssistantMessage } from \"@earendil-works/pi-ai\";\nimport { Container, Spacer, Text, TruncatedText, type TUI } from \"@earendil-works/pi-tui\";\nimport { createAllToolRenderers } from \"../core/tools/renderers/index.ts\";\nimport { AssistantMessageComponent } from \"../modes/interactive/components/assistant-message.ts\";\nimport { type StatusIndicator, WorkingStatusIndicator } from \"../modes/interactive/components/status-indicator.ts\";\nimport { ToolExecutionComponent, type ToolRenderers } from \"../modes/interactive/components/tool-execution.ts\";\nimport { UserMessageComponent } from \"../modes/interactive/components/user-message.ts\";\nimport { theme } from \"../modes/interactive/theme/theme.ts\";\n\nfunction userMessageText(message: AgentMessage): string {\n\tif (message.role !== \"user\") return \"\";\n\tif (typeof message.content === \"string\") return message.content;\n\treturn message.content\n\t\t.filter((content) => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\");\n}\n\n/** Snapshot-driven transcript used by the service-only experimental presentation. */\nexport class ExperimentalChatView {\n\tstatic readonly #renderers: Record<string, ToolRenderers> = createAllToolRenderers();\n\n\treadonly transcript = new Container();\n\treadonly pendingMessages = new Container();\n\treadonly status = new Container();\n\treadonly #ui: TUI;\n\treadonly #cwd: string;\n\treadonly #tools = new Map<string, ToolExecutionComponent>();\n\t#renderedEntryIds: string[] = [];\n\t#streaming: AssistantMessageComponent | undefined;\n\t#indicator: StatusIndicator | undefined;\n\t#working = false;\n\n\tconstructor(ui: TUI, cwd: string) {\n\t\tthis.#ui = ui;\n\t\tthis.#cwd = cwd;\n\t}\n\n\tapply(snapshot: LaneSnapshot): void {\n\t\tthis.#syncTranscript(snapshot.transcript);\n\t\tthis.#syncStreaming(snapshot.operation?.streamingMessage);\n\t\tfor (const tool of snapshot.operation?.runningTools ?? []) {\n\t\t\tconst component = this.#tool(tool.toolName, tool.toolCallId, tool.args);\n\t\t\tif (tool.status === \"running\") {\n\t\t\t\tcomponent.markExecutionStarted();\n\t\t\t\tif (tool.result !== undefined) component.updateResult({ ...tool.result, isError: false }, true);\n\t\t\t} else {\n\t\t\t\tcomponent.updateResult({ ...tool.result, isError: tool.isError }, false);\n\t\t\t}\n\t\t}\n\t\tthis.#syncQueues(snapshot.queues);\n\t\tthis.#setWorking(snapshot.operation !== null);\n\t\tthis.transcript.invalidate();\n\t\tthis.pendingMessages.invalidate();\n\t\tthis.status.invalidate();\n\t}\n\n\trefreshTheme(snapshot: LaneSnapshot): void {\n\t\tthis.#indicator?.dispose();\n\t\tthis.#indicator = undefined;\n\t\tthis.#working = false;\n\t\tthis.transcript.clear();\n\t\tthis.pendingMessages.clear();\n\t\tthis.status.clear();\n\t\tthis.#tools.clear();\n\t\tthis.#renderedEntryIds = [];\n\t\tthis.#streaming = undefined;\n\t\tthis.apply(snapshot);\n\t}\n\n\tdispose(): void {\n\t\tthis.#indicator?.dispose();\n\t}\n\n\t#syncQueues(queues: LaneSnapshot[\"queues\"]): void {\n\t\tthis.pendingMessages.clear();\n\t\tfor (const item of queues) {\n\t\t\tconst text =\n\t\t\t\titem.type === \"message\" ? userMessageText(item.message).replace(/\\s+/g, \" \") : `<${item.customType}>`;\n\t\t\tthis.pendingMessages.addChild(new TruncatedText(theme.fg(\"muted\", `[${item.kind}] ${text}`), 1, 0));\n\t\t}\n\t}\n\n\t#syncTranscript(transcript: readonly Entry[]): void {\n\t\tconst diverged = this.#renderedEntryIds.some((id, index) => transcript[index]?.id !== id);\n\t\tif (diverged) {\n\t\t\tthis.transcript.clear();\n\t\t\tthis.#tools.clear();\n\t\t\tthis.#renderedEntryIds = [];\n\t\t\tthis.#streaming = undefined;\n\t\t}\n\t\tfor (const entry of transcript.slice(this.#renderedEntryIds.length)) {\n\t\t\tthis.#addEntry(entry);\n\t\t\tthis.#renderedEntryIds.push(entry.id);\n\t\t}\n\t}\n\n\t#addEntry(entry: Entry): void {\n\t\tif (entry.type === \"compaction\") {\n\t\t\tthis.#addText(theme.fg(\"muted\", `[compaction] compacted from ${entry.tokensBefore} tokens`));\n\t\t\tfor (const retained of entry.retainedTail) this.#addMessage(retained);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.type === \"branch_summary\") {\n\t\t\tthis.#addText(theme.fg(\"muted\", \"[branch summary]\"));\n\t\t\tthis.#addText(entry.summary);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.type === \"custom\") {\n\t\t\tthis.#addText(theme.fg(\"muted\", `[${entry.customType}]`));\n\t\t\treturn;\n\t\t}\n\t\tthis.#addMessage(entry.message);\n\t}\n\n\t#addMessage(message: AgentMessage): void {\n\t\tif (message.role === \"user\") {\n\t\t\tthis.transcript.addChild(new Spacer(1));\n\t\t\tthis.transcript.addChild(new UserMessageComponent(userMessageText(message)));\n\t\t\treturn;\n\t\t}\n\t\tif (message.role === \"assistant\") {\n\t\t\tconst component = this.#streaming ?? new AssistantMessageComponent();\n\t\t\tif (!this.#streaming) this.transcript.addChild(component);\n\t\t\tthis.#streaming = undefined;\n\t\t\tcomponent.updateContent(message, false);\n\t\t\tfor (const content of message.content) {\n\t\t\t\tif (content.type === \"toolCall\") this.#tool(content.name, content.id, content.arguments).setArgsComplete();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (message.role === \"toolResult\") this.#tool(message.toolName, message.toolCallId).updateResult(message);\n\t}\n\n\t#syncStreaming(message: AssistantMessage | undefined): void {\n\t\tif (!message) return;\n\t\tif (!this.#streaming) {\n\t\t\tthis.#streaming = new AssistantMessageComponent();\n\t\t\tthis.transcript.addChild(this.#streaming);\n\t\t}\n\t\tthis.#streaming.updateContent(message, true);\n\t\tfor (const content of message.content) {\n\t\t\tif (content.type === \"toolCall\") this.#tool(content.name, content.id, content.arguments);\n\t\t}\n\t}\n\n\t#tool(toolName: string, toolCallId: string, args?: unknown): ToolExecutionComponent {\n\t\tconst existing = this.#tools.get(toolCallId);\n\t\tif (existing) {\n\t\t\tif (args !== undefined) existing.updateArgs(args);\n\t\t\treturn existing;\n\t\t}\n\t\tconst component = new ToolExecutionComponent(\n\t\t\ttoolName,\n\t\t\ttoolCallId,\n\t\t\targs ?? {},\n\t\t\t{},\n\t\t\tExperimentalChatView.#renderers[toolName],\n\t\t\tthis.#ui,\n\t\t\tthis.#cwd,\n\t\t);\n\t\tthis.transcript.addChild(component);\n\t\tthis.#tools.set(toolCallId, component);\n\t\treturn component;\n\t}\n\n\t#addText(text: string): void {\n\t\tthis.transcript.addChild(new Spacer(1));\n\t\tthis.transcript.addChild(new Text(text, 1, 0));\n\t}\n\n\t#setWorking(working: boolean): void {\n\t\tif (working === this.#working) return;\n\t\tthis.#working = working;\n\t\tthis.#indicator?.dispose();\n\t\tthis.#indicator = undefined;\n\t\tthis.status.clear();\n\t\tif (working) {\n\t\t\tthis.#indicator = new WorkingStatusIndicator(this.#ui, \"Working... (esc to abort)\");\n\t\t\tthis.status.addChild(this.#indicator);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,171 @@
1
+ import { Container, Spacer, Text, TruncatedText } from "@earendil-works/pi-tui";
2
+ import { createAllToolRenderers } from "../core/tools/renderers/index.js";
3
+ import { AssistantMessageComponent } from "../modes/interactive/components/assistant-message.js";
4
+ import { WorkingStatusIndicator } from "../modes/interactive/components/status-indicator.js";
5
+ import { ToolExecutionComponent } from "../modes/interactive/components/tool-execution.js";
6
+ import { UserMessageComponent } from "../modes/interactive/components/user-message.js";
7
+ import { theme } from "../modes/interactive/theme/theme.js";
8
+ function userMessageText(message) {
9
+ if (message.role !== "user")
10
+ return "";
11
+ if (typeof message.content === "string")
12
+ return message.content;
13
+ return message.content
14
+ .filter((content) => content.type === "text")
15
+ .map((content) => content.text)
16
+ .join("");
17
+ }
18
+ /** Snapshot-driven transcript used by the service-only experimental presentation. */
19
+ export class ExperimentalChatView {
20
+ static #renderers = createAllToolRenderers();
21
+ transcript = new Container();
22
+ pendingMessages = new Container();
23
+ status = new Container();
24
+ #ui;
25
+ #cwd;
26
+ #tools = new Map();
27
+ #renderedEntryIds = [];
28
+ #streaming;
29
+ #indicator;
30
+ #working = false;
31
+ constructor(ui, cwd) {
32
+ this.#ui = ui;
33
+ this.#cwd = cwd;
34
+ }
35
+ apply(snapshot) {
36
+ this.#syncTranscript(snapshot.transcript);
37
+ this.#syncStreaming(snapshot.operation?.streamingMessage);
38
+ for (const tool of snapshot.operation?.runningTools ?? []) {
39
+ const component = this.#tool(tool.toolName, tool.toolCallId, tool.args);
40
+ if (tool.status === "running") {
41
+ component.markExecutionStarted();
42
+ if (tool.result !== undefined)
43
+ component.updateResult({ ...tool.result, isError: false }, true);
44
+ }
45
+ else {
46
+ component.updateResult({ ...tool.result, isError: tool.isError }, false);
47
+ }
48
+ }
49
+ this.#syncQueues(snapshot.queues);
50
+ this.#setWorking(snapshot.operation !== null);
51
+ this.transcript.invalidate();
52
+ this.pendingMessages.invalidate();
53
+ this.status.invalidate();
54
+ }
55
+ refreshTheme(snapshot) {
56
+ this.#indicator?.dispose();
57
+ this.#indicator = undefined;
58
+ this.#working = false;
59
+ this.transcript.clear();
60
+ this.pendingMessages.clear();
61
+ this.status.clear();
62
+ this.#tools.clear();
63
+ this.#renderedEntryIds = [];
64
+ this.#streaming = undefined;
65
+ this.apply(snapshot);
66
+ }
67
+ dispose() {
68
+ this.#indicator?.dispose();
69
+ }
70
+ #syncQueues(queues) {
71
+ this.pendingMessages.clear();
72
+ for (const item of queues) {
73
+ const text = item.type === "message" ? userMessageText(item.message).replace(/\s+/g, " ") : `<${item.customType}>`;
74
+ this.pendingMessages.addChild(new TruncatedText(theme.fg("muted", `[${item.kind}] ${text}`), 1, 0));
75
+ }
76
+ }
77
+ #syncTranscript(transcript) {
78
+ const diverged = this.#renderedEntryIds.some((id, index) => transcript[index]?.id !== id);
79
+ if (diverged) {
80
+ this.transcript.clear();
81
+ this.#tools.clear();
82
+ this.#renderedEntryIds = [];
83
+ this.#streaming = undefined;
84
+ }
85
+ for (const entry of transcript.slice(this.#renderedEntryIds.length)) {
86
+ this.#addEntry(entry);
87
+ this.#renderedEntryIds.push(entry.id);
88
+ }
89
+ }
90
+ #addEntry(entry) {
91
+ if (entry.type === "compaction") {
92
+ this.#addText(theme.fg("muted", `[compaction] compacted from ${entry.tokensBefore} tokens`));
93
+ for (const retained of entry.retainedTail)
94
+ this.#addMessage(retained);
95
+ return;
96
+ }
97
+ if (entry.type === "branch_summary") {
98
+ this.#addText(theme.fg("muted", "[branch summary]"));
99
+ this.#addText(entry.summary);
100
+ return;
101
+ }
102
+ if (entry.type === "custom") {
103
+ this.#addText(theme.fg("muted", `[${entry.customType}]`));
104
+ return;
105
+ }
106
+ this.#addMessage(entry.message);
107
+ }
108
+ #addMessage(message) {
109
+ if (message.role === "user") {
110
+ this.transcript.addChild(new Spacer(1));
111
+ this.transcript.addChild(new UserMessageComponent(userMessageText(message)));
112
+ return;
113
+ }
114
+ if (message.role === "assistant") {
115
+ const component = this.#streaming ?? new AssistantMessageComponent();
116
+ if (!this.#streaming)
117
+ this.transcript.addChild(component);
118
+ this.#streaming = undefined;
119
+ component.updateContent(message, false);
120
+ for (const content of message.content) {
121
+ if (content.type === "toolCall")
122
+ this.#tool(content.name, content.id, content.arguments).setArgsComplete();
123
+ }
124
+ return;
125
+ }
126
+ if (message.role === "toolResult")
127
+ this.#tool(message.toolName, message.toolCallId).updateResult(message);
128
+ }
129
+ #syncStreaming(message) {
130
+ if (!message)
131
+ return;
132
+ if (!this.#streaming) {
133
+ this.#streaming = new AssistantMessageComponent();
134
+ this.transcript.addChild(this.#streaming);
135
+ }
136
+ this.#streaming.updateContent(message, true);
137
+ for (const content of message.content) {
138
+ if (content.type === "toolCall")
139
+ this.#tool(content.name, content.id, content.arguments);
140
+ }
141
+ }
142
+ #tool(toolName, toolCallId, args) {
143
+ const existing = this.#tools.get(toolCallId);
144
+ if (existing) {
145
+ if (args !== undefined)
146
+ existing.updateArgs(args);
147
+ return existing;
148
+ }
149
+ const component = new ToolExecutionComponent(toolName, toolCallId, args ?? {}, {}, ExperimentalChatView.#renderers[toolName], this.#ui, this.#cwd);
150
+ this.transcript.addChild(component);
151
+ this.#tools.set(toolCallId, component);
152
+ return component;
153
+ }
154
+ #addText(text) {
155
+ this.transcript.addChild(new Spacer(1));
156
+ this.transcript.addChild(new Text(text, 1, 0));
157
+ }
158
+ #setWorking(working) {
159
+ if (working === this.#working)
160
+ return;
161
+ this.#working = working;
162
+ this.#indicator?.dispose();
163
+ this.#indicator = undefined;
164
+ this.status.clear();
165
+ if (working) {
166
+ this.#indicator = new WorkingStatusIndicator(this.#ui, "Working... (esc to abort)");
167
+ this.status.addChild(this.#indicator);
168
+ }
169
+ }
170
+ }
171
+ //# sourceMappingURL=client-tui-chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-tui-chat.js","sourceRoot":"","sources":["../../src/experimental/client-tui-chat.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage, Entry, LaneSnapshot } from \"@earendil-works/pi-agent-core\";\nimport type { AssistantMessage } from \"@earendil-works/pi-ai\";\nimport { Container, Spacer, Text, TruncatedText, type TUI } from \"@earendil-works/pi-tui\";\nimport { createAllToolRenderers } from \"../core/tools/renderers/index.ts\";\nimport { AssistantMessageComponent } from \"../modes/interactive/components/assistant-message.ts\";\nimport { type StatusIndicator, WorkingStatusIndicator } from \"../modes/interactive/components/status-indicator.ts\";\nimport { ToolExecutionComponent, type ToolRenderers } from \"../modes/interactive/components/tool-execution.ts\";\nimport { UserMessageComponent } from \"../modes/interactive/components/user-message.ts\";\nimport { theme } from \"../modes/interactive/theme/theme.ts\";\n\nfunction userMessageText(message: AgentMessage): string {\n\tif (message.role !== \"user\") return \"\";\n\tif (typeof message.content === \"string\") return message.content;\n\treturn message.content\n\t\t.filter((content) => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\");\n}\n\n/** Snapshot-driven transcript used by the service-only experimental presentation. */\nexport class ExperimentalChatView {\n\tstatic readonly #renderers: Record<string, ToolRenderers> = createAllToolRenderers();\n\n\treadonly transcript = new Container();\n\treadonly pendingMessages = new Container();\n\treadonly status = new Container();\n\treadonly #ui: TUI;\n\treadonly #cwd: string;\n\treadonly #tools = new Map<string, ToolExecutionComponent>();\n\t#renderedEntryIds: string[] = [];\n\t#streaming: AssistantMessageComponent | undefined;\n\t#indicator: StatusIndicator | undefined;\n\t#working = false;\n\n\tconstructor(ui: TUI, cwd: string) {\n\t\tthis.#ui = ui;\n\t\tthis.#cwd = cwd;\n\t}\n\n\tapply(snapshot: LaneSnapshot): void {\n\t\tthis.#syncTranscript(snapshot.transcript);\n\t\tthis.#syncStreaming(snapshot.operation?.streamingMessage);\n\t\tfor (const tool of snapshot.operation?.runningTools ?? []) {\n\t\t\tconst component = this.#tool(tool.toolName, tool.toolCallId, tool.args);\n\t\t\tif (tool.status === \"running\") {\n\t\t\t\tcomponent.markExecutionStarted();\n\t\t\t\tif (tool.result !== undefined) component.updateResult({ ...tool.result, isError: false }, true);\n\t\t\t} else {\n\t\t\t\tcomponent.updateResult({ ...tool.result, isError: tool.isError }, false);\n\t\t\t}\n\t\t}\n\t\tthis.#syncQueues(snapshot.queues);\n\t\tthis.#setWorking(snapshot.operation !== null);\n\t\tthis.transcript.invalidate();\n\t\tthis.pendingMessages.invalidate();\n\t\tthis.status.invalidate();\n\t}\n\n\trefreshTheme(snapshot: LaneSnapshot): void {\n\t\tthis.#indicator?.dispose();\n\t\tthis.#indicator = undefined;\n\t\tthis.#working = false;\n\t\tthis.transcript.clear();\n\t\tthis.pendingMessages.clear();\n\t\tthis.status.clear();\n\t\tthis.#tools.clear();\n\t\tthis.#renderedEntryIds = [];\n\t\tthis.#streaming = undefined;\n\t\tthis.apply(snapshot);\n\t}\n\n\tdispose(): void {\n\t\tthis.#indicator?.dispose();\n\t}\n\n\t#syncQueues(queues: LaneSnapshot[\"queues\"]): void {\n\t\tthis.pendingMessages.clear();\n\t\tfor (const item of queues) {\n\t\t\tconst text =\n\t\t\t\titem.type === \"message\" ? userMessageText(item.message).replace(/\\s+/g, \" \") : `<${item.customType}>`;\n\t\t\tthis.pendingMessages.addChild(new TruncatedText(theme.fg(\"muted\", `[${item.kind}] ${text}`), 1, 0));\n\t\t}\n\t}\n\n\t#syncTranscript(transcript: readonly Entry[]): void {\n\t\tconst diverged = this.#renderedEntryIds.some((id, index) => transcript[index]?.id !== id);\n\t\tif (diverged) {\n\t\t\tthis.transcript.clear();\n\t\t\tthis.#tools.clear();\n\t\t\tthis.#renderedEntryIds = [];\n\t\t\tthis.#streaming = undefined;\n\t\t}\n\t\tfor (const entry of transcript.slice(this.#renderedEntryIds.length)) {\n\t\t\tthis.#addEntry(entry);\n\t\t\tthis.#renderedEntryIds.push(entry.id);\n\t\t}\n\t}\n\n\t#addEntry(entry: Entry): void {\n\t\tif (entry.type === \"compaction\") {\n\t\t\tthis.#addText(theme.fg(\"muted\", `[compaction] compacted from ${entry.tokensBefore} tokens`));\n\t\t\tfor (const retained of entry.retainedTail) this.#addMessage(retained);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.type === \"branch_summary\") {\n\t\t\tthis.#addText(theme.fg(\"muted\", \"[branch summary]\"));\n\t\t\tthis.#addText(entry.summary);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.type === \"custom\") {\n\t\t\tthis.#addText(theme.fg(\"muted\", `[${entry.customType}]`));\n\t\t\treturn;\n\t\t}\n\t\tthis.#addMessage(entry.message);\n\t}\n\n\t#addMessage(message: AgentMessage): void {\n\t\tif (message.role === \"user\") {\n\t\t\tthis.transcript.addChild(new Spacer(1));\n\t\t\tthis.transcript.addChild(new UserMessageComponent(userMessageText(message)));\n\t\t\treturn;\n\t\t}\n\t\tif (message.role === \"assistant\") {\n\t\t\tconst component = this.#streaming ?? new AssistantMessageComponent();\n\t\t\tif (!this.#streaming) this.transcript.addChild(component);\n\t\t\tthis.#streaming = undefined;\n\t\t\tcomponent.updateContent(message, false);\n\t\t\tfor (const content of message.content) {\n\t\t\t\tif (content.type === \"toolCall\") this.#tool(content.name, content.id, content.arguments).setArgsComplete();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (message.role === \"toolResult\") this.#tool(message.toolName, message.toolCallId).updateResult(message);\n\t}\n\n\t#syncStreaming(message: AssistantMessage | undefined): void {\n\t\tif (!message) return;\n\t\tif (!this.#streaming) {\n\t\t\tthis.#streaming = new AssistantMessageComponent();\n\t\t\tthis.transcript.addChild(this.#streaming);\n\t\t}\n\t\tthis.#streaming.updateContent(message, true);\n\t\tfor (const content of message.content) {\n\t\t\tif (content.type === \"toolCall\") this.#tool(content.name, content.id, content.arguments);\n\t\t}\n\t}\n\n\t#tool(toolName: string, toolCallId: string, args?: unknown): ToolExecutionComponent {\n\t\tconst existing = this.#tools.get(toolCallId);\n\t\tif (existing) {\n\t\t\tif (args !== undefined) existing.updateArgs(args);\n\t\t\treturn existing;\n\t\t}\n\t\tconst component = new ToolExecutionComponent(\n\t\t\ttoolName,\n\t\t\ttoolCallId,\n\t\t\targs ?? {},\n\t\t\t{},\n\t\t\tExperimentalChatView.#renderers[toolName],\n\t\t\tthis.#ui,\n\t\t\tthis.#cwd,\n\t\t);\n\t\tthis.transcript.addChild(component);\n\t\tthis.#tools.set(toolCallId, component);\n\t\treturn component;\n\t}\n\n\t#addText(text: string): void {\n\t\tthis.transcript.addChild(new Spacer(1));\n\t\tthis.transcript.addChild(new Text(text, 1, 0));\n\t}\n\n\t#setWorking(working: boolean): void {\n\t\tif (working === this.#working) return;\n\t\tthis.#working = working;\n\t\tthis.#indicator?.dispose();\n\t\tthis.#indicator = undefined;\n\t\tthis.status.clear();\n\t\tif (working) {\n\t\t\tthis.#indicator = new WorkingStatusIndicator(this.#ui, \"Working... (esc to abort)\");\n\t\t\tthis.status.addChild(this.#indicator);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import { type FacetLoader } from "@earendil-works/chord";
2
+ import { type Component, type TUI } from "@earendil-works/pi-tui";
3
+ import type { ClientCommand } from "../cli/experimental/commands/client.ts";
4
+ import { type OpenClientRuntimeOptions } from "./client-runtime.ts";
5
+ import type { ServerServiceSource, SessionServiceSource } from "./services/connection.ts";
6
+ export interface RunClientTuiOptions extends OpenClientRuntimeOptions {
7
+ readonly facetLoader?: FacetLoader;
8
+ }
9
+ export interface ClientTuiServer {
10
+ readonly serverId: string;
11
+ readonly radius: boolean;
12
+ readonly server: ServerServiceSource;
13
+ readonly session: SessionServiceSource;
14
+ }
15
+ /** Service-only presentation driven by a replicated main-lane snapshot. */
16
+ export declare class ExperimentalClientTui implements Component {
17
+ #private;
18
+ private constructor();
19
+ static create(options: {
20
+ readonly command: ClientCommand;
21
+ readonly ui: TUI;
22
+ readonly servers: readonly ClientTuiServer[];
23
+ readonly facetLoader?: FacetLoader;
24
+ requestRender(): void;
25
+ finish(): void;
26
+ }): Promise<ExperimentalClientTui>;
27
+ get layoutRoot(): Component;
28
+ render(width: number): string[];
29
+ handleInput(data: string): void;
30
+ invalidate(): void;
31
+ dispose(): void;
32
+ refreshTheme(): void;
33
+ showError(error: string): void;
34
+ close(): Promise<void>;
35
+ }
36
+ export declare function runClientTui(command: ClientCommand, options?: RunClientTuiOptions): Promise<void>;
37
+ //# sourceMappingURL=client-tui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-tui.d.ts","sourceRoot":"","sources":["../../src/experimental/client-tui.ts"],"names":[],"mappings":"AACA,OAAO,EAKN,KAAK,WAAW,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAEN,KAAK,SAAS,EAMd,KAAK,GAAG,EACR,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAC;AAU5E,OAAO,EAAE,KAAK,wBAAwB,EAAqB,MAAM,qBAAqB,CAAC;AAIvF,OAAO,KAAK,EAEX,mBAAmB,EAEnB,oBAAoB,EACpB,MAAM,0BAA0B,CAAC;AAWlC,MAAM,WAAW,mBAAoB,SAAQ,wBAAwB;IACpE,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;CACvC;AA6BD,2EAA2E;AAC3E,qBAAa,qBAAsB,YAAW,SAAS;;IAiCtD,OAAO,eA6BN;IAED,OAAa,MAAM,CAAC,OAAO,EAAE;QAC5B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAChC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;QACjB,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;QAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;QACnC,aAAa,IAAI,IAAI,CAAC;QACtB,MAAM,IAAI,IAAI,CAAC;KACf,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAkBjC;IAED,IAAI,UAAU,IAAI,SAAS,CAE1B;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ9B;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAgB9B;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,IAAI,IAAI,CAEd;IAED,YAAY,IAAI,IAAI,CAInB;IAED,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG7B;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrB;CAuYD;AAoGD,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoE3G","sourcesContent":["import { resolve } from \"node:path\";\nimport {\n\tcombineFacetLoaders,\n\tcreateFacetHost,\n\tdefineFacet,\n\ttype FacetHost,\n\ttype FacetLoader,\n\ttype JsonValue,\n\ttype LoadedFacets,\n} from \"@earendil-works/chord\";\nimport { BACKGROUND_CONTEXT } from \"@earendil-works/chord/context\";\nimport {\n\tCombinedAutocompleteProvider,\n\ttype Component,\n\tContainer,\n\ttype SelectItem,\n\tSelectList,\n\tsetKeybindings,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ClientCommand } from \"../cli/experimental/commands/client.ts\";\nimport { getAgentDir } from \"../config.ts\";\nimport { KeybindingsManager } from \"../core/keybindings.ts\";\nimport { DefaultResourceLoader } from \"../core/resource-loader.ts\";\nimport { SettingsManager } from \"../core/settings-manager.ts\";\nimport { createChatViewport } from \"../modes/interactive/chat-viewport.ts\";\nimport { CustomEditor } from \"../modes/interactive/components/custom-editor.ts\";\nimport { getEditorTheme, setRegisteredThemes, stopThemeWatcher, theme } from \"../modes/interactive/theme/theme.ts\";\nimport { InteractiveThemeController } from \"../modes/interactive/theme/theme-controller.ts\";\nimport { createInteractiveTui } from \"../modes/interactive/tui-renderer.ts\";\nimport { type OpenClientRuntimeOptions, openClientRuntime } from \"./client-runtime.ts\";\nimport { ExperimentalChatView } from \"./client-tui-chat.ts\";\nimport { createPresentationFacetLoaders } from \"./plugins/bundled.ts\";\nimport { AgentController, type AgentOperationResponse, type AgentQueueResponse } from \"./services/agent-controller.ts\";\nimport type {\n\tServerConnectionState,\n\tServerServiceSource,\n\tSessionAttachmentState,\n\tSessionServiceSource,\n} from \"./services/connection.ts\";\nimport { PresentationPlugins } from \"./services/plugins.ts\";\nimport { PresentationUI } from \"./services/presentation-ui.ts\";\nimport { SessionDirectory, SessionManagement, type SessionSummary } from \"./services/sessions.ts\";\nimport { SlashCommands } from \"./services/slash-commands.ts\";\nimport {\n\tcreateBuiltInSlashCommandsFacet,\n\tcreateSlashCommandsRuntimeFacet,\n} from \"./services/slash-commands-provider.ts\";\nimport { Transcript, type Transcript as TranscriptService } from \"./services/transcript.ts\";\n\nexport interface RunClientTuiOptions extends OpenClientRuntimeOptions {\n\treadonly facetLoader?: FacetLoader;\n}\n\nexport interface ClientTuiServer {\n\treadonly serverId: string;\n\treadonly radius: boolean;\n\treadonly server: ServerServiceSource;\n\treadonly session: SessionServiceSource;\n}\n\ninterface SessionFeature {\n\treadonly serverId: string;\n\treadonly session: SessionServiceSource;\n\treadonly transcript: TranscriptService;\n}\n\ninterface PreparedClientSession {\n\treadonly server: ClientTuiServer;\n\treadonly summary: SessionSummary;\n\treadonly presentationPlugins: JsonValue;\n}\n\ninterface PendingSelection {\n\treadonly title: string;\n\treadonly items: readonly SelectItem[];\n\treadonly selectedValue?: string;\n\tresolve(value: string | undefined): void;\n}\n\nconst selectTheme = {\n\tselectedPrefix: (text: string) => theme.fg(\"accent\", text),\n\tselectedText: (text: string) => theme.fg(\"accent\", text),\n\tdescription: (text: string) => theme.fg(\"muted\", text),\n\tscrollInfo: (text: string) => theme.fg(\"dim\", text),\n\tnoMatch: (text: string) => theme.fg(\"warning\", text),\n};\n\n/** Service-only presentation driven by a replicated main-lane snapshot. */\nexport class ExperimentalClientTui implements Component {\n\treadonly #ui: TUI;\n\treadonly #requestRender: () => void;\n\treadonly #finish: () => void;\n\treadonly #documentContainer = new Container();\n\treadonly #sessionHeading = new Text(\"\", 1, 0);\n\treadonly #pendingMessagesContainer = new Container();\n\treadonly #statusContainer = new Container();\n\treadonly #editorContainer = new Container();\n\treadonly #footerComponent = new Text(\"\", 1, 0);\n\treadonly #layoutRoot: Component;\n\treadonly #sharedFacets: LoadedFacets;\n\treadonly #keybindings = KeybindingsManager.create();\n\t#presentationFacets: LoadedFacets | undefined;\n\t#facetHost: FacetHost | undefined;\n\t#facetReloadTail = Promise.resolve();\n\t#session: SessionFeature | undefined;\n\t#slashCommands: SlashCommands | undefined;\n\t#controller: AgentController | undefined;\n\treadonly #chatInput: CustomEditor;\n\t#selectList: SelectList | undefined;\n\t#selection: PendingSelection | undefined;\n\t#screen: \"select\" | \"chat\" = \"chat\";\n\t#selectedServerId: string | undefined;\n\t#sessionId: string | undefined;\n\t#status = \"Starting Session…\";\n\t#busy = false;\n\t#closed = false;\n\t#closePromise: Promise<void> | undefined;\n\t#recoveryTransition: Promise<void> = Promise.resolve();\n\t#laneUnsubscribe: (() => void) | undefined;\n\t#chatView: ExperimentalChatView | undefined;\n\n\tprivate constructor(ui: TUI, requestRender: () => void, finish: () => void, loadedFacets: LoadedFacets) {\n\t\tthis.#ui = ui;\n\t\tthis.#requestRender = requestRender;\n\t\tthis.#finish = finish;\n\t\tthis.#sharedFacets = loadedFacets;\n\t\tsetKeybindings(this.#keybindings);\n\t\tthis.#chatInput = new CustomEditor(ui, getEditorTheme(), this.#keybindings, { paddingX: 1 });\n\t\tthis.#chatInput.onSubmit = (message) => void this.#runPrompt(message);\n\t\tthis.#chatInput.onEscape = () => this.#interrupt();\n\t\tthis.#chatInput.onCtrlD = finish;\n\t\tthis.#chatInput.onAction(\"app.clear\", finish);\n\t\tthis.#chatInput.onAction(\"app.model.select\", () => void this.#executeSlashCommand(\"model\", \"\"));\n\t\tthis.#chatInput.onAction(\"app.message.followUp\", () => {\n\t\t\tconst text = this.#chatInput.getText().trim();\n\t\t\tif (text.length === 0) return;\n\t\t\tthis.#chatInput.setText(\"\");\n\t\t\tvoid this.#queueFollowUp(text);\n\t\t});\n\t\tthis.#editorContainer.addChild(this.#chatInput);\n\t\tthis.#layoutRoot = createChatViewport({\n\t\t\tdocument: this.#documentContainer,\n\t\t\tpendingMessages: this.#pendingMessagesContainer,\n\t\t\tstatus: this.#statusContainer,\n\t\t\teditor: this.#editorContainer,\n\t\t\tfooter: this.#footerComponent,\n\t\t\tscrollbarTrackStyle: (text) => theme.fg(\"scrollbarTrack\", text),\n\t\t\tscrollbarThumbStyle: (text) => theme.fg(\"scrollbarThumb\", text),\n\t\t}).root;\n\t\tthis.#rebuild();\n\t}\n\n\tstatic async create(options: {\n\t\treadonly command: ClientCommand;\n\t\treadonly ui: TUI;\n\t\treadonly servers: readonly ClientTuiServer[];\n\t\treadonly facetLoader?: FacetLoader;\n\t\trequestRender(): void;\n\t\tfinish(): void;\n\t}): Promise<ExperimentalClientTui> {\n\t\tconst prepared = await prepareClientSession(options.command, options.servers);\n\t\tconst loadedFacets = await combineFacetLoaders(\n\t\t\toptions.facetLoader === undefined ? [] : [options.facetLoader],\n\t\t).load();\n\t\tconst component = new ExperimentalClientTui(options.ui, options.requestRender, options.finish, loadedFacets);\n\t\ttry {\n\t\t\tawait component.#start(prepared);\n\t\t\tawait component.#openPreparedSession(prepared);\n\t\t\treturn component;\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait component.close();\n\t\t\t} catch (cleanupError) {\n\t\t\t\tthrow new AggregateError([error, cleanupError], \"Experimental TUI startup and cleanup failed\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tget layoutRoot(): Component {\n\t\treturn this.#layoutRoot;\n\t}\n\n\trender(width: number): string[] {\n\t\treturn [\n\t\t\t...this.#documentContainer.render(width),\n\t\t\t...this.#pendingMessagesContainer.render(width),\n\t\t\t...this.#statusContainer.render(width),\n\t\t\t...this.#editorContainer.render(width),\n\t\t\t...this.#footerComponent.render(width),\n\t\t];\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.#busy) {\n\t\t\tif (\n\t\t\t\tthis.#keybindings.matches(data, \"app.clear\") ||\n\t\t\t\t(this.#chatInput.getText().length === 0 && this.#keybindings.matches(data, \"app.exit\"))\n\t\t\t) {\n\t\t\t\tthis.#finish();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (this.#screen === \"chat\") {\n\t\t\tthis.#chatInput.handleInput(data);\n\t\t\tthis.#requestRender();\n\t\t\treturn;\n\t\t}\n\t\tthis.#selectList?.handleInput(data);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.#layoutRoot.invalidate();\n\t}\n\n\tdispose(): void {\n\t\tvoid this.close().catch(() => {});\n\t}\n\n\trefreshTheme(): void {\n\t\tconst snapshot = this.#laneSnapshot();\n\t\tif (snapshot !== undefined) this.#chatView?.refreshTheme(snapshot);\n\t\tthis.#rebuild();\n\t}\n\n\tshowError(error: string): void {\n\t\tthis.#status = `Error: ${error}`;\n\t\tthis.#rebuild();\n\t}\n\n\tclose(): Promise<void> {\n\t\tthis.#closePromise ??= this.#close();\n\t\treturn this.#closePromise;\n\t}\n\n\tasync #start(prepared: PreparedClientSession): Promise<void> {\n\t\tconst server = prepared.server;\n\t\tlet presentationFacets = await combineFacetLoaders(\n\t\t\tcreatePresentationFacetLoaders(prepared.presentationPlugins),\n\t\t).load();\n\t\tthis.#presentationFacets = presentationFacets;\n\t\tlet facetHost!: FacetHost;\n\t\tconst reloadPresentationPlugins = (data: JsonValue): Promise<void> => {\n\t\t\tconst operation = this.#facetReloadTail.then(async () => {\n\t\t\t\tconst candidate = await combineFacetLoaders(createPresentationFacetLoaders(data)).load();\n\t\t\t\ttry {\n\t\t\t\t\tawait facetHost.reload(candidate.facets);\n\t\t\t\t} catch (error) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait candidate.dispose();\n\t\t\t\t\t} catch (cleanupError) {\n\t\t\t\t\t\tthrow new AggregateError([error, cleanupError], \"TUI plugin reload and cleanup failed\");\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst retired = presentationFacets;\n\t\t\t\tpresentationFacets = candidate;\n\t\t\t\tthis.#presentationFacets = candidate;\n\t\t\t\tawait retired.dispose();\n\t\t\t});\n\t\t\tthis.#facetReloadTail = operation.catch(() => {});\n\t\t\treturn operation;\n\t\t};\n\t\tconst presentationBridgeFacet = defineFacet({\n\t\t\tid: \"@pi/presentation-bridge\",\n\t\t\tsetup: (env) => {\n\t\t\t\tenv.provide(PresentationUI, {\n\t\t\t\t\tselect: (title, items, selectedValue) =>\n\t\t\t\t\t\tthis.#select(\n\t\t\t\t\t\t\ttitle,\n\t\t\t\t\t\t\titems.map((item) => ({ ...item })),\n\t\t\t\t\t\t\tselectedValue,\n\t\t\t\t\t\t),\n\t\t\t\t\tshowStatus: (status) => {\n\t\t\t\t\t\tthis.#status = status;\n\t\t\t\t\t\tthis.#rebuild();\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tconst commands = env.use(SlashCommands);\n\t\t\t\tconst controller = env.use(AgentController);\n\t\t\t\tconst transcript = env.use(Transcript);\n\t\t\t\tconst sessionFeature: SessionFeature = {\n\t\t\t\t\tserverId: server.serverId,\n\t\t\t\t\tsession: server.session,\n\t\t\t\t\ttranscript,\n\t\t\t\t};\n\t\t\t\tenv.onActivate(() => {\n\t\t\t\t\tif (this.#session !== undefined || this.#slashCommands !== undefined || this.#controller !== undefined) {\n\t\t\t\t\t\tthrow new Error(\"Presentation services are already active\");\n\t\t\t\t\t}\n\t\t\t\t\tthis.#session = sessionFeature;\n\t\t\t\t\tthis.#slashCommands = commands;\n\t\t\t\t\tthis.#controller = controller;\n\t\t\t\t\tenv.own(() => {\n\t\t\t\t\t\tif (this.#session === sessionFeature) this.#session = undefined;\n\t\t\t\t\t\tif (this.#slashCommands === commands) this.#slashCommands = undefined;\n\t\t\t\t\t\tif (this.#controller === controller) this.#controller = undefined;\n\t\t\t\t\t});\n\t\t\t\t\tenv.own(commands.subscribe(() => this.#updateAutocomplete()));\n\t\t\t\t\tif (server.radius) {\n\t\t\t\t\t\tenv.own(\n\t\t\t\t\t\t\tserver.server.connection.subscribe((state) => this.#handleConnectionState(server.serverId, state)),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tenv.own(\n\t\t\t\t\t\t\tserver.session.attachment.subscribe((state) => this.#handleAttachmentState(sessionFeature, state)),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t\tfacetHost = await createFacetHost({\n\t\t\tfacets: [\n\t\t\t\tcreateSlashCommandsRuntimeFacet(),\n\t\t\t\tpresentationBridgeFacet,\n\t\t\t\tcreateBuiltInSlashCommandsFacet({ reloadPresentationPlugins }),\n\t\t\t\t...this.#sharedFacets.facets,\n\t\t\t\t...presentationFacets.facets,\n\t\t\t],\n\t\t\tserviceSources: [server.server, server.session],\n\t\t});\n\t\tthis.#facetHost = facetHost;\n\t}\n\n\tasync #openPreparedSession(prepared: PreparedClientSession): Promise<void> {\n\t\tconst feature = this.#session;\n\t\tif (feature === undefined) throw new Error(`No Session service is available for ${prepared.server.serverId}`);\n\t\tawait feature.session.whenAttached(prepared.summary.sessionId, BACKGROUND_CONTEXT);\n\t\tthis.#selectedServerId = feature.serverId;\n\t\tthis.#sessionId = prepared.summary.sessionId;\n\t\tthis.#updateAutocomplete();\n\t\tawait this.#openLane(feature);\n\t\tthis.#screen = \"chat\";\n\t\tthis.#status = \"\";\n\t\tthis.#rebuild();\n\t}\n\n\tasync #close(): Promise<void> {\n\t\tthis.#closed = true;\n\t\tthis.#completeSelection(undefined);\n\t\tconst errors: unknown[] = [];\n\t\ttry {\n\t\t\tawait this.#recoveryTransition;\n\t\t\tawait this.#closeLane();\n\t\t\tawait this.#facetReloadTail;\n\t\t} catch (error) {\n\t\t\terrors.push(error);\n\t\t}\n\t\tif (this.#facetHost !== undefined) {\n\t\t\ttry {\n\t\t\t\tawait this.#facetHost.dispose();\n\t\t\t} catch (error) {\n\t\t\t\terrors.push(error);\n\t\t\t}\n\t\t\tthis.#facetHost = undefined;\n\t\t}\n\t\tconst generations = [this.#presentationFacets, this.#sharedFacets].filter(\n\t\t\t(generation): generation is LoadedFacets => generation !== undefined,\n\t\t);\n\t\tthis.#presentationFacets = undefined;\n\t\tconst results = await Promise.allSettled(generations.map((generation) => generation.dispose()));\n\t\terrors.push(...results.flatMap((result) => (result.status === \"rejected\" ? [result.reason] : [])));\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose experimental TUI facets\");\n\t}\n\n\t#rebuild(): void {\n\t\tthis.#sessionHeading.setText(\n\t\t\tthis.#sessionId === undefined || this.#selectedServerId === undefined\n\t\t\t\t? \"\"\n\t\t\t\t: theme.fg(\"dim\", `Server: ${this.#selectedServerId}\\nSession: ${this.#sessionId}`),\n\t\t);\n\t\tthis.#statusContainer.clear();\n\t\tif (this.#status.length > 0) {\n\t\t\tthis.#statusContainer.addChild(new Text(theme.fg(\"dim\", this.#status), 1, 0));\n\t\t}\n\t\tif (this.#chatView !== undefined) this.#statusContainer.addChild(this.#chatView.status);\n\t\tthis.#footerComponent.setText(theme.fg(\"dim\", this.#footer()));\n\t\tthis.#editorContainer.clear();\n\t\tif (this.#screen === \"select\" && this.#selection !== undefined) {\n\t\t\tthis.#chatInput.focused = false;\n\t\t\tconst selector = new Container();\n\t\t\tselector.addChild(new Text(theme.bold(this.#selection.title), 1, 1));\n\t\t\tconst items = [...this.#selection.items];\n\t\t\tthis.#selectList = new SelectList(items, Math.min(Math.max(items.length, 1), 12), selectTheme);\n\t\t\tconst selectedIndex = items.findIndex((item) => item.value === this.#selection?.selectedValue);\n\t\t\tif (selectedIndex >= 0) this.#selectList.setSelectedIndex(selectedIndex);\n\t\t\tthis.#selectList.onSelect = (item) => this.#completeSelection(item.value);\n\t\t\tthis.#selectList.onCancel = () => this.#completeSelection(undefined);\n\t\t\tselector.addChild(this.#selectList);\n\t\t\tthis.#editorContainer.addChild(selector);\n\t\t} else {\n\t\t\tthis.#selectList = undefined;\n\t\t\tthis.#chatInput.focused = !this.#busy;\n\t\t\tthis.#editorContainer.addChild(this.#chatInput);\n\t\t}\n\t\tthis.#layoutRoot.invalidate();\n\t\tthis.#requestRender();\n\t}\n\n\t#select(title: string, items: readonly SelectItem[], selectedValue?: string): Promise<string | undefined> {\n\t\tif (this.#selection !== undefined) throw new Error(\"A slash command selector is already active\");\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.#selection = { title, items, ...(selectedValue === undefined ? {} : { selectedValue }), resolve };\n\t\t\tthis.#screen = \"select\";\n\t\t\tthis.#rebuild();\n\t\t});\n\t}\n\n\t#completeSelection(value: string | undefined): void {\n\t\tconst selection = this.#selection;\n\t\tif (selection === undefined) return;\n\t\tthis.#selection = undefined;\n\t\tthis.#screen = \"chat\";\n\t\tselection.resolve(value);\n\t\tif (!this.#closed) this.#rebuild();\n\t}\n\n\t#updateAutocomplete(): void {\n\t\tconst commands = this.#selectedSlashCommands()?.list() ?? [];\n\t\tthis.#chatInput.setAutocompleteProvider(\n\t\t\tnew CombinedAutocompleteProvider(\n\t\t\t\tcommands.map((command) => ({\n\t\t\t\t\tname: command.name,\n\t\t\t\t\tdescription: command.description,\n\t\t\t\t\t...(command.argumentHint === undefined ? {} : { argumentHint: command.argumentHint }),\n\t\t\t\t\t...(command.getArgumentCompletions === undefined\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tgetArgumentCompletions: async (prefix: string) => {\n\t\t\t\t\t\t\t\t\tconst items = await command.getArgumentCompletions!(prefix);\n\t\t\t\t\t\t\t\t\treturn items === null ? null : [...items];\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}),\n\t\t\t\t})),\n\t\t\t\tprocess.cwd(),\n\t\t\t),\n\t\t);\n\t\tthis.#requestRender();\n\t}\n\n\t#selectedSlashCommands(): SlashCommands | undefined {\n\t\treturn this.#slashCommands;\n\t}\n\n\t#handleConnectionState(serverId: string, state: ServerConnectionState): void {\n\t\tif (this.#closed || this.#selectedServerId !== serverId) return;\n\t\tif (state.status === \"connected\") {\n\t\t\tif (this.#laneUnsubscribe === undefined) {\n\t\t\t\tthis.#busy = true;\n\t\t\t\tthis.#status = \"Reattaching Session…\";\n\t\t\t\tthis.#rebuild();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.#busy = true;\n\t\tthis.#status = state.status === \"connecting\" ? \"Reconnecting to Radius…\" : \"Radius disconnected; retrying…\";\n\t\tthis.#queueRecovery(() => this.#closeLane());\n\t\tthis.#rebuild();\n\t}\n\n\t#handleAttachmentState(feature: SessionFeature, state: SessionAttachmentState): void {\n\t\tif (this.#closed || this.#selectedServerId !== feature.serverId || this.#sessionId === undefined) return;\n\t\tif (state.status === \"attached\" && state.sessionId === this.#sessionId) {\n\t\t\tthis.#queueRecovery(async () => {\n\t\t\t\tif (this.#laneUnsubscribe === undefined) await this.#openLane(feature);\n\t\t\t\tthis.#busy = false;\n\t\t\t\tthis.#status = \"\";\n\t\t\t\tthis.#rebuild();\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (state.status === \"attaching\" && state.sessionId === this.#sessionId) {\n\t\t\tthis.#busy = true;\n\t\t\tthis.#status = \"Reattaching Session…\";\n\t\t\tthis.#rebuild();\n\t\t}\n\t}\n\n\t#queueRecovery(operation: () => Promise<void>): void {\n\t\tthis.#recoveryTransition = this.#recoveryTransition\n\t\t\t.then(async () => {\n\t\t\t\tif (!this.#closed) await operation();\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (this.#closed) return;\n\t\t\t\tthis.#busy = true;\n\t\t\t\tthis.#status = `Reconnect error: ${message(error)}`;\n\t\t\t\tthis.#rebuild();\n\t\t\t});\n\t}\n\n\tasync #openLane(feature: SessionFeature): Promise<void> {\n\t\tawait this.#closeLane();\n\t\tconst view = new ExperimentalChatView(this.#ui, process.cwd());\n\t\tthis.#chatView = view;\n\t\tthis.#documentContainer.addChild(this.#sessionHeading);\n\t\tthis.#documentContainer.addChild(view.transcript);\n\t\tthis.#pendingMessagesContainer.addChild(view.pendingMessages);\n\t\tthis.#laneUnsubscribe = feature.transcript.state.subscribe((value) => {\n\t\t\tif (value.snapshot === null) return;\n\t\t\tview.apply(value.snapshot);\n\t\t\tthis.#rebuild();\n\t\t});\n\t\tif (feature.transcript.state.value?.snapshot === null || feature.transcript.state.value?.snapshot === undefined) {\n\t\t\tawait this.#closeLane();\n\t\t\tthrow new Error(\"Transcript has no initialized snapshot\");\n\t\t}\n\t}\n\n\tasync #closeLane(): Promise<void> {\n\t\tthis.#laneUnsubscribe?.();\n\t\tthis.#laneUnsubscribe = undefined;\n\t\tthis.#chatView?.dispose();\n\t\tthis.#chatView = undefined;\n\t\tthis.#documentContainer.clear();\n\t\tthis.#pendingMessagesContainer.clear();\n\t\tthis.#statusContainer.clear();\n\t}\n\n\tasync #runPrompt(messageText: string): Promise<void> {\n\t\tconst prompt = messageText.trim();\n\t\tif (prompt.length === 0) return;\n\t\tif (prompt.startsWith(\"/\")) {\n\t\t\tconst separator = prompt.indexOf(\" \");\n\t\t\tconst name = prompt.slice(1, separator === -1 ? undefined : separator);\n\t\t\tconst args = separator === -1 ? \"\" : prompt.slice(separator + 1).trim();\n\t\t\tawait this.#executeSlashCommand(name, args);\n\t\t\treturn;\n\t\t}\n\t\tthis.#chatInput.setText(\"\");\n\t\ttry {\n\t\t\tawait this.#submitPrompt(prompt);\n\t\t} catch (error) {\n\t\t\tthis.#status = `Error: ${message(error)}`;\n\t\t\tthis.#rebuild();\n\t\t}\n\t}\n\n\tasync #executeSlashCommand(name: string, args: string): Promise<void> {\n\t\tconst command = this.#selectedSlashCommands()\n\t\t\t?.list()\n\t\t\t.find((candidate) => candidate.name === name);\n\t\tthis.#chatInput.setText(\"\");\n\t\tif (command === undefined) {\n\t\t\tthis.#status = `Unknown slash command: /${name}`;\n\t\t\tthis.#rebuild();\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst result = await command.run(args, BACKGROUND_CONTEXT);\n\t\t\tif (result !== undefined) {\n\t\t\t\tif (\"entryId\" in result) this.#reportQueue(result);\n\t\t\t\telse this.#reportOperation(result);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.#status = `Error: ${message(error)}`;\n\t\t\tthis.#rebuild();\n\t\t}\n\t}\n\n\tasync #submitPrompt(prompt: string): Promise<void> {\n\t\tconst controller = this.#selectedController();\n\t\tif (controller === undefined) throw new Error(\"No Session AgentController service is available\");\n\t\tconst operation = this.#laneSnapshot()?.operation;\n\t\tconst running = operation !== null && operation !== undefined;\n\t\tthis.#status = running ? \"Queueing steering message…\" : \"Running turn…\";\n\t\tthis.#rebuild();\n\t\tif (running) this.#reportQueue(await controller.steer({ message: prompt, images: null }, BACKGROUND_CONTEXT));\n\t\telse this.#reportOperation(await controller.prompt({ message: prompt, images: null }, BACKGROUND_CONTEXT));\n\t}\n\n\tasync #queueFollowUp(text: string): Promise<void> {\n\t\tconst controller = this.#selectedController();\n\t\tif (controller === undefined) return;\n\t\ttry {\n\t\t\tthis.#status = \"Queueing follow-up…\";\n\t\t\tthis.#rebuild();\n\t\t\tthis.#reportQueue(await controller.followUp({ message: text, images: null }, BACKGROUND_CONTEXT));\n\t\t} catch (error) {\n\t\t\tthis.#status = `Error: ${message(error)}`;\n\t\t\tthis.#rebuild();\n\t\t}\n\t}\n\n\t#reportOperation(response: AgentOperationResponse): void {\n\t\tthis.#status = response.accepted\n\t\t\t? response.error === null\n\t\t\t\t? \"\"\n\t\t\t\t: `Operation failed: ${response.error.message}`\n\t\t\t: `Operation rejected: ${response.error.message}`;\n\t\tthis.#rebuild();\n\t}\n\n\t#reportQueue(response: AgentQueueResponse): void {\n\t\tthis.#status = response.accepted ? `Queued ${response.entryId}.` : `Message rejected: ${response.error.message}`;\n\t\tthis.#rebuild();\n\t}\n\n\t#interrupt(): void {\n\t\tconst operation = this.#laneSnapshot()?.operation;\n\t\tconst controller = this.#selectedController();\n\t\tif (operation === null || operation === undefined || controller === undefined) return;\n\t\tthis.#status = `Aborting ${operation.id}…`;\n\t\tthis.#rebuild();\n\t\tvoid controller.requestAbort(operation.id, BACKGROUND_CONTEXT).catch((error: unknown) => {\n\t\t\tthis.#status = `Error: ${message(error)}`;\n\t\t\tthis.#rebuild();\n\t\t});\n\t}\n\n\t#selectedController(): AgentController | undefined {\n\t\treturn this.#controller;\n\t}\n\n\t#laneSnapshot() {\n\t\tconst snapshot = this.#session?.transcript.state.value?.snapshot;\n\t\treturn snapshot === null ? undefined : snapshot;\n\t}\n\n\t#footer(): string {\n\t\tconst snapshot = this.#laneSnapshot();\n\t\tif (!snapshot) return \"/model · /thinking · /compact · /reload\";\n\t\treturn `${snapshot.configuration.model.provider}/${snapshot.configuration.model.modelId} · thinking:${snapshot.configuration.thinkingLevel} · ${snapshot.stats.messageCount} messages · /model · /thinking · /compact · /reload`;\n\t}\n}\n\nasync function prepareClientSession(\n\tcommand: ClientCommand,\n\tservers: readonly ClientTuiServer[],\n): Promise<PreparedClientSession> {\n\tconst opened = servers.map((server) => ({\n\t\tserver,\n\t\tservices: server.server.open({\n\t\t\tservices: [SessionDirectory, SessionManagement, PresentationPlugins],\n\t\t\tassertAccess() {},\n\t\t\tonError() {},\n\t\t}),\n\t}));\n\ttry {\n\t\tconst features = opened.map(({ server, services }) => ({\n\t\t\tserver,\n\t\t\tdirectory: services.use(SessionDirectory),\n\t\t\tmanagement: services.use(SessionManagement),\n\t\t\tplugins: services.use(PresentationPlugins),\n\t\t}));\n\t\tawait Promise.all(opened.map(({ services }) => services.ready(BACKGROUND_CONTEXT)));\n\t\tlet selected:\n\t\t\t| {\n\t\t\t\t\treadonly server: ClientTuiServer;\n\t\t\t\t\treadonly management: SessionManagement;\n\t\t\t\t\treadonly plugins: PresentationPlugins;\n\t\t\t\t\treadonly summary: SessionSummary;\n\t\t\t }\n\t\t\t| undefined;\n\t\tif (command.sessionId !== undefined) {\n\t\t\tconst matches = features.flatMap((feature) =>\n\t\t\t\t(feature.directory.state.value?.sessions ?? [])\n\t\t\t\t\t.filter((session) => session.sessionId === command.sessionId)\n\t\t\t\t\t.map((summary) => ({\n\t\t\t\t\t\tserver: feature.server,\n\t\t\t\t\t\tmanagement: feature.management,\n\t\t\t\t\t\tplugins: feature.plugins,\n\t\t\t\t\t\tsummary,\n\t\t\t\t\t})),\n\t\t\t);\n\t\t\tif (matches.length > 1) throw new Error(`Session ${command.sessionId} is available from more than one server`);\n\t\t\tselected = matches[0];\n\t\t\tif (selected === undefined) {\n\t\t\t\tif (command.connect?.transport === \"radius\") {\n\t\t\t\t\tthrow new Error(`Remote server does not contain Session ${command.sessionId}`);\n\t\t\t\t}\n\t\t\t\tconst feature = requireSingleServer(features);\n\t\t\t\tselected = {\n\t\t\t\t\tserver: feature.server,\n\t\t\t\t\tmanagement: feature.management,\n\t\t\t\t\tplugins: feature.plugins,\n\t\t\t\t\tsummary: await feature.management.create({ id: command.sessionId }, BACKGROUND_CONTEXT),\n\t\t\t\t};\n\t\t\t}\n\t\t} else if (command.continue === true || command.resume === true) {\n\t\t\tselected = features\n\t\t\t\t.flatMap((feature) =>\n\t\t\t\t\t(feature.directory.state.value?.sessions ?? []).map((summary) => ({\n\t\t\t\t\t\tserver: feature.server,\n\t\t\t\t\t\tmanagement: feature.management,\n\t\t\t\t\t\tplugins: feature.plugins,\n\t\t\t\t\t\tsummary,\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\t\t.sort(\n\t\t\t\t\t(left, right) =>\n\t\t\t\t\t\tright.summary.createdAt - left.summary.createdAt ||\n\t\t\t\t\t\tleft.summary.serverId.localeCompare(right.summary.serverId) ||\n\t\t\t\t\t\tleft.summary.sessionId.localeCompare(right.summary.sessionId),\n\t\t\t\t)[0];\n\t\t}\n\t\tif (selected === undefined) {\n\t\t\tconst feature = requireSingleServer(features);\n\t\t\tselected = {\n\t\t\t\tserver: feature.server,\n\t\t\t\tmanagement: feature.management,\n\t\t\t\tplugins: feature.plugins,\n\t\t\t\tsummary: await feature.management.create({}, BACKGROUND_CONTEXT),\n\t\t\t};\n\t\t}\n\t\tconst presentationPlugins = await selected.plugins.prepareSession(\n\t\t\t{\n\t\t\t\tsessionId: selected.summary.sessionId,\n\t\t\t\tpackagePaths: command.pluginPackages?.map((packagePath) => resolve(packagePath)) ?? null,\n\t\t\t},\n\t\t\tBACKGROUND_CONTEXT,\n\t\t);\n\t\tawait selected.management.attach(selected.summary.sessionId, BACKGROUND_CONTEXT);\n\t\tawait selected.server.session.whenAttached(selected.summary.sessionId, BACKGROUND_CONTEXT);\n\t\treturn {\n\t\t\tserver: selected.server,\n\t\t\tsummary: selected.summary,\n\t\t\tpresentationPlugins,\n\t\t};\n\t} finally {\n\t\tawait Promise.allSettled(opened.map(({ services }) => services.dispose(BACKGROUND_CONTEXT)));\n\t}\n}\n\nexport async function runClientTui(command: ClientCommand, options: RunClientTuiOptions = {}): Promise<void> {\n\tconst cwd = process.cwd();\n\tconst agentDir = getAgentDir();\n\tconst settingsManager = SettingsManager.create(cwd, agentDir);\n\tconst resourceLoader = new DefaultResourceLoader({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager,\n\t\tnoExtensions: true,\n\t\tnoSkills: true,\n\t\tnoPromptTemplates: true,\n\t\tnoContextFiles: true,\n\t});\n\tawait resourceLoader.reload();\n\tsetRegisteredThemes(resourceLoader.getThemes().themes);\n\tconst runtime = await openClientRuntime(command, options);\n\tconst tui = createInteractiveTui({\n\t\ttuiMode: \"fullscreen\",\n\t\tshowHardwareCursor: settingsManager.getShowHardwareCursor(),\n\t\tlogDirectory: agentDir,\n\t});\n\ttui.setClearOnShrink(settingsManager.getClearOnShrink());\n\tlet component: ExperimentalClientTui | undefined;\n\tlet tuiStarted = false;\n\tconst themeController = new InteractiveThemeController(tui, {\n\t\tgetSettingsManager: () => settingsManager,\n\t\tshowError: (error) => component?.showError(error),\n\t\tonChanged: () => component?.refreshTheme(),\n\t});\n\ttry {\n\t\tlet finish!: () => void;\n\t\tconst finished = new Promise<void>((resolve) => {\n\t\t\tfinish = () => {\n\t\t\t\tthemeController.disableAutoSync();\n\t\t\t\tif (tuiStarted) {\n\t\t\t\t\ttui.stop();\n\t\t\t\t\ttuiStarted = false;\n\t\t\t\t}\n\t\t\t\tresolve();\n\t\t\t};\n\t\t});\n\t\tcomponent = await ExperimentalClientTui.create({\n\t\t\tcommand,\n\t\t\tui: tui,\n\t\t\tservers: runtime.servers.map((server) => ({\n\t\t\t\tserverId: server.route.serverId,\n\t\t\t\tradius: server.route.transport === \"radius\",\n\t\t\t\tserver: server.server,\n\t\t\t\tsession: server.session,\n\t\t\t})),\n\t\t\tfacetLoader: options.facetLoader,\n\t\t\trequestRender: () => tui.requestRender(),\n\t\t\tfinish,\n\t\t});\n\t\ttui.addChild(component);\n\t\ttui.setLayoutRoot(component.layoutRoot);\n\t\ttui.setFocus(component);\n\t\ttuiStarted = true;\n\t\ttui.start();\n\t\tawait themeController.applyFromSettings();\n\t\tawait finished;\n\t} finally {\n\t\tthemeController.dispose();\n\t\tstopThemeWatcher();\n\t\tif (tuiStarted) tui.stop();\n\t\tawait component?.close();\n\t\tawait runtime.dispose();\n\t}\n}\n\nfunction requireSingleServer<T>(features: readonly T[]): T {\n\tif (features.length !== 1) throw new Error(\"Starting a Session requires exactly one server\");\n\treturn features[0]!;\n}\n\nfunction message(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n"]}