muonroi-cli 1.2.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 (1207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +380 -0
  3. package/dist/__test-stubs__/ee-server.d.ts +27 -0
  4. package/dist/__test-stubs__/ee-server.js +138 -0
  5. package/dist/__test-stubs__/ee-server.js.map +1 -0
  6. package/dist/billing/index.d.ts +5 -0
  7. package/dist/billing/index.js +2 -0
  8. package/dist/billing/index.js.map +1 -0
  9. package/dist/cloud/index.d.ts +5 -0
  10. package/dist/cloud/index.js +2 -0
  11. package/dist/cloud/index.js.map +1 -0
  12. package/dist/daemon/scheduler.d.ts +15 -0
  13. package/dist/daemon/scheduler.js +126 -0
  14. package/dist/daemon/scheduler.js.map +1 -0
  15. package/dist/daemon/scheduler.test.d.ts +1 -0
  16. package/dist/daemon/scheduler.test.js +103 -0
  17. package/dist/daemon/scheduler.test.js.map +1 -0
  18. package/dist/ee/__tests__/pipeline.integration.test.d.ts +14 -0
  19. package/dist/ee/__tests__/pipeline.integration.test.js +151 -0
  20. package/dist/ee/__tests__/pipeline.integration.test.js.map +1 -0
  21. package/dist/ee/auth.d.ts +19 -0
  22. package/dist/ee/auth.js +48 -0
  23. package/dist/ee/auth.js.map +1 -0
  24. package/dist/ee/auth.test.d.ts +1 -0
  25. package/dist/ee/auth.test.js +59 -0
  26. package/dist/ee/auth.test.js.map +1 -0
  27. package/dist/ee/bridge.d.ts +68 -0
  28. package/dist/ee/bridge.js +177 -0
  29. package/dist/ee/bridge.js.map +1 -0
  30. package/dist/ee/bridge.test.d.ts +1 -0
  31. package/dist/ee/bridge.test.js +231 -0
  32. package/dist/ee/bridge.test.js.map +1 -0
  33. package/dist/ee/client.d.ts +22 -0
  34. package/dist/ee/client.js +464 -0
  35. package/dist/ee/client.js.map +1 -0
  36. package/dist/ee/client.test.d.ts +1 -0
  37. package/dist/ee/client.test.js +151 -0
  38. package/dist/ee/client.test.js.map +1 -0
  39. package/dist/ee/embedding-cache.d.ts +7 -0
  40. package/dist/ee/embedding-cache.js +33 -0
  41. package/dist/ee/embedding-cache.js.map +1 -0
  42. package/dist/ee/extract-session.d.ts +17 -0
  43. package/dist/ee/extract-session.js +53 -0
  44. package/dist/ee/extract-session.js.map +1 -0
  45. package/dist/ee/extract-session.test.d.ts +1 -0
  46. package/dist/ee/extract-session.test.js +197 -0
  47. package/dist/ee/extract-session.test.js.map +1 -0
  48. package/dist/ee/health.d.ts +29 -0
  49. package/dist/ee/health.js +64 -0
  50. package/dist/ee/health.js.map +1 -0
  51. package/dist/ee/index.d.ts +10 -0
  52. package/dist/ee/index.js +9 -0
  53. package/dist/ee/index.js.map +1 -0
  54. package/dist/ee/intercept.d.ts +46 -0
  55. package/dist/ee/intercept.js +117 -0
  56. package/dist/ee/intercept.js.map +1 -0
  57. package/dist/ee/intercept.test.d.ts +1 -0
  58. package/dist/ee/intercept.test.js +170 -0
  59. package/dist/ee/intercept.test.js.map +1 -0
  60. package/dist/ee/judge.d.ts +36 -0
  61. package/dist/ee/judge.js +56 -0
  62. package/dist/ee/judge.js.map +1 -0
  63. package/dist/ee/judge.test.d.ts +1 -0
  64. package/dist/ee/judge.test.js +139 -0
  65. package/dist/ee/judge.test.js.map +1 -0
  66. package/dist/ee/offline-queue.d.ts +37 -0
  67. package/dist/ee/offline-queue.js +163 -0
  68. package/dist/ee/offline-queue.js.map +1 -0
  69. package/dist/ee/offline-queue.test.d.ts +1 -0
  70. package/dist/ee/offline-queue.test.js +246 -0
  71. package/dist/ee/offline-queue.test.js.map +1 -0
  72. package/dist/ee/posttool.d.ts +11 -0
  73. package/dist/ee/posttool.js +16 -0
  74. package/dist/ee/posttool.js.map +1 -0
  75. package/dist/ee/posttool.test.d.ts +1 -0
  76. package/dist/ee/posttool.test.js +70 -0
  77. package/dist/ee/posttool.test.js.map +1 -0
  78. package/dist/ee/prompt-stale.d.ts +20 -0
  79. package/dist/ee/prompt-stale.js +37 -0
  80. package/dist/ee/prompt-stale.js.map +1 -0
  81. package/dist/ee/prompt-stale.test.d.ts +8 -0
  82. package/dist/ee/prompt-stale.test.js +76 -0
  83. package/dist/ee/prompt-stale.test.js.map +1 -0
  84. package/dist/ee/render.d.ts +20 -0
  85. package/dist/ee/render.js +30 -0
  86. package/dist/ee/render.js.map +1 -0
  87. package/dist/ee/render.test.d.ts +1 -0
  88. package/dist/ee/render.test.js +61 -0
  89. package/dist/ee/render.test.js.map +1 -0
  90. package/dist/ee/scope.d.ts +6 -0
  91. package/dist/ee/scope.js +92 -0
  92. package/dist/ee/scope.js.map +1 -0
  93. package/dist/ee/scope.test.d.ts +1 -0
  94. package/dist/ee/scope.test.js +97 -0
  95. package/dist/ee/scope.test.js.map +1 -0
  96. package/dist/ee/tenant.d.ts +2 -0
  97. package/dist/ee/tenant.js +13 -0
  98. package/dist/ee/tenant.js.map +1 -0
  99. package/dist/ee/touch.test.d.ts +1 -0
  100. package/dist/ee/touch.test.js +60 -0
  101. package/dist/ee/touch.test.js.map +1 -0
  102. package/dist/ee/types.d.ts +300 -0
  103. package/dist/ee/types.js +14 -0
  104. package/dist/ee/types.js.map +1 -0
  105. package/dist/flow/__tests__/migration.test.d.ts +1 -0
  106. package/dist/flow/__tests__/migration.test.js +105 -0
  107. package/dist/flow/__tests__/migration.test.js.map +1 -0
  108. package/dist/flow/__tests__/parser.test.d.ts +1 -0
  109. package/dist/flow/__tests__/parser.test.js +68 -0
  110. package/dist/flow/__tests__/parser.test.js.map +1 -0
  111. package/dist/flow/__tests__/run-manager.test.d.ts +1 -0
  112. package/dist/flow/__tests__/run-manager.test.js +83 -0
  113. package/dist/flow/__tests__/run-manager.test.js.map +1 -0
  114. package/dist/flow/__tests__/scaffold.test.d.ts +1 -0
  115. package/dist/flow/__tests__/scaffold.test.js +47 -0
  116. package/dist/flow/__tests__/scaffold.test.js.map +1 -0
  117. package/dist/flow/__tests__/warning-persist.test.d.ts +5 -0
  118. package/dist/flow/__tests__/warning-persist.test.js +96 -0
  119. package/dist/flow/__tests__/warning-persist.test.js.map +1 -0
  120. package/dist/flow/artifact-io.d.ts +16 -0
  121. package/dist/flow/artifact-io.js +35 -0
  122. package/dist/flow/artifact-io.js.map +1 -0
  123. package/dist/flow/compaction/__tests__/compress.test.d.ts +1 -0
  124. package/dist/flow/compaction/__tests__/compress.test.js +63 -0
  125. package/dist/flow/compaction/__tests__/compress.test.js.map +1 -0
  126. package/dist/flow/compaction/__tests__/extract.test.d.ts +1 -0
  127. package/dist/flow/compaction/__tests__/extract.test.js +66 -0
  128. package/dist/flow/compaction/__tests__/extract.test.js.map +1 -0
  129. package/dist/flow/compaction/__tests__/preserve.test.d.ts +1 -0
  130. package/dist/flow/compaction/__tests__/preserve.test.js +62 -0
  131. package/dist/flow/compaction/__tests__/preserve.test.js.map +1 -0
  132. package/dist/flow/compaction/compress.d.ts +22 -0
  133. package/dist/flow/compaction/compress.js +47 -0
  134. package/dist/flow/compaction/compress.js.map +1 -0
  135. package/dist/flow/compaction/extract.d.ts +23 -0
  136. package/dist/flow/compaction/extract.js +46 -0
  137. package/dist/flow/compaction/extract.js.map +1 -0
  138. package/dist/flow/compaction/index.d.ts +23 -0
  139. package/dist/flow/compaction/index.js +63 -0
  140. package/dist/flow/compaction/index.js.map +1 -0
  141. package/dist/flow/compaction/preserve.d.ts +24 -0
  142. package/dist/flow/compaction/preserve.js +35 -0
  143. package/dist/flow/compaction/preserve.js.map +1 -0
  144. package/dist/flow/index.d.ts +12 -0
  145. package/dist/flow/index.js +16 -0
  146. package/dist/flow/index.js.map +1 -0
  147. package/dist/flow/migration.d.ts +23 -0
  148. package/dist/flow/migration.js +126 -0
  149. package/dist/flow/migration.js.map +1 -0
  150. package/dist/flow/parser.d.ts +27 -0
  151. package/dist/flow/parser.js +68 -0
  152. package/dist/flow/parser.js.map +1 -0
  153. package/dist/flow/run-manager.d.ts +36 -0
  154. package/dist/flow/run-manager.js +110 -0
  155. package/dist/flow/run-manager.js.map +1 -0
  156. package/dist/flow/scaffold.d.ts +22 -0
  157. package/dist/flow/scaffold.js +47 -0
  158. package/dist/flow/scaffold.js.map +1 -0
  159. package/dist/flow/warning-persist.d.ts +9 -0
  160. package/dist/flow/warning-persist.js +75 -0
  161. package/dist/flow/warning-persist.js.map +1 -0
  162. package/dist/gsd/__tests__/types.test.d.ts +1 -0
  163. package/dist/gsd/__tests__/types.test.js +65 -0
  164. package/dist/gsd/__tests__/types.test.js.map +1 -0
  165. package/dist/gsd/index.d.ts +1 -0
  166. package/dist/gsd/index.js +2 -0
  167. package/dist/gsd/index.js.map +1 -0
  168. package/dist/gsd/types.d.ts +4 -0
  169. package/dist/gsd/types.js +39 -0
  170. package/dist/gsd/types.js.map +1 -0
  171. package/dist/headless/output.d.ts +68 -0
  172. package/dist/headless/output.js +206 -0
  173. package/dist/headless/output.js.map +1 -0
  174. package/dist/headless/output.test.d.ts +1 -0
  175. package/dist/headless/output.test.js +178 -0
  176. package/dist/headless/output.test.js.map +1 -0
  177. package/dist/hooks/config.d.ts +18 -0
  178. package/dist/hooks/config.js +39 -0
  179. package/dist/hooks/config.js.map +1 -0
  180. package/dist/hooks/index.d.ts +44 -0
  181. package/dist/hooks/index.js +187 -0
  182. package/dist/hooks/index.js.map +1 -0
  183. package/dist/hooks/types.d.ts +130 -0
  184. package/dist/hooks/types.js +47 -0
  185. package/dist/hooks/types.js.map +1 -0
  186. package/dist/index.d.ts +2 -0
  187. package/dist/index.js +512 -0
  188. package/dist/index.js.map +1 -0
  189. package/dist/lsp/builtins.d.ts +9 -0
  190. package/dist/lsp/builtins.js +351 -0
  191. package/dist/lsp/builtins.js.map +1 -0
  192. package/dist/lsp/builtins.test.d.ts +1 -0
  193. package/dist/lsp/builtins.test.js +91 -0
  194. package/dist/lsp/builtins.test.js.map +1 -0
  195. package/dist/lsp/client.d.ts +20 -0
  196. package/dist/lsp/client.js +290 -0
  197. package/dist/lsp/client.js.map +1 -0
  198. package/dist/lsp/manager.d.ts +20 -0
  199. package/dist/lsp/manager.js +235 -0
  200. package/dist/lsp/manager.js.map +1 -0
  201. package/dist/lsp/manager.test.d.ts +1 -0
  202. package/dist/lsp/manager.test.js +133 -0
  203. package/dist/lsp/manager.test.js.map +1 -0
  204. package/dist/lsp/npm-cache.d.ts +2 -0
  205. package/dist/lsp/npm-cache.js +105 -0
  206. package/dist/lsp/npm-cache.js.map +1 -0
  207. package/dist/lsp/npm-cache.test.d.ts +1 -0
  208. package/dist/lsp/npm-cache.test.js +53 -0
  209. package/dist/lsp/npm-cache.test.js.map +1 -0
  210. package/dist/lsp/runtime.d.ts +6 -0
  211. package/dist/lsp/runtime.js +53 -0
  212. package/dist/lsp/runtime.js.map +1 -0
  213. package/dist/lsp/smoke.test.d.ts +1 -0
  214. package/dist/lsp/smoke.test.js +56 -0
  215. package/dist/lsp/smoke.test.js.map +1 -0
  216. package/dist/lsp/types.d.ts +83 -0
  217. package/dist/lsp/types.js +12 -0
  218. package/dist/lsp/types.js.map +1 -0
  219. package/dist/mcp/auto-setup.d.ts +2 -0
  220. package/dist/mcp/auto-setup.js +76 -0
  221. package/dist/mcp/auto-setup.js.map +1 -0
  222. package/dist/mcp/catalog.d.ts +10 -0
  223. package/dist/mcp/catalog.js +121 -0
  224. package/dist/mcp/catalog.js.map +1 -0
  225. package/dist/mcp/oauth-callback.d.ts +9 -0
  226. package/dist/mcp/oauth-callback.js +49 -0
  227. package/dist/mcp/oauth-callback.js.map +1 -0
  228. package/dist/mcp/oauth-provider.d.ts +33 -0
  229. package/dist/mcp/oauth-provider.js +94 -0
  230. package/dist/mcp/oauth-provider.js.map +1 -0
  231. package/dist/mcp/parse-headers.d.ts +2 -0
  232. package/dist/mcp/parse-headers.js +33 -0
  233. package/dist/mcp/parse-headers.js.map +1 -0
  234. package/dist/mcp/parse-headers.test.d.ts +1 -0
  235. package/dist/mcp/parse-headers.test.js +43 -0
  236. package/dist/mcp/parse-headers.test.js.map +1 -0
  237. package/dist/mcp/runtime.d.ts +11 -0
  238. package/dist/mcp/runtime.js +81 -0
  239. package/dist/mcp/runtime.js.map +1 -0
  240. package/dist/mcp/smoke.test.d.ts +1 -0
  241. package/dist/mcp/smoke.test.js +159 -0
  242. package/dist/mcp/smoke.test.js.map +1 -0
  243. package/dist/mcp/validate.d.ts +9 -0
  244. package/dist/mcp/validate.js +39 -0
  245. package/dist/mcp/validate.js.map +1 -0
  246. package/dist/models/__tests__/registry.test.d.ts +1 -0
  247. package/dist/models/__tests__/registry.test.js +74 -0
  248. package/dist/models/__tests__/registry.test.js.map +1 -0
  249. package/dist/models/catalog-client.d.ts +21 -0
  250. package/dist/models/catalog-client.js +53 -0
  251. package/dist/models/catalog-client.js.map +1 -0
  252. package/dist/models/classify-tier.d.ts +2 -0
  253. package/dist/models/classify-tier.js +34 -0
  254. package/dist/models/classify-tier.js.map +1 -0
  255. package/dist/models/index.d.ts +1 -0
  256. package/dist/models/index.js +2 -0
  257. package/dist/models/index.js.map +1 -0
  258. package/dist/models/registry.d.ts +19 -0
  259. package/dist/models/registry.js +65 -0
  260. package/dist/models/registry.js.map +1 -0
  261. package/dist/ops/bug-report.d.ts +28 -0
  262. package/dist/ops/bug-report.js +67 -0
  263. package/dist/ops/bug-report.js.map +1 -0
  264. package/dist/ops/bug-report.test.d.ts +1 -0
  265. package/dist/ops/bug-report.test.js +148 -0
  266. package/dist/ops/bug-report.test.js.map +1 -0
  267. package/dist/ops/doctor.d.ts +16 -0
  268. package/dist/ops/doctor.js +162 -0
  269. package/dist/ops/doctor.js.map +1 -0
  270. package/dist/ops/doctor.test.d.ts +1 -0
  271. package/dist/ops/doctor.test.js +95 -0
  272. package/dist/ops/doctor.test.js.map +1 -0
  273. package/dist/orchestrator/__tests__/flow-resume.test.d.ts +5 -0
  274. package/dist/orchestrator/__tests__/flow-resume.test.js +61 -0
  275. package/dist/orchestrator/__tests__/flow-resume.test.js.map +1 -0
  276. package/dist/orchestrator/__tests__/route-feedback.test.d.ts +1 -0
  277. package/dist/orchestrator/__tests__/route-feedback.test.js +47 -0
  278. package/dist/orchestrator/__tests__/route-feedback.test.js.map +1 -0
  279. package/dist/orchestrator/abort.d.ts +29 -0
  280. package/dist/orchestrator/abort.js +31 -0
  281. package/dist/orchestrator/abort.js.map +1 -0
  282. package/dist/orchestrator/abort.test.d.ts +1 -0
  283. package/dist/orchestrator/abort.test.js +34 -0
  284. package/dist/orchestrator/abort.test.js.map +1 -0
  285. package/dist/orchestrator/agent.test.d.ts +1 -0
  286. package/dist/orchestrator/agent.test.js +126 -0
  287. package/dist/orchestrator/agent.test.js.map +1 -0
  288. package/dist/orchestrator/cleanup.test.d.ts +1 -0
  289. package/dist/orchestrator/cleanup.test.js +67 -0
  290. package/dist/orchestrator/cleanup.test.js.map +1 -0
  291. package/dist/orchestrator/compaction.d.ts +36 -0
  292. package/dist/orchestrator/compaction.js +375 -0
  293. package/dist/orchestrator/compaction.js.map +1 -0
  294. package/dist/orchestrator/compaction.test.d.ts +1 -0
  295. package/dist/orchestrator/compaction.test.js +105 -0
  296. package/dist/orchestrator/compaction.test.js.map +1 -0
  297. package/dist/orchestrator/delegations.d.ts +49 -0
  298. package/dist/orchestrator/delegations.js +283 -0
  299. package/dist/orchestrator/delegations.js.map +1 -0
  300. package/dist/orchestrator/delegations.test.d.ts +1 -0
  301. package/dist/orchestrator/delegations.test.js +107 -0
  302. package/dist/orchestrator/delegations.test.js.map +1 -0
  303. package/dist/orchestrator/flow-resume.d.ts +25 -0
  304. package/dist/orchestrator/flow-resume.js +53 -0
  305. package/dist/orchestrator/flow-resume.js.map +1 -0
  306. package/dist/orchestrator/orchestrator.d.ts +239 -0
  307. package/dist/orchestrator/orchestrator.js +3209 -0
  308. package/dist/orchestrator/orchestrator.js.map +1 -0
  309. package/dist/orchestrator/pending-calls.d.ts +75 -0
  310. package/dist/orchestrator/pending-calls.js +178 -0
  311. package/dist/orchestrator/pending-calls.js.map +1 -0
  312. package/dist/orchestrator/pending-calls.test.d.ts +1 -0
  313. package/dist/orchestrator/pending-calls.test.js +188 -0
  314. package/dist/orchestrator/pending-calls.test.js.map +1 -0
  315. package/dist/orchestrator/reasoning.d.ts +3 -0
  316. package/dist/orchestrator/reasoning.js +53 -0
  317. package/dist/orchestrator/reasoning.js.map +1 -0
  318. package/dist/orchestrator/reasoning.test.d.ts +1 -0
  319. package/dist/orchestrator/reasoning.test.js +26 -0
  320. package/dist/orchestrator/reasoning.test.js.map +1 -0
  321. package/dist/orchestrator/sandbox.test.d.ts +1 -0
  322. package/dist/orchestrator/sandbox.test.js +94 -0
  323. package/dist/orchestrator/sandbox.test.js.map +1 -0
  324. package/dist/pil/__tests__/budget.test.d.ts +1 -0
  325. package/dist/pil/__tests__/budget.test.js +33 -0
  326. package/dist/pil/__tests__/budget.test.js.map +1 -0
  327. package/dist/pil/__tests__/layer1-intent.test.d.ts +1 -0
  328. package/dist/pil/__tests__/layer1-intent.test.js +199 -0
  329. package/dist/pil/__tests__/layer1-intent.test.js.map +1 -0
  330. package/dist/pil/__tests__/layer2-personality.test.d.ts +1 -0
  331. package/dist/pil/__tests__/layer2-personality.test.js +57 -0
  332. package/dist/pil/__tests__/layer2-personality.test.js.map +1 -0
  333. package/dist/pil/__tests__/layer3-ee-injection.test.d.ts +1 -0
  334. package/dist/pil/__tests__/layer3-ee-injection.test.js +96 -0
  335. package/dist/pil/__tests__/layer3-ee-injection.test.js.map +1 -0
  336. package/dist/pil/__tests__/layer4-gsd.test.d.ts +1 -0
  337. package/dist/pil/__tests__/layer4-gsd.test.js +68 -0
  338. package/dist/pil/__tests__/layer4-gsd.test.js.map +1 -0
  339. package/dist/pil/__tests__/layer5-context.test.d.ts +1 -0
  340. package/dist/pil/__tests__/layer5-context.test.js +100 -0
  341. package/dist/pil/__tests__/layer5-context.test.js.map +1 -0
  342. package/dist/pil/__tests__/layer6-output.test.d.ts +1 -0
  343. package/dist/pil/__tests__/layer6-output.test.js +115 -0
  344. package/dist/pil/__tests__/layer6-output.test.js.map +1 -0
  345. package/dist/pil/__tests__/ollama-classify.test.d.ts +1 -0
  346. package/dist/pil/__tests__/ollama-classify.test.js +68 -0
  347. package/dist/pil/__tests__/ollama-classify.test.js.map +1 -0
  348. package/dist/pil/__tests__/orchestrator-integration.test.d.ts +10 -0
  349. package/dist/pil/__tests__/orchestrator-integration.test.js +98 -0
  350. package/dist/pil/__tests__/orchestrator-integration.test.js.map +1 -0
  351. package/dist/pil/__tests__/pipeline.test.d.ts +1 -0
  352. package/dist/pil/__tests__/pipeline.test.js +150 -0
  353. package/dist/pil/__tests__/pipeline.test.js.map +1 -0
  354. package/dist/pil/__tests__/response-tools.test.d.ts +1 -0
  355. package/dist/pil/__tests__/response-tools.test.js +133 -0
  356. package/dist/pil/__tests__/response-tools.test.js.map +1 -0
  357. package/dist/pil/__tests__/schema.test.d.ts +1 -0
  358. package/dist/pil/__tests__/schema.test.js +112 -0
  359. package/dist/pil/__tests__/schema.test.js.map +1 -0
  360. package/dist/pil/__tests__/store.test.d.ts +1 -0
  361. package/dist/pil/__tests__/store.test.js +44 -0
  362. package/dist/pil/__tests__/store.test.js.map +1 -0
  363. package/dist/pil/__tests__/task-tier-map.test.d.ts +1 -0
  364. package/dist/pil/__tests__/task-tier-map.test.js +33 -0
  365. package/dist/pil/__tests__/task-tier-map.test.js.map +1 -0
  366. package/dist/pil/budget.d.ts +8 -0
  367. package/dist/pil/budget.js +17 -0
  368. package/dist/pil/budget.js.map +1 -0
  369. package/dist/pil/index.d.ts +11 -0
  370. package/dist/pil/index.js +11 -0
  371. package/dist/pil/index.js.map +1 -0
  372. package/dist/pil/layer1-intent.d.ts +13 -0
  373. package/dist/pil/layer1-intent.js +142 -0
  374. package/dist/pil/layer1-intent.js.map +1 -0
  375. package/dist/pil/layer2-personality.d.ts +2 -0
  376. package/dist/pil/layer2-personality.js +29 -0
  377. package/dist/pil/layer2-personality.js.map +1 -0
  378. package/dist/pil/layer3-ee-injection.d.ts +10 -0
  379. package/dist/pil/layer3-ee-injection.js +72 -0
  380. package/dist/pil/layer3-ee-injection.js.map +1 -0
  381. package/dist/pil/layer4-gsd.d.ts +2 -0
  382. package/dist/pil/layer4-gsd.js +64 -0
  383. package/dist/pil/layer4-gsd.js.map +1 -0
  384. package/dist/pil/layer5-context.d.ts +2 -0
  385. package/dist/pil/layer5-context.js +124 -0
  386. package/dist/pil/layer5-context.js.map +1 -0
  387. package/dist/pil/layer6-output.d.ts +18 -0
  388. package/dist/pil/layer6-output.js +105 -0
  389. package/dist/pil/layer6-output.js.map +1 -0
  390. package/dist/pil/ollama-classify.d.ts +13 -0
  391. package/dist/pil/ollama-classify.js +38 -0
  392. package/dist/pil/ollama-classify.js.map +1 -0
  393. package/dist/pil/pipeline.d.ts +16 -0
  394. package/dist/pil/pipeline.js +96 -0
  395. package/dist/pil/pipeline.js.map +1 -0
  396. package/dist/pil/response-tools.d.ts +63 -0
  397. package/dist/pil/response-tools.js +87 -0
  398. package/dist/pil/response-tools.js.map +1 -0
  399. package/dist/pil/schema.d.ts +80 -0
  400. package/dist/pil/schema.js +43 -0
  401. package/dist/pil/schema.js.map +1 -0
  402. package/dist/pil/store.d.ts +12 -0
  403. package/dist/pil/store.js +21 -0
  404. package/dist/pil/store.js.map +1 -0
  405. package/dist/pil/task-tier-map.d.ts +30 -0
  406. package/dist/pil/task-tier-map.js +83 -0
  407. package/dist/pil/task-tier-map.js.map +1 -0
  408. package/dist/pil/timeout.d.ts +7 -0
  409. package/dist/pil/timeout.js +10 -0
  410. package/dist/pil/timeout.js.map +1 -0
  411. package/dist/pil/types.d.ts +39 -0
  412. package/dist/pil/types.js +7 -0
  413. package/dist/pil/types.js.map +1 -0
  414. package/dist/providers/__test-utils__/load-fixture.d.ts +9 -0
  415. package/dist/providers/__test-utils__/load-fixture.js +34 -0
  416. package/dist/providers/__test-utils__/load-fixture.js.map +1 -0
  417. package/dist/providers/__tests__/runtime-integration.test.d.ts +1 -0
  418. package/dist/providers/__tests__/runtime-integration.test.js +71 -0
  419. package/dist/providers/__tests__/runtime-integration.test.js.map +1 -0
  420. package/dist/providers/__tests__/runtime.test.d.ts +1 -0
  421. package/dist/providers/__tests__/runtime.test.js +76 -0
  422. package/dist/providers/__tests__/runtime.test.js.map +1 -0
  423. package/dist/providers/adapter.d.ts +15 -0
  424. package/dist/providers/adapter.js +45 -0
  425. package/dist/providers/adapter.js.map +1 -0
  426. package/dist/providers/adapter.test.d.ts +1 -0
  427. package/dist/providers/adapter.test.js +19 -0
  428. package/dist/providers/adapter.test.js.map +1 -0
  429. package/dist/providers/anthropic.d.ts +53 -0
  430. package/dist/providers/anthropic.js +141 -0
  431. package/dist/providers/anthropic.js.map +1 -0
  432. package/dist/providers/errors.d.ts +18 -0
  433. package/dist/providers/errors.js +36 -0
  434. package/dist/providers/errors.js.map +1 -0
  435. package/dist/providers/errors.test.d.ts +1 -0
  436. package/dist/providers/errors.test.js +66 -0
  437. package/dist/providers/errors.test.js.map +1 -0
  438. package/dist/providers/gemini.d.ts +11 -0
  439. package/dist/providers/gemini.js +33 -0
  440. package/dist/providers/gemini.js.map +1 -0
  441. package/dist/providers/gemini.test.d.ts +1 -0
  442. package/dist/providers/gemini.test.js +37 -0
  443. package/dist/providers/gemini.test.js.map +1 -0
  444. package/dist/providers/index.d.ts +11 -0
  445. package/dist/providers/index.js +15 -0
  446. package/dist/providers/index.js.map +1 -0
  447. package/dist/providers/keychain.d.ts +32 -0
  448. package/dist/providers/keychain.js +127 -0
  449. package/dist/providers/keychain.js.map +1 -0
  450. package/dist/providers/keychain.test.d.ts +1 -0
  451. package/dist/providers/keychain.test.js +80 -0
  452. package/dist/providers/keychain.test.js.map +1 -0
  453. package/dist/providers/ollama.d.ts +13 -0
  454. package/dist/providers/ollama.js +33 -0
  455. package/dist/providers/ollama.js.map +1 -0
  456. package/dist/providers/ollama.test.d.ts +1 -0
  457. package/dist/providers/ollama.test.js +37 -0
  458. package/dist/providers/ollama.test.js.map +1 -0
  459. package/dist/providers/openai-compatible.d.ts +15 -0
  460. package/dist/providers/openai-compatible.js +45 -0
  461. package/dist/providers/openai-compatible.js.map +1 -0
  462. package/dist/providers/openai-compatible.test.d.ts +1 -0
  463. package/dist/providers/openai-compatible.test.js +53 -0
  464. package/dist/providers/openai-compatible.test.js.map +1 -0
  465. package/dist/providers/openai.d.ts +11 -0
  466. package/dist/providers/openai.js +33 -0
  467. package/dist/providers/openai.js.map +1 -0
  468. package/dist/providers/openai.test.d.ts +1 -0
  469. package/dist/providers/openai.test.js +53 -0
  470. package/dist/providers/openai.test.js.map +1 -0
  471. package/dist/providers/patch-zod-schema.d.ts +24 -0
  472. package/dist/providers/patch-zod-schema.js +121 -0
  473. package/dist/providers/patch-zod-schema.js.map +1 -0
  474. package/dist/providers/pricing.d.ts +21 -0
  475. package/dist/providers/pricing.js +50 -0
  476. package/dist/providers/pricing.js.map +1 -0
  477. package/dist/providers/pricing.test.d.ts +1 -0
  478. package/dist/providers/pricing.test.js +47 -0
  479. package/dist/providers/pricing.test.js.map +1 -0
  480. package/dist/providers/runtime.d.ts +21 -0
  481. package/dist/providers/runtime.js +83 -0
  482. package/dist/providers/runtime.js.map +1 -0
  483. package/dist/providers/stream-loop.d.ts +16 -0
  484. package/dist/providers/stream-loop.js +63 -0
  485. package/dist/providers/stream-loop.js.map +1 -0
  486. package/dist/providers/types.d.ts +106 -0
  487. package/dist/providers/types.js +12 -0
  488. package/dist/providers/types.js.map +1 -0
  489. package/dist/providers/vision-proxy.d.ts +23 -0
  490. package/dist/providers/vision-proxy.js +152 -0
  491. package/dist/providers/vision-proxy.js.map +1 -0
  492. package/dist/providers/vision-proxy.test.d.ts +1 -0
  493. package/dist/providers/vision-proxy.test.js +136 -0
  494. package/dist/providers/vision-proxy.test.js.map +1 -0
  495. package/dist/router/classifier/grammars.d.ts +3 -0
  496. package/dist/router/classifier/grammars.js +14 -0
  497. package/dist/router/classifier/grammars.js.map +1 -0
  498. package/dist/router/classifier/index.d.ts +3 -0
  499. package/dist/router/classifier/index.js +19 -0
  500. package/dist/router/classifier/index.js.map +1 -0
  501. package/dist/router/classifier/index.test.d.ts +1 -0
  502. package/dist/router/classifier/index.test.js +29 -0
  503. package/dist/router/classifier/index.test.js.map +1 -0
  504. package/dist/router/classifier/regex.d.ts +2 -0
  505. package/dist/router/classifier/regex.js +45 -0
  506. package/dist/router/classifier/regex.js.map +1 -0
  507. package/dist/router/classifier/regex.test.d.ts +1 -0
  508. package/dist/router/classifier/regex.test.js +42 -0
  509. package/dist/router/classifier/regex.test.js.map +1 -0
  510. package/dist/router/classifier/tree-sitter.d.ts +5 -0
  511. package/dist/router/classifier/tree-sitter.js +84 -0
  512. package/dist/router/classifier/tree-sitter.js.map +1 -0
  513. package/dist/router/classifier/tree-sitter.test.d.ts +1 -0
  514. package/dist/router/classifier/tree-sitter.test.js +23 -0
  515. package/dist/router/classifier/tree-sitter.test.js.map +1 -0
  516. package/dist/router/cold.d.ts +7 -0
  517. package/dist/router/cold.js +21 -0
  518. package/dist/router/cold.js.map +1 -0
  519. package/dist/router/cold.test.d.ts +1 -0
  520. package/dist/router/cold.test.js +56 -0
  521. package/dist/router/cold.test.js.map +1 -0
  522. package/dist/router/decide.d.ts +42 -0
  523. package/dist/router/decide.js +300 -0
  524. package/dist/router/decide.js.map +1 -0
  525. package/dist/router/decide.test.d.ts +1 -0
  526. package/dist/router/decide.test.js +116 -0
  527. package/dist/router/decide.test.js.map +1 -0
  528. package/dist/router/health.d.ts +10 -0
  529. package/dist/router/health.js +44 -0
  530. package/dist/router/health.js.map +1 -0
  531. package/dist/router/health.test.d.ts +1 -0
  532. package/dist/router/health.test.js +52 -0
  533. package/dist/router/health.test.js.map +1 -0
  534. package/dist/router/store.d.ts +24 -0
  535. package/dist/router/store.js +27 -0
  536. package/dist/router/store.js.map +1 -0
  537. package/dist/router/types.d.ts +18 -0
  538. package/dist/router/types.js +2 -0
  539. package/dist/router/types.js.map +1 -0
  540. package/dist/router/warm.d.ts +7 -0
  541. package/dist/router/warm.js +40 -0
  542. package/dist/router/warm.js.map +1 -0
  543. package/dist/router/warm.test.d.ts +1 -0
  544. package/dist/router/warm.test.js +144 -0
  545. package/dist/router/warm.test.js.map +1 -0
  546. package/dist/storage/__tests__/migrations.test.d.ts +12 -0
  547. package/dist/storage/__tests__/migrations.test.js +357 -0
  548. package/dist/storage/__tests__/migrations.test.js.map +1 -0
  549. package/dist/storage/atomic-io.d.ts +18 -0
  550. package/dist/storage/atomic-io.js +65 -0
  551. package/dist/storage/atomic-io.js.map +1 -0
  552. package/dist/storage/atomic-io.test.d.ts +1 -0
  553. package/dist/storage/atomic-io.test.js +49 -0
  554. package/dist/storage/atomic-io.test.js.map +1 -0
  555. package/dist/storage/config.d.ts +22 -0
  556. package/dist/storage/config.js +35 -0
  557. package/dist/storage/config.js.map +1 -0
  558. package/dist/storage/config.test.d.ts +1 -0
  559. package/dist/storage/config.test.js +29 -0
  560. package/dist/storage/config.test.js.map +1 -0
  561. package/dist/storage/db.d.ts +18 -0
  562. package/dist/storage/db.js +71 -0
  563. package/dist/storage/db.js.map +1 -0
  564. package/dist/storage/index.d.ts +8 -0
  565. package/dist/storage/index.js +10 -0
  566. package/dist/storage/index.js.map +1 -0
  567. package/dist/storage/migrations.d.ts +2 -0
  568. package/dist/storage/migrations.js +138 -0
  569. package/dist/storage/migrations.js.map +1 -0
  570. package/dist/storage/session-dir.d.ts +27 -0
  571. package/dist/storage/session-dir.js +36 -0
  572. package/dist/storage/session-dir.js.map +1 -0
  573. package/dist/storage/sessions.d.ts +16 -0
  574. package/dist/storage/sessions.js +142 -0
  575. package/dist/storage/sessions.js.map +1 -0
  576. package/dist/storage/tool-results.d.ts +4 -0
  577. package/dist/storage/tool-results.js +50 -0
  578. package/dist/storage/tool-results.js.map +1 -0
  579. package/dist/storage/transcript-view.d.ts +14 -0
  580. package/dist/storage/transcript-view.js +23 -0
  581. package/dist/storage/transcript-view.js.map +1 -0
  582. package/dist/storage/transcript.d.ts +12 -0
  583. package/dist/storage/transcript.js +269 -0
  584. package/dist/storage/transcript.js.map +1 -0
  585. package/dist/storage/transcript.test.d.ts +1 -0
  586. package/dist/storage/transcript.test.js +22 -0
  587. package/dist/storage/transcript.test.js.map +1 -0
  588. package/dist/storage/usage-cap.d.ts +34 -0
  589. package/dist/storage/usage-cap.js +54 -0
  590. package/dist/storage/usage-cap.js.map +1 -0
  591. package/dist/storage/usage-cap.test.d.ts +1 -0
  592. package/dist/storage/usage-cap.test.js +51 -0
  593. package/dist/storage/usage-cap.test.js.map +1 -0
  594. package/dist/storage/usage.d.ts +11 -0
  595. package/dist/storage/usage.js +65 -0
  596. package/dist/storage/usage.js.map +1 -0
  597. package/dist/storage/workspaces.d.ts +9 -0
  598. package/dist/storage/workspaces.js +60 -0
  599. package/dist/storage/workspaces.js.map +1 -0
  600. package/dist/tools/bash.d.ts +46 -0
  601. package/dist/tools/bash.js +541 -0
  602. package/dist/tools/bash.js.map +1 -0
  603. package/dist/tools/bash.test.d.ts +1 -0
  604. package/dist/tools/bash.test.js +240 -0
  605. package/dist/tools/bash.test.js.map +1 -0
  606. package/dist/tools/computer.d.ts +88 -0
  607. package/dist/tools/computer.js +443 -0
  608. package/dist/tools/computer.js.map +1 -0
  609. package/dist/tools/computer.test.d.ts +1 -0
  610. package/dist/tools/computer.test.js +142 -0
  611. package/dist/tools/computer.test.js.map +1 -0
  612. package/dist/tools/file.d.ts +17 -0
  613. package/dist/tools/file.js +101 -0
  614. package/dist/tools/file.js.map +1 -0
  615. package/dist/tools/file.test.d.ts +1 -0
  616. package/dist/tools/file.test.js +58 -0
  617. package/dist/tools/file.test.js.map +1 -0
  618. package/dist/tools/grep.d.ts +8 -0
  619. package/dist/tools/grep.js +144 -0
  620. package/dist/tools/grep.js.map +1 -0
  621. package/dist/tools/registry.d.ts +19 -0
  622. package/dist/tools/registry.js +245 -0
  623. package/dist/tools/registry.js.map +1 -0
  624. package/dist/tools/schedule.d.ts +85 -0
  625. package/dist/tools/schedule.js +498 -0
  626. package/dist/tools/schedule.js.map +1 -0
  627. package/dist/tools/schedule.test.d.ts +1 -0
  628. package/dist/tools/schedule.test.js +118 -0
  629. package/dist/tools/schedule.test.js.map +1 -0
  630. package/dist/types/index.d.ts +279 -0
  631. package/dist/types/index.js +6 -0
  632. package/dist/types/index.js.map +1 -0
  633. package/dist/ui/agents-modal.d.ts +36 -0
  634. package/dist/ui/agents-modal.js +54 -0
  635. package/dist/ui/agents-modal.js.map +1 -0
  636. package/dist/ui/app.d.ts +31 -0
  637. package/dist/ui/app.js +3957 -0
  638. package/dist/ui/app.js.map +1 -0
  639. package/dist/ui/components/SuggestionOverlay.d.ts +6 -0
  640. package/dist/ui/components/SuggestionOverlay.js +12 -0
  641. package/dist/ui/components/SuggestionOverlay.js.map +1 -0
  642. package/dist/ui/components/btw-overlay.d.ts +11 -0
  643. package/dist/ui/components/btw-overlay.js +16 -0
  644. package/dist/ui/components/btw-overlay.js.map +1 -0
  645. package/dist/ui/hooks/useTypeahead.d.ts +18 -0
  646. package/dist/ui/hooks/useTypeahead.js +111 -0
  647. package/dist/ui/hooks/useTypeahead.js.map +1 -0
  648. package/dist/ui/markdown.d.ts +5 -0
  649. package/dist/ui/markdown.js +38 -0
  650. package/dist/ui/markdown.js.map +1 -0
  651. package/dist/ui/mcp-modal-types.d.ts +24 -0
  652. package/dist/ui/mcp-modal-types.js +13 -0
  653. package/dist/ui/mcp-modal-types.js.map +1 -0
  654. package/dist/ui/mcp-modal.d.ts +33 -0
  655. package/dist/ui/mcp-modal.js +94 -0
  656. package/dist/ui/mcp-modal.js.map +1 -0
  657. package/dist/ui/plan.d.ts +24 -0
  658. package/dist/ui/plan.js +122 -0
  659. package/dist/ui/plan.js.map +1 -0
  660. package/dist/ui/schedule-modal.d.ts +15 -0
  661. package/dist/ui/schedule-modal.js +36 -0
  662. package/dist/ui/schedule-modal.js.map +1 -0
  663. package/dist/ui/slash/__tests__/clear.test.d.ts +1 -0
  664. package/dist/ui/slash/__tests__/clear.test.js +58 -0
  665. package/dist/ui/slash/__tests__/clear.test.js.map +1 -0
  666. package/dist/ui/slash/__tests__/compact.test.d.ts +1 -0
  667. package/dist/ui/slash/__tests__/compact.test.js +43 -0
  668. package/dist/ui/slash/__tests__/compact.test.js.map +1 -0
  669. package/dist/ui/slash/__tests__/cost.test.d.ts +1 -0
  670. package/dist/ui/slash/__tests__/cost.test.js +54 -0
  671. package/dist/ui/slash/__tests__/cost.test.js.map +1 -0
  672. package/dist/ui/slash/__tests__/discuss.test.d.ts +1 -0
  673. package/dist/ui/slash/__tests__/discuss.test.js +83 -0
  674. package/dist/ui/slash/__tests__/discuss.test.js.map +1 -0
  675. package/dist/ui/slash/__tests__/execute.test.d.ts +1 -0
  676. package/dist/ui/slash/__tests__/execute.test.js +71 -0
  677. package/dist/ui/slash/__tests__/execute.test.js.map +1 -0
  678. package/dist/ui/slash/__tests__/expand.test.d.ts +1 -0
  679. package/dist/ui/slash/__tests__/expand.test.js +67 -0
  680. package/dist/ui/slash/__tests__/expand.test.js.map +1 -0
  681. package/dist/ui/slash/__tests__/optimize.test.d.ts +1 -0
  682. package/dist/ui/slash/__tests__/optimize.test.js +130 -0
  683. package/dist/ui/slash/__tests__/optimize.test.js.map +1 -0
  684. package/dist/ui/slash/__tests__/plan.test.d.ts +1 -0
  685. package/dist/ui/slash/__tests__/plan.test.js +79 -0
  686. package/dist/ui/slash/__tests__/plan.test.js.map +1 -0
  687. package/dist/ui/slash/clear.d.ts +11 -0
  688. package/dist/ui/slash/clear.js +77 -0
  689. package/dist/ui/slash/clear.js.map +1 -0
  690. package/dist/ui/slash/compact.d.ts +11 -0
  691. package/dist/ui/slash/compact.js +40 -0
  692. package/dist/ui/slash/compact.js.map +1 -0
  693. package/dist/ui/slash/cost.d.ts +11 -0
  694. package/dist/ui/slash/cost.js +55 -0
  695. package/dist/ui/slash/cost.js.map +1 -0
  696. package/dist/ui/slash/council.d.ts +2 -0
  697. package/dist/ui/slash/council.js +22 -0
  698. package/dist/ui/slash/council.js.map +1 -0
  699. package/dist/ui/slash/debug.d.ts +45 -0
  700. package/dist/ui/slash/debug.js +111 -0
  701. package/dist/ui/slash/debug.js.map +1 -0
  702. package/dist/ui/slash/discuss.d.ts +11 -0
  703. package/dist/ui/slash/discuss.js +59 -0
  704. package/dist/ui/slash/discuss.js.map +1 -0
  705. package/dist/ui/slash/ee.d.ts +10 -0
  706. package/dist/ui/slash/ee.js +247 -0
  707. package/dist/ui/slash/ee.js.map +1 -0
  708. package/dist/ui/slash/execute.d.ts +12 -0
  709. package/dist/ui/slash/execute.js +36 -0
  710. package/dist/ui/slash/execute.js.map +1 -0
  711. package/dist/ui/slash/expand.d.ts +11 -0
  712. package/dist/ui/slash/expand.js +42 -0
  713. package/dist/ui/slash/expand.js.map +1 -0
  714. package/dist/ui/slash/optimize.d.ts +12 -0
  715. package/dist/ui/slash/optimize.js +37 -0
  716. package/dist/ui/slash/optimize.js.map +1 -0
  717. package/dist/ui/slash/plan.d.ts +11 -0
  718. package/dist/ui/slash/plan.js +48 -0
  719. package/dist/ui/slash/plan.js.map +1 -0
  720. package/dist/ui/slash/registry.d.ts +23 -0
  721. package/dist/ui/slash/registry.js +26 -0
  722. package/dist/ui/slash/registry.js.map +1 -0
  723. package/dist/ui/slash/route.d.ts +12 -0
  724. package/dist/ui/slash/route.js +35 -0
  725. package/dist/ui/slash/route.js.map +1 -0
  726. package/dist/ui/slash/route.test.d.ts +1 -0
  727. package/dist/ui/slash/route.test.js +70 -0
  728. package/dist/ui/slash/route.test.js.map +1 -0
  729. package/dist/ui/status-bar/index.d.ts +14 -0
  730. package/dist/ui/status-bar/index.js +74 -0
  731. package/dist/ui/status-bar/index.js.map +1 -0
  732. package/dist/ui/status-bar/index.test.d.ts +1 -0
  733. package/dist/ui/status-bar/index.test.js +80 -0
  734. package/dist/ui/status-bar/index.test.js.map +1 -0
  735. package/dist/ui/status-bar/store.d.ts +38 -0
  736. package/dist/ui/status-bar/store.js +144 -0
  737. package/dist/ui/status-bar/store.js.map +1 -0
  738. package/dist/ui/status-bar/store.test.d.ts +1 -0
  739. package/dist/ui/status-bar/store.test.js +116 -0
  740. package/dist/ui/status-bar/store.test.js.map +1 -0
  741. package/dist/ui/status-bar/tier-badge.d.ts +11 -0
  742. package/dist/ui/status-bar/tier-badge.js +19 -0
  743. package/dist/ui/status-bar/tier-badge.js.map +1 -0
  744. package/dist/ui/status-bar/tier-badge.test.d.ts +1 -0
  745. package/dist/ui/status-bar/tier-badge.test.js +34 -0
  746. package/dist/ui/status-bar/tier-badge.test.js.map +1 -0
  747. package/dist/ui/status-bar/usd-meter.d.ts +13 -0
  748. package/dist/ui/status-bar/usd-meter.js +13 -0
  749. package/dist/ui/status-bar/usd-meter.js.map +1 -0
  750. package/dist/ui/status-bar/usd-meter.test.d.ts +1 -0
  751. package/dist/ui/status-bar/usd-meter.test.js +32 -0
  752. package/dist/ui/status-bar/usd-meter.test.js.map +1 -0
  753. package/dist/ui/terminal-selection-text.d.ts +23 -0
  754. package/dist/ui/terminal-selection-text.js +59 -0
  755. package/dist/ui/terminal-selection-text.js.map +1 -0
  756. package/dist/ui/theme.d.ts +53 -0
  757. package/dist/ui/theme.js +53 -0
  758. package/dist/ui/theme.js.map +1 -0
  759. package/dist/usage/downgrade.d.ts +37 -0
  760. package/dist/usage/downgrade.js +49 -0
  761. package/dist/usage/downgrade.js.map +1 -0
  762. package/dist/usage/downgrade.test.d.ts +1 -0
  763. package/dist/usage/downgrade.test.js +67 -0
  764. package/dist/usage/downgrade.test.js.map +1 -0
  765. package/dist/usage/estimator.d.ts +17 -0
  766. package/dist/usage/estimator.js +28 -0
  767. package/dist/usage/estimator.js.map +1 -0
  768. package/dist/usage/estimator.test.d.ts +1 -0
  769. package/dist/usage/estimator.test.js +38 -0
  770. package/dist/usage/estimator.test.js.map +1 -0
  771. package/dist/usage/ledger.d.ts +42 -0
  772. package/dist/usage/ledger.js +181 -0
  773. package/dist/usage/ledger.js.map +1 -0
  774. package/dist/usage/ledger.test.d.ts +1 -0
  775. package/dist/usage/ledger.test.js +171 -0
  776. package/dist/usage/ledger.test.js.map +1 -0
  777. package/dist/usage/midstream.d.ts +24 -0
  778. package/dist/usage/midstream.js +45 -0
  779. package/dist/usage/midstream.js.map +1 -0
  780. package/dist/usage/midstream.test.d.ts +1 -0
  781. package/dist/usage/midstream.test.js +47 -0
  782. package/dist/usage/midstream.test.js.map +1 -0
  783. package/dist/usage/thresholds.d.ts +38 -0
  784. package/dist/usage/thresholds.js +54 -0
  785. package/dist/usage/thresholds.js.map +1 -0
  786. package/dist/usage/thresholds.test.d.ts +1 -0
  787. package/dist/usage/thresholds.test.js +77 -0
  788. package/dist/usage/thresholds.test.js.map +1 -0
  789. package/dist/usage/types.d.ts +30 -0
  790. package/dist/usage/types.js +21 -0
  791. package/dist/usage/types.js.map +1 -0
  792. package/dist/utils/at-mentions.d.ts +12 -0
  793. package/dist/utils/at-mentions.js +92 -0
  794. package/dist/utils/at-mentions.js.map +1 -0
  795. package/dist/utils/clipboard-image.d.ts +13 -0
  796. package/dist/utils/clipboard-image.js +121 -0
  797. package/dist/utils/clipboard-image.js.map +1 -0
  798. package/dist/utils/file-index.d.ts +13 -0
  799. package/dist/utils/file-index.js +143 -0
  800. package/dist/utils/file-index.js.map +1 -0
  801. package/dist/utils/git-root.d.ts +1 -0
  802. package/dist/utils/git-root.js +16 -0
  803. package/dist/utils/git-root.js.map +1 -0
  804. package/dist/utils/host-clipboard.d.ts +4 -0
  805. package/dist/utils/host-clipboard.js +32 -0
  806. package/dist/utils/host-clipboard.js.map +1 -0
  807. package/dist/utils/install-manager.d.ts +56 -0
  808. package/dist/utils/install-manager.js +340 -0
  809. package/dist/utils/install-manager.js.map +1 -0
  810. package/dist/utils/install-manager.test.d.ts +1 -0
  811. package/dist/utils/install-manager.test.js +127 -0
  812. package/dist/utils/install-manager.test.js.map +1 -0
  813. package/dist/utils/instructions.d.ts +3 -0
  814. package/dist/utils/instructions.js +107 -0
  815. package/dist/utils/instructions.js.map +1 -0
  816. package/dist/utils/instructions.test.d.ts +1 -0
  817. package/dist/utils/instructions.test.js +68 -0
  818. package/dist/utils/instructions.test.js.map +1 -0
  819. package/dist/utils/permission-mode.d.ts +24 -0
  820. package/dist/utils/permission-mode.js +29 -0
  821. package/dist/utils/permission-mode.js.map +1 -0
  822. package/dist/utils/permission-mode.test.d.ts +1 -0
  823. package/dist/utils/permission-mode.test.js +97 -0
  824. package/dist/utils/permission-mode.test.js.map +1 -0
  825. package/dist/utils/redactor.d.ts +57 -0
  826. package/dist/utils/redactor.js +179 -0
  827. package/dist/utils/redactor.js.map +1 -0
  828. package/dist/utils/redactor.test.d.ts +1 -0
  829. package/dist/utils/redactor.test.js +84 -0
  830. package/dist/utils/redactor.test.js.map +1 -0
  831. package/dist/utils/settings.d.ts +179 -0
  832. package/dist/utils/settings.js +579 -0
  833. package/dist/utils/settings.js.map +1 -0
  834. package/dist/utils/settings.test.d.ts +1 -0
  835. package/dist/utils/settings.test.js +160 -0
  836. package/dist/utils/settings.test.js.map +1 -0
  837. package/dist/utils/side-question.d.ts +10 -0
  838. package/dist/utils/side-question.js +24 -0
  839. package/dist/utils/side-question.js.map +1 -0
  840. package/dist/utils/skills.d.ts +20 -0
  841. package/dist/utils/skills.js +194 -0
  842. package/dist/utils/skills.js.map +1 -0
  843. package/dist/utils/skills.test.d.ts +1 -0
  844. package/dist/utils/skills.test.js +45 -0
  845. package/dist/utils/skills.test.js.map +1 -0
  846. package/dist/utils/subagent-display.d.ts +1 -0
  847. package/dist/utils/subagent-display.js +20 -0
  848. package/dist/utils/subagent-display.js.map +1 -0
  849. package/dist/utils/subagent-display.test.d.ts +1 -0
  850. package/dist/utils/subagent-display.test.js +21 -0
  851. package/dist/utils/subagent-display.test.js.map +1 -0
  852. package/dist/utils/subagents-settings.test.d.ts +1 -0
  853. package/dist/utils/subagents-settings.test.js +58 -0
  854. package/dist/utils/subagents-settings.test.js.map +1 -0
  855. package/dist/utils/telegram-audio-settings.test.d.ts +1 -0
  856. package/dist/utils/telegram-audio-settings.test.js +39 -0
  857. package/dist/utils/telegram-audio-settings.test.js.map +1 -0
  858. package/dist/utils/update-checker.d.ts +11 -0
  859. package/dist/utils/update-checker.js +22 -0
  860. package/dist/utils/update-checker.js.map +1 -0
  861. package/dist/utils/update-checker.test.d.ts +1 -0
  862. package/dist/utils/update-checker.test.js +125 -0
  863. package/dist/utils/update-checker.test.js.map +1 -0
  864. package/dist/verify/checkpoint.d.ts +11 -0
  865. package/dist/verify/checkpoint.js +202 -0
  866. package/dist/verify/checkpoint.js.map +1 -0
  867. package/dist/verify/checkpoint.test.d.ts +1 -0
  868. package/dist/verify/checkpoint.test.js +160 -0
  869. package/dist/verify/checkpoint.test.js.map +1 -0
  870. package/dist/verify/entrypoint.d.ts +30 -0
  871. package/dist/verify/entrypoint.js +349 -0
  872. package/dist/verify/entrypoint.js.map +1 -0
  873. package/dist/verify/entrypoint.test.d.ts +1 -0
  874. package/dist/verify/entrypoint.test.js +233 -0
  875. package/dist/verify/entrypoint.test.js.map +1 -0
  876. package/dist/verify/environment.d.ts +9 -0
  877. package/dist/verify/environment.js +116 -0
  878. package/dist/verify/environment.js.map +1 -0
  879. package/dist/verify/environment.test.d.ts +1 -0
  880. package/dist/verify/environment.test.js +94 -0
  881. package/dist/verify/environment.test.js.map +1 -0
  882. package/dist/verify/evidence.d.ts +10 -0
  883. package/dist/verify/evidence.js +94 -0
  884. package/dist/verify/evidence.js.map +1 -0
  885. package/dist/verify/orchestrator.d.ts +25 -0
  886. package/dist/verify/orchestrator.js +87 -0
  887. package/dist/verify/orchestrator.js.map +1 -0
  888. package/dist/verify/orchestrator.test.d.ts +1 -0
  889. package/dist/verify/orchestrator.test.js +126 -0
  890. package/dist/verify/orchestrator.test.js.map +1 -0
  891. package/dist/verify/recipes.d.ts +20 -0
  892. package/dist/verify/recipes.js +451 -0
  893. package/dist/verify/recipes.js.map +1 -0
  894. package/dist/verify/retry.d.ts +4 -0
  895. package/dist/verify/retry.js +50 -0
  896. package/dist/verify/retry.js.map +1 -0
  897. package/dist/verify/runtime-prep.test.d.ts +1 -0
  898. package/dist/verify/runtime-prep.test.js +38 -0
  899. package/dist/verify/runtime-prep.test.js.map +1 -0
  900. package/package.json +93 -0
  901. package/src/__test-stubs__/ee-server.ts +173 -0
  902. package/src/billing/index.ts +5 -0
  903. package/src/bun-sqlite.d.ts +15 -0
  904. package/src/cloud/index.ts +5 -0
  905. package/src/daemon/scheduler.test.ts +128 -0
  906. package/src/daemon/scheduler.ts +152 -0
  907. package/src/ee/.gitkeep +0 -0
  908. package/src/ee/__tests__/pipeline.integration.test.ts +193 -0
  909. package/src/ee/auth.test.ts +76 -0
  910. package/src/ee/auth.ts +59 -0
  911. package/src/ee/bridge.test.ts +290 -0
  912. package/src/ee/bridge.ts +254 -0
  913. package/src/ee/client.test.ts +171 -0
  914. package/src/ee/client.ts +512 -0
  915. package/src/ee/embedding-cache.ts +42 -0
  916. package/src/ee/extract-session.test.ts +236 -0
  917. package/src/ee/extract-session.ts +69 -0
  918. package/src/ee/health.ts +83 -0
  919. package/src/ee/index.ts +32 -0
  920. package/src/ee/intercept.test.ts +191 -0
  921. package/src/ee/intercept.ts +132 -0
  922. package/src/ee/judge.test.ts +157 -0
  923. package/src/ee/judge.ts +68 -0
  924. package/src/ee/offline-queue.test.ts +351 -0
  925. package/src/ee/offline-queue.ts +212 -0
  926. package/src/ee/posttool.test.ts +81 -0
  927. package/src/ee/posttool.ts +16 -0
  928. package/src/ee/prompt-stale.test.ts +87 -0
  929. package/src/ee/prompt-stale.ts +39 -0
  930. package/src/ee/render.test.ts +71 -0
  931. package/src/ee/render.ts +47 -0
  932. package/src/ee/scope.test.ts +112 -0
  933. package/src/ee/scope.ts +93 -0
  934. package/src/ee/tenant.ts +14 -0
  935. package/src/ee/touch.test.ts +71 -0
  936. package/src/ee/types.ts +322 -0
  937. package/src/flow/.gitkeep +0 -0
  938. package/src/flow/__tests__/migration.test.ts +133 -0
  939. package/src/flow/__tests__/parser.test.ts +77 -0
  940. package/src/flow/__tests__/run-manager.test.ts +95 -0
  941. package/src/flow/__tests__/scaffold.test.ts +57 -0
  942. package/src/flow/__tests__/warning-persist.test.ts +112 -0
  943. package/src/flow/artifact-io.ts +41 -0
  944. package/src/flow/compaction/__tests__/compress.test.ts +69 -0
  945. package/src/flow/compaction/__tests__/extract.test.ts +74 -0
  946. package/src/flow/compaction/__tests__/preserve.test.ts +69 -0
  947. package/src/flow/compaction/compress.ts +67 -0
  948. package/src/flow/compaction/extract.ts +60 -0
  949. package/src/flow/compaction/index.ts +86 -0
  950. package/src/flow/compaction/preserve.ts +48 -0
  951. package/src/flow/index.ts +18 -0
  952. package/src/flow/migration.ts +139 -0
  953. package/src/flow/parser.ts +78 -0
  954. package/src/flow/run-manager.ts +129 -0
  955. package/src/flow/scaffold.ts +52 -0
  956. package/src/flow/warning-persist.ts +84 -0
  957. package/src/gsd/.gitkeep +0 -0
  958. package/src/gsd/__tests__/types.test.ts +77 -0
  959. package/src/gsd/index.ts +1 -0
  960. package/src/gsd/types.ts +47 -0
  961. package/src/headless/output.test.ts +201 -0
  962. package/src/headless/output.ts +312 -0
  963. package/src/hooks/config.ts +41 -0
  964. package/src/hooks/index.ts +250 -0
  965. package/src/hooks/types.ts +221 -0
  966. package/src/index.ts +655 -0
  967. package/src/lsp/builtins.test.ts +104 -0
  968. package/src/lsp/builtins.ts +409 -0
  969. package/src/lsp/client.ts +342 -0
  970. package/src/lsp/manager.test.ts +164 -0
  971. package/src/lsp/manager.ts +293 -0
  972. package/src/lsp/npm-cache.test.ts +68 -0
  973. package/src/lsp/npm-cache.ts +108 -0
  974. package/src/lsp/runtime.ts +70 -0
  975. package/src/lsp/smoke.test.ts +72 -0
  976. package/src/lsp/types.ts +116 -0
  977. package/src/mcp/auto-setup.ts +80 -0
  978. package/src/mcp/catalog.ts +138 -0
  979. package/src/mcp/oauth-callback.ts +63 -0
  980. package/src/mcp/oauth-provider.ts +130 -0
  981. package/src/mcp/parse-headers.test.ts +54 -0
  982. package/src/mcp/parse-headers.ts +35 -0
  983. package/src/mcp/runtime.ts +113 -0
  984. package/src/mcp/smoke.test.ts +170 -0
  985. package/src/mcp/validate.ts +48 -0
  986. package/src/models/__tests__/registry.test.ts +95 -0
  987. package/src/models/catalog-client.ts +83 -0
  988. package/src/models/catalog.json +314 -0
  989. package/src/models/classify-tier.ts +37 -0
  990. package/src/models/index.ts +9 -0
  991. package/src/models/registry.ts +73 -0
  992. package/src/ops/bug-report.test.ts +172 -0
  993. package/src/ops/bug-report.ts +80 -0
  994. package/src/ops/doctor.test.ts +107 -0
  995. package/src/ops/doctor.ts +172 -0
  996. package/src/orchestrator/__tests__/flow-resume.test.ts +71 -0
  997. package/src/orchestrator/__tests__/route-feedback.test.ts +55 -0
  998. package/src/orchestrator/abort.test.ts +37 -0
  999. package/src/orchestrator/abort.ts +51 -0
  1000. package/src/orchestrator/agent.test.ts +138 -0
  1001. package/src/orchestrator/cleanup.test.ts +88 -0
  1002. package/src/orchestrator/compaction.test.ts +134 -0
  1003. package/src/orchestrator/compaction.ts +487 -0
  1004. package/src/orchestrator/delegations.test.ts +145 -0
  1005. package/src/orchestrator/delegations.ts +364 -0
  1006. package/src/orchestrator/flow-resume.ts +55 -0
  1007. package/src/orchestrator/orchestrator.ts +3965 -0
  1008. package/src/orchestrator/pending-calls.test.ts +226 -0
  1009. package/src/orchestrator/pending-calls.ts +240 -0
  1010. package/src/orchestrator/reasoning.test.ts +29 -0
  1011. package/src/orchestrator/reasoning.ts +64 -0
  1012. package/src/orchestrator/sandbox.test.ts +115 -0
  1013. package/src/pil/__tests__/budget.test.ts +39 -0
  1014. package/src/pil/__tests__/layer1-intent.test.ts +243 -0
  1015. package/src/pil/__tests__/layer2-personality.test.ts +63 -0
  1016. package/src/pil/__tests__/layer3-ee-injection.test.ts +117 -0
  1017. package/src/pil/__tests__/layer4-gsd.test.ts +76 -0
  1018. package/src/pil/__tests__/layer5-context.test.ts +116 -0
  1019. package/src/pil/__tests__/layer6-output.test.ts +139 -0
  1020. package/src/pil/__tests__/ollama-classify.test.ts +81 -0
  1021. package/src/pil/__tests__/orchestrator-integration.test.ts +107 -0
  1022. package/src/pil/__tests__/pipeline.test.ts +173 -0
  1023. package/src/pil/__tests__/response-tools.test.ts +165 -0
  1024. package/src/pil/__tests__/schema.test.ts +128 -0
  1025. package/src/pil/__tests__/store.test.ts +49 -0
  1026. package/src/pil/__tests__/task-tier-map.test.ts +41 -0
  1027. package/src/pil/budget.ts +18 -0
  1028. package/src/pil/index.ts +12 -0
  1029. package/src/pil/layer1-intent.ts +155 -0
  1030. package/src/pil/layer2-personality.ts +37 -0
  1031. package/src/pil/layer3-ee-injection.ts +79 -0
  1032. package/src/pil/layer4-gsd.ts +77 -0
  1033. package/src/pil/layer5-context.ts +125 -0
  1034. package/src/pil/layer6-output.ts +113 -0
  1035. package/src/pil/ollama-classify.ts +49 -0
  1036. package/src/pil/pipeline.ts +112 -0
  1037. package/src/pil/response-tools.ts +110 -0
  1038. package/src/pil/schema.ts +49 -0
  1039. package/src/pil/store.ts +29 -0
  1040. package/src/pil/task-tier-map.ts +90 -0
  1041. package/src/pil/timeout.ts +10 -0
  1042. package/src/pil/types.ts +42 -0
  1043. package/src/providers/.gitkeep +0 -0
  1044. package/src/providers/__test-utils__/load-fixture.ts +36 -0
  1045. package/src/providers/__tests__/runtime-integration.test.ts +80 -0
  1046. package/src/providers/__tests__/runtime.test.ts +86 -0
  1047. package/src/providers/adapter.test.ts +21 -0
  1048. package/src/providers/adapter.ts +48 -0
  1049. package/src/providers/anthropic.ts +169 -0
  1050. package/src/providers/errors.test.ts +76 -0
  1051. package/src/providers/errors.ts +46 -0
  1052. package/src/providers/gemini.test.ts +46 -0
  1053. package/src/providers/gemini.ts +37 -0
  1054. package/src/providers/index.ts +32 -0
  1055. package/src/providers/keychain.test.ts +95 -0
  1056. package/src/providers/keychain.ts +133 -0
  1057. package/src/providers/ollama.test.ts +46 -0
  1058. package/src/providers/ollama.ts +36 -0
  1059. package/src/providers/openai-compatible.test.ts +63 -0
  1060. package/src/providers/openai-compatible.ts +50 -0
  1061. package/src/providers/openai.test.ts +65 -0
  1062. package/src/providers/openai.ts +37 -0
  1063. package/src/providers/patch-zod-schema.ts +129 -0
  1064. package/src/providers/pricing.test.ts +56 -0
  1065. package/src/providers/pricing.ts +55 -0
  1066. package/src/providers/runtime.ts +111 -0
  1067. package/src/providers/stream-loop.ts +69 -0
  1068. package/src/providers/types.ts +111 -0
  1069. package/src/providers/vision-proxy.test.ts +167 -0
  1070. package/src/providers/vision-proxy.ts +209 -0
  1071. package/src/router/.gitkeep +0 -0
  1072. package/src/router/classifier/grammars.ts +17 -0
  1073. package/src/router/classifier/index.test.ts +33 -0
  1074. package/src/router/classifier/index.ts +21 -0
  1075. package/src/router/classifier/regex.test.ts +47 -0
  1076. package/src/router/classifier/regex.ts +54 -0
  1077. package/src/router/classifier/tree-sitter.test.ts +26 -0
  1078. package/src/router/classifier/tree-sitter.ts +87 -0
  1079. package/src/router/cold.test.ts +62 -0
  1080. package/src/router/cold.ts +27 -0
  1081. package/src/router/decide.test.ts +135 -0
  1082. package/src/router/decide.ts +354 -0
  1083. package/src/router/health.test.ts +59 -0
  1084. package/src/router/health.ts +49 -0
  1085. package/src/router/store.ts +47 -0
  1086. package/src/router/types.ts +20 -0
  1087. package/src/router/warm.test.ts +167 -0
  1088. package/src/router/warm.ts +47 -0
  1089. package/src/storage/__tests__/migrations.test.ts +395 -0
  1090. package/src/storage/atomic-io.test.ts +61 -0
  1091. package/src/storage/atomic-io.ts +62 -0
  1092. package/src/storage/config.test.ts +33 -0
  1093. package/src/storage/config.ts +49 -0
  1094. package/src/storage/db.ts +95 -0
  1095. package/src/storage/index.ts +19 -0
  1096. package/src/storage/migrations.ts +143 -0
  1097. package/src/storage/session-dir.ts +37 -0
  1098. package/src/storage/sessions.ts +178 -0
  1099. package/src/storage/tool-results.ts +56 -0
  1100. package/src/storage/transcript-view.ts +45 -0
  1101. package/src/storage/transcript.test.ts +24 -0
  1102. package/src/storage/transcript.ts +358 -0
  1103. package/src/storage/usage-cap.test.ts +59 -0
  1104. package/src/storage/usage-cap.ts +81 -0
  1105. package/src/storage/usage.ts +115 -0
  1106. package/src/storage/workspaces.ts +84 -0
  1107. package/src/tools/bash.test.ts +336 -0
  1108. package/src/tools/bash.ts +612 -0
  1109. package/src/tools/computer.test.ts +187 -0
  1110. package/src/tools/computer.ts +632 -0
  1111. package/src/tools/file.test.ts +95 -0
  1112. package/src/tools/file.ts +128 -0
  1113. package/src/tools/grep.ts +187 -0
  1114. package/src/tools/registry.ts +282 -0
  1115. package/src/tools/schedule.test.ts +143 -0
  1116. package/src/tools/schedule.ts +610 -0
  1117. package/src/types/index.ts +311 -0
  1118. package/src/ui/agents-modal.tsx +272 -0
  1119. package/src/ui/app.tsx +6277 -0
  1120. package/src/ui/components/SuggestionOverlay.tsx +38 -0
  1121. package/src/ui/components/btw-overlay.tsx +66 -0
  1122. package/src/ui/hooks/useTypeahead.ts +146 -0
  1123. package/src/ui/markdown.tsx +49 -0
  1124. package/src/ui/mcp-modal-types.ts +33 -0
  1125. package/src/ui/mcp-modal.tsx +456 -0
  1126. package/src/ui/plan.tsx +346 -0
  1127. package/src/ui/schedule-modal.tsx +126 -0
  1128. package/src/ui/slash/__tests__/clear.test.ts +86 -0
  1129. package/src/ui/slash/__tests__/compact.test.ts +56 -0
  1130. package/src/ui/slash/__tests__/cost.test.ts +62 -0
  1131. package/src/ui/slash/__tests__/discuss.test.ts +101 -0
  1132. package/src/ui/slash/__tests__/execute.test.ts +86 -0
  1133. package/src/ui/slash/__tests__/expand.test.ts +86 -0
  1134. package/src/ui/slash/__tests__/optimize.test.ts +155 -0
  1135. package/src/ui/slash/__tests__/plan.test.ts +95 -0
  1136. package/src/ui/slash/clear.ts +89 -0
  1137. package/src/ui/slash/compact.ts +46 -0
  1138. package/src/ui/slash/cost.ts +59 -0
  1139. package/src/ui/slash/council.ts +27 -0
  1140. package/src/ui/slash/debug.ts +153 -0
  1141. package/src/ui/slash/discuss.ts +71 -0
  1142. package/src/ui/slash/ee.ts +271 -0
  1143. package/src/ui/slash/execute.ts +44 -0
  1144. package/src/ui/slash/expand.ts +51 -0
  1145. package/src/ui/slash/optimize.ts +47 -0
  1146. package/src/ui/slash/plan.ts +62 -0
  1147. package/src/ui/slash/registry.ts +38 -0
  1148. package/src/ui/slash/route.test.ts +82 -0
  1149. package/src/ui/slash/route.ts +43 -0
  1150. package/src/ui/status-bar/index.test.tsx +90 -0
  1151. package/src/ui/status-bar/index.tsx +94 -0
  1152. package/src/ui/status-bar/store.test.ts +131 -0
  1153. package/src/ui/status-bar/store.ts +166 -0
  1154. package/src/ui/status-bar/tier-badge.test.tsx +38 -0
  1155. package/src/ui/status-bar/tier-badge.tsx +29 -0
  1156. package/src/ui/status-bar/usd-meter.test.tsx +37 -0
  1157. package/src/ui/status-bar/usd-meter.tsx +22 -0
  1158. package/src/ui/terminal-selection-text.ts +72 -0
  1159. package/src/ui/theme.ts +54 -0
  1160. package/src/usage/.gitkeep +0 -0
  1161. package/src/usage/downgrade.test.ts +82 -0
  1162. package/src/usage/downgrade.ts +70 -0
  1163. package/src/usage/estimator.test.ts +43 -0
  1164. package/src/usage/estimator.ts +34 -0
  1165. package/src/usage/ledger.test.ts +200 -0
  1166. package/src/usage/ledger.ts +213 -0
  1167. package/src/usage/midstream.test.ts +55 -0
  1168. package/src/usage/midstream.ts +51 -0
  1169. package/src/usage/thresholds.test.ts +83 -0
  1170. package/src/usage/thresholds.ts +74 -0
  1171. package/src/usage/types.ts +40 -0
  1172. package/src/utils/at-mentions.ts +120 -0
  1173. package/src/utils/clipboard-image.ts +114 -0
  1174. package/src/utils/file-index.ts +152 -0
  1175. package/src/utils/git-root.ts +17 -0
  1176. package/src/utils/host-clipboard.ts +30 -0
  1177. package/src/utils/install-manager.test.ts +167 -0
  1178. package/src/utils/install-manager.ts +429 -0
  1179. package/src/utils/instructions.test.ts +84 -0
  1180. package/src/utils/instructions.ts +116 -0
  1181. package/src/utils/permission-mode.test.ts +121 -0
  1182. package/src/utils/permission-mode.ts +37 -0
  1183. package/src/utils/redactor.test.ts +100 -0
  1184. package/src/utils/redactor.ts +223 -0
  1185. package/src/utils/settings.test.ts +181 -0
  1186. package/src/utils/settings.ts +787 -0
  1187. package/src/utils/side-question.ts +39 -0
  1188. package/src/utils/skills.test.ts +58 -0
  1189. package/src/utils/skills.ts +207 -0
  1190. package/src/utils/subagent-display.test.ts +23 -0
  1191. package/src/utils/subagent-display.ts +11 -0
  1192. package/src/utils/subagents-settings.test.ts +77 -0
  1193. package/src/utils/telegram-audio-settings.test.ts +44 -0
  1194. package/src/utils/update-checker.test.ts +182 -0
  1195. package/src/utils/update-checker.ts +33 -0
  1196. package/src/verify/checkpoint.test.ts +186 -0
  1197. package/src/verify/checkpoint.ts +239 -0
  1198. package/src/verify/entrypoint.test.ts +293 -0
  1199. package/src/verify/entrypoint.ts +439 -0
  1200. package/src/verify/environment.test.ts +119 -0
  1201. package/src/verify/environment.ts +115 -0
  1202. package/src/verify/evidence.ts +104 -0
  1203. package/src/verify/orchestrator.test.ts +159 -0
  1204. package/src/verify/orchestrator.ts +129 -0
  1205. package/src/verify/recipes.ts +516 -0
  1206. package/src/verify/retry.ts +62 -0
  1207. package/src/verify/runtime-prep.test.ts +47 -0
package/dist/ui/app.js ADDED
@@ -0,0 +1,3957 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentui/react/jsx-runtime";
2
+ import { decodePasteBytes, parseKeypress } from "@opentui/core";
3
+ import { useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/react";
4
+ import os from "os";
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
+ import { POPULAR_MCP_CATALOG } from "../mcp/catalog";
7
+ import { parseEnvLines, parseHeaderLines } from "../mcp/parse-headers";
8
+ import { toMcpServerId, validateMcpServerConfig } from "../mcp/validate";
9
+ import { Agent } from "../orchestrator/orchestrator";
10
+ import { deliberateCompact } from "../flow/compaction/index.js";
11
+ import * as path from "node:path";
12
+ import { MODES } from "../types/index";
13
+ import { processAtMentions } from "../utils/at-mentions.js";
14
+ import { FileIndex } from "../utils/file-index.js";
15
+ import { readClipboardImage } from "../utils/clipboard-image";
16
+ import { copyTextToHostClipboard } from "../utils/host-clipboard";
17
+ import { getApiKey, getTelegramBotToken, isReservedSubagentName, loadMcpServers, loadPaymentSettings, loadUserSettings, loadValidSubAgents, saveApprovedTelegramUserId, saveMcpServers, savePaymentSettings, saveProjectSettings, saveUserSettings, } from "../utils/settings";
18
+ import { discoverSkills, formatSkillsForChat } from "../utils/skills";
19
+ import { formatSubagentName } from "../utils/subagent-display";
20
+ import { checkForUpdate, runUpdate } from "../utils/update-checker";
21
+ import { buildVerifyPrompt } from "../verify/entrypoint";
22
+ import { buildSubagentBrowseRows, SUBAGENT_EDITOR_FIELDS, SubagentEditorModal, SubagentsBrowserModal, } from "./agents-modal";
23
+ import { BtwOverlay } from "./components/btw-overlay.js";
24
+ import { SuggestionOverlay } from "./components/SuggestionOverlay.js";
25
+ import { useTypeahead } from "./hooks/useTypeahead.js";
26
+ import { Markdown } from "./markdown";
27
+ import { buildMcpBrowseRows, McpBrowserModal, McpEditorModal } from "./mcp-modal";
28
+ import { createEmptyMcpEditorDraft } from "./mcp-modal-types";
29
+ import { formatPlanAnswers, initialPlanQuestionsState, PlanQuestionsPanel, PlanView, } from "./plan";
30
+ import { buildScheduleBrowseRows, ScheduleBrowserModal } from "./schedule-modal";
31
+ import { dispatchSlash } from "./slash/registry.js";
32
+ import { StatusBar } from "./status-bar/index.js";
33
+ import { statusBarStore, wireStatusBar } from "./status-bar/store.js";
34
+ import { getCompactTuiSelectionText } from "./terminal-selection-text";
35
+ import { dark } from "./theme";
36
+ import "./slash/route.js";
37
+ import "./slash/optimize.js";
38
+ import "./slash/discuss.js";
39
+ import "./slash/plan.js";
40
+ import "./slash/execute.js";
41
+ import "./slash/compact.js";
42
+ import "./slash/expand.js";
43
+ import "./slash/clear.js";
44
+ import "./slash/cost.js";
45
+ import "./slash/ee.js";
46
+ import "./slash/debug.js";
47
+ import "./slash/council.js";
48
+ import { getEffectiveReasoningEffort, getModelIds, getModelInfo, getSupportedReasoningEfforts, MODELS, isLoading, normalizeModelId, } from "../models/registry.js";
49
+ const DEFAULT_MODEL = "claude-sonnet-4-6";
50
+ function createTelegramBridge(_opts) {
51
+ return null;
52
+ }
53
+ function approvePairingCode(_code) {
54
+ return { ok: false, error: "Telegram bridge not available." };
55
+ }
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ function createTurnCoordinator() {
58
+ return {
59
+ reset: () => { },
60
+ handleEvent: () => { },
61
+ run: async (fn) => fn(),
62
+ };
63
+ }
64
+ function formatStructuredResponse(sr) {
65
+ const d = sr.data;
66
+ switch (sr.taskType) {
67
+ case "refactor": {
68
+ const r = d;
69
+ const parts = [r.summary ?? ""];
70
+ for (const c of r.changes ?? [])
71
+ parts.push(`\n── ${c.file} ──\n${c.diff}`);
72
+ if (r.verify_command)
73
+ parts.push(`\nverify: ${r.verify_command}`);
74
+ return parts.join("\n");
75
+ }
76
+ case "debug": {
77
+ const r = d;
78
+ const parts = [`hypothesis: ${r.hypothesis}`, `root cause: ${r.root_cause}`];
79
+ if (r.fix)
80
+ parts.push(`\n── fix: ${r.fix.file} ──\n${r.fix.diff}`);
81
+ if (r.verify_command)
82
+ parts.push(`verify: ${r.verify_command}`);
83
+ return parts.join("\n");
84
+ }
85
+ case "plan": {
86
+ const r = d;
87
+ const lines = (r.steps ?? []).map((s, i) => `${i + 1}. ${s.action}\n done when: ${s.criterion}${s.rationale ? `\n why: ${s.rationale}` : ""}`);
88
+ if (r.assumptions?.length)
89
+ lines.push(`\nassumptions:\n${r.assumptions.map((a) => ` - ${a}`).join("\n")}`);
90
+ if (r.risks?.length)
91
+ lines.push(`\nrisks:\n${r.risks.map((r2) => ` - ${r2}`).join("\n")}`);
92
+ return lines.join("\n");
93
+ }
94
+ case "analyze": {
95
+ const r = d;
96
+ return (r.findings ?? []).map((f) => `[${f.severity.toUpperCase()}] ${f.text}\n evidence: ${f.evidence}`).join("\n");
97
+ }
98
+ case "documentation": {
99
+ const r = d;
100
+ const parts = [r.content ?? ""];
101
+ for (const ex of r.examples ?? [])
102
+ parts.push(`\n${ex.description}\n${ex.code}`);
103
+ return parts.join("\n");
104
+ }
105
+ case "generate": {
106
+ const r = d;
107
+ const parts = [];
108
+ if (r.explanation)
109
+ parts.push(r.explanation);
110
+ for (const f of r.files ?? [])
111
+ parts.push(`\n── ${f.path} (${f.language}) ──\n${f.content}`);
112
+ return parts.join("\n");
113
+ }
114
+ default:
115
+ return JSON.stringify(d, null, 2);
116
+ }
117
+ }
118
+ function buildAssistantEntry(content, extra) {
119
+ return { type: "assistant", content, timestamp: new Date(), ...extra };
120
+ }
121
+ function buildToolResultEntry(toolCall, toolResult, extra) {
122
+ const output = toolResult.output ?? (toolResult.error ? `Error: ${toolResult.error}` : "");
123
+ return {
124
+ type: "tool_result",
125
+ content: typeof output === "string" ? output : String(output),
126
+ timestamp: new Date(),
127
+ toolCall,
128
+ toolResult,
129
+ ...extra,
130
+ };
131
+ }
132
+ function buildUserEntry(content, extra) {
133
+ return { type: "user", content, timestamp: new Date(), ...extra };
134
+ }
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ function decorateTelegramEntries(_entries, _userId, _remoteKey) {
137
+ return [];
138
+ }
139
+ function getTelegramSourceLabel(_role, _userId) {
140
+ return "";
141
+ }
142
+ function getUnflushedTelegramAssistantContent(_content, _flushedChars) {
143
+ return "";
144
+ }
145
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
+ function replaceTurnEntries(_prev, _remoteKey, _delta) {
147
+ return _prev;
148
+ }
149
+ const STAR_PALETTE = ["#777777", "#666666", "#4a4a4a", "#333333", "#222222"];
150
+ const LOADING_SPINNER_FRAMES = ["⬒", "⬔", "⬓", "⬕"];
151
+ const PROMPT_LOADING_FRAMES = [
152
+ { active: 0, forward: true },
153
+ { active: 1, forward: true },
154
+ { active: 2, forward: true },
155
+ { active: 1, forward: false },
156
+ ];
157
+ function getPasteBlockToken(block) {
158
+ if (block.isImage) {
159
+ return `[Image #${block.id}]`;
160
+ }
161
+ return `[Pasted #${block.id} ${block.lines}+ lines]`;
162
+ }
163
+ function getFileMentionToken(block) {
164
+ const name = block.path.split("/").pop() || block.path;
165
+ return `[File: ${name}]`;
166
+ }
167
+ const HERO_ROWS = [
168
+ {
169
+ stars: [
170
+ { col: 0, ch: "·" },
171
+ { col: 13, ch: "*" },
172
+ { col: 21, ch: "·" },
173
+ { col: 34, ch: "·" },
174
+ ],
175
+ },
176
+ {
177
+ stars: [
178
+ { col: 3, ch: "*" },
179
+ { col: 11, ch: "·" },
180
+ { col: 17, ch: "·" },
181
+ { col: 25, ch: "*" },
182
+ ],
183
+ },
184
+ {
185
+ stars: [
186
+ { col: 6, ch: "·" },
187
+ { col: 12, ch: "·" },
188
+ { col: 15, ch: "·" },
189
+ { col: 18, ch: "·" },
190
+ { col: 24, ch: "·" },
191
+ ],
192
+ },
193
+ {
194
+ stars: [
195
+ { col: 2, ch: "·" },
196
+ { col: 10, ch: "·" },
197
+ { col: 19, ch: "·" },
198
+ { col: 27, ch: "·" },
199
+ ],
200
+ brand: 13,
201
+ },
202
+ {
203
+ stars: [
204
+ { col: 6, ch: "·" },
205
+ { col: 12, ch: "·" },
206
+ { col: 15, ch: "·" },
207
+ { col: 18, ch: "·" },
208
+ { col: 24, ch: "·" },
209
+ ],
210
+ },
211
+ {
212
+ stars: [
213
+ { col: 3, ch: "·" },
214
+ { col: 11, ch: "*" },
215
+ { col: 17, ch: "·" },
216
+ { col: 25, ch: "·" },
217
+ ],
218
+ },
219
+ {
220
+ stars: [
221
+ { col: 0, ch: "*" },
222
+ { col: 13, ch: "·" },
223
+ { col: 21, ch: "*" },
224
+ { col: 34, ch: "·" },
225
+ ],
226
+ },
227
+ ];
228
+ function HeroLogo({ t }) {
229
+ const [tick, setTick] = useState(0);
230
+ const starIdx = useRef(0);
231
+ useEffect(() => {
232
+ const id = setInterval(() => setTick((n) => n + 1), 900);
233
+ return () => clearInterval(id);
234
+ }, []);
235
+ starIdx.current = 0;
236
+ const nextColor = () => {
237
+ const i = starIdx.current++;
238
+ return STAR_PALETTE[(i * 7 + tick * 3 + i * tick) % STAR_PALETTE.length];
239
+ };
240
+ return (_jsx("box", { flexDirection: "column", alignItems: "center", children: HERO_ROWS.map((row, r) => {
241
+ const els = [];
242
+ let cursor = 0;
243
+ for (const star of row.stars) {
244
+ if (row.brand !== undefined && cursor <= row.brand && star.col > row.brand) {
245
+ els.push(" ".repeat(row.brand - cursor));
246
+ els.push(_jsx("span", { style: { fg: t.primary }, children: "muonroi" }, "brand"));
247
+ cursor = row.brand + 7;
248
+ }
249
+ const gap = star.col - cursor;
250
+ if (gap > 0)
251
+ els.push(" ".repeat(gap));
252
+ els.push(_jsx("span", { style: { fg: nextColor() }, children: star.ch }, `s-${star.col}`));
253
+ cursor = star.col + 1;
254
+ }
255
+ if (row.brand !== undefined && cursor <= row.brand) {
256
+ els.push(" ".repeat(row.brand - cursor));
257
+ els.push(_jsx("span", { style: { fg: t.primary }, children: "muonroi" }, "brand"));
258
+ cursor = row.brand + 7;
259
+ }
260
+ els.push(" ".repeat(Math.max(0, 35 - cursor)));
261
+ // biome-ignore lint/suspicious/noArrayIndexKey: static constant array that never reorders
262
+ return _jsx("text", { children: els }, r);
263
+ }) }));
264
+ }
265
+ const SPLIT = {
266
+ topLeft: "",
267
+ bottomLeft: "",
268
+ vertical: "┃",
269
+ topRight: "",
270
+ bottomRight: "",
271
+ horizontal: " ",
272
+ bottomT: "",
273
+ topT: "",
274
+ cross: "",
275
+ leftT: "",
276
+ rightT: "",
277
+ };
278
+ const _SPLIT_END = { ...SPLIT, bottomLeft: "╹" };
279
+ const _EMPTY = {
280
+ topLeft: "",
281
+ bottomLeft: "",
282
+ vertical: "",
283
+ topRight: "",
284
+ bottomRight: "",
285
+ horizontal: " ",
286
+ bottomT: "",
287
+ topT: "",
288
+ cross: "",
289
+ leftT: "",
290
+ rightT: "",
291
+ };
292
+ const _LINE = {
293
+ topLeft: "━",
294
+ bottomLeft: "━",
295
+ vertical: "",
296
+ topRight: "━",
297
+ bottomRight: "━",
298
+ horizontal: "━",
299
+ bottomT: "━",
300
+ topT: "━",
301
+ cross: "━",
302
+ leftT: "━",
303
+ rightT: "━",
304
+ };
305
+ const SLASH_MENU_ITEMS = [
306
+ { id: "exit", label: "exit", description: "Quit the CLI" },
307
+ { id: "help", label: "help", description: "Show available commands" },
308
+ { id: "clear", label: "clear", description: "Clear conversation and start fresh" },
309
+ { id: "compact", label: "compact", description: "Compact conversation context" },
310
+ { id: "remote-control", label: "remote-control", description: "Remote control" },
311
+ { id: "agents", label: "agents", description: "Manage custom sub-agents" },
312
+ { id: "schedule", label: "schedule", description: "View scheduled runs" },
313
+ { id: "mcp", label: "mcp", description: "Manage MCP servers" },
314
+ { id: "sandbox", label: "sandbox", description: "Select shell sandbox mode" },
315
+ { id: "wallet", label: "wallet", description: "Wallet and payment settings" },
316
+ { id: "models", label: "models", description: "Select a model" },
317
+ { id: "new", label: "new session", description: "Start a new session" },
318
+ { id: "commit-push", label: "commit & push", description: "Commit and push" },
319
+ { id: "commit-pr", label: "commit & pr", description: "Commit and open PR" },
320
+ { id: "review", label: "review", description: "Review recent changes" },
321
+ { id: "verify", label: "verify", description: "Run local verification" },
322
+ { id: "skills", label: "skills", description: "Manage skills" },
323
+ { id: "btw", label: "btw", description: "Ask a side question without interrupting" },
324
+ { id: "update", label: "update", description: "Update muonroi-cli to the latest version" },
325
+ { id: "cost", label: "cost", description: "Show session cost breakdown" },
326
+ { id: "ee", label: "ee", description: "Experience Engine status and controls" },
327
+ { id: "route", label: "route", description: "Show current model routing info" },
328
+ { id: "plan", label: "plan", description: "Show active GSD plan" },
329
+ { id: "execute", label: "execute", description: "Execute active GSD plan" },
330
+ { id: "discuss", label: "discuss", description: "Discuss phase gray areas" },
331
+ { id: "expand", label: "expand", description: "Expand last compacted context" },
332
+ { id: "optimize", label: "optimize", description: "Optimize prompt for token savings" },
333
+ { id: "debug", label: "debug", description: "Toggle debug trace mode" },
334
+ { id: "council", label: "council", description: "Multi-model adversarial debate" },
335
+ ];
336
+ const REVIEW_PROMPT = `Review all current changes in this repository. Follow these steps:
337
+
338
+ 1. Run \`git status\` to see which files have been modified, staged, or are untracked.
339
+ 2. Run \`git diff\` to see unstaged changes and \`git diff --cached\` to see staged changes.
340
+ 3. If there are no changes at all, say so and stop.
341
+ 4. Read any changed files in full if needed for context.
342
+
343
+ Then produce a **Review Report** in this exact structure:
344
+
345
+ ## Summary
346
+ One paragraph overview of what changed and why (inferred from the diff).
347
+
348
+ ## Files Changed
349
+ For each changed file, list the filename and a brief description of the change.
350
+
351
+ ## Issues Found
352
+ List any bugs, logic errors, security concerns, missing error handling, or correctness problems. If none, say "No issues found."
353
+
354
+ ## Suggestions
355
+ Code quality, naming, performance, and best-practice improvements. If none, say "No suggestions."
356
+
357
+ ## Risk Assessment
358
+ Rate the overall risk of these changes as **Low**, **Medium**, or **High** with a short justification.`;
359
+ const COMMIT_PUSH_PROMPT = `Create a git commit for the current repository changes and push the current branch to its remote.
360
+
361
+ Before committing, inspect the current branch. If it is not already a feature branch, create and switch to a new feature branch with a descriptive name based on the changes.
362
+
363
+ Follow the repository's commit workflow and safety checks. Inspect the current changes, stage any relevant untracked files, create an appropriate commit message, and push the branch if a commit was created. If there is nothing to commit, say so and stop.`;
364
+ const COMMIT_PR_PROMPT = `Create a git commit for the current repository changes and open a pull request for the current branch.
365
+
366
+ Before committing, inspect the current branch. If it is not already a feature branch, create and switch to a new feature branch with a descriptive name based on the changes.
367
+
368
+ Follow the repository's commit and pull request workflows. Inspect the current changes, stage any relevant untracked files, create an appropriate commit, push the branch if needed, then open a pull request with a concise summary and test plan. Return the pull request URL. If there is nothing to commit or open in a pull request, explain why and stop.`;
369
+ const BUILTIN_TYPED_SLASH_COMMANDS = new Set([
370
+ "/clear",
371
+ "/model",
372
+ "/models",
373
+ "/sandbox",
374
+ "/remote-control",
375
+ "/mcp",
376
+ "/mcps",
377
+ "/agents",
378
+ "/agent",
379
+ "/schedule",
380
+ "/schedules",
381
+ "/quit",
382
+ "/exit",
383
+ "/q",
384
+ "/review",
385
+ "/verify",
386
+ "/commit-push",
387
+ "/commit-pr",
388
+ "/wallet",
389
+ "/btw",
390
+ ]);
391
+ const SANDBOX_ROWS = [
392
+ {
393
+ key: "mode",
394
+ label: "Mode",
395
+ type: "toggle",
396
+ getDisplay: (mode) => (mode === "shuru" ? "Shuru" : "Off"),
397
+ getOptions: () => ["Off", "Shuru"],
398
+ apply: (_mode, _s, value) => ({ mode: value === "Shuru" ? "shuru" : "off" }),
399
+ },
400
+ {
401
+ key: "allowNet",
402
+ label: "Network",
403
+ type: "toggle",
404
+ getDisplay: (_m, s) => (s.allowNet ? "On" : "Off"),
405
+ getOptions: () => ["Off", "On"],
406
+ apply: (_m, _s, value) => ({ settings: { allowNet: value === "On" } }),
407
+ },
408
+ {
409
+ key: "allowedHosts",
410
+ label: "Allowed hosts",
411
+ type: "text",
412
+ placeholder: "api.openai.com, registry.npmjs.org",
413
+ getDisplay: (_m, s) => s.allowedHosts?.join(", ") || "(unrestricted)",
414
+ apply: (_m, _s, value) => ({
415
+ settings: {
416
+ allowedHosts: value
417
+ ? value
418
+ .split(",")
419
+ .map((h) => h.trim())
420
+ .filter(Boolean)
421
+ : undefined,
422
+ },
423
+ }),
424
+ },
425
+ {
426
+ key: "ports",
427
+ label: "Port forwards",
428
+ type: "text",
429
+ placeholder: "8080:80, 8443:443",
430
+ getDisplay: (_m, s) => s.ports?.join(", ") || "(none)",
431
+ apply: (_m, _s, value) => ({
432
+ settings: {
433
+ ports: value
434
+ ? value
435
+ .split(",")
436
+ .map((p) => p.trim())
437
+ .filter(Boolean)
438
+ : undefined,
439
+ },
440
+ }),
441
+ },
442
+ {
443
+ key: "cpus",
444
+ label: "CPUs",
445
+ type: "text",
446
+ placeholder: "e.g. 4",
447
+ getDisplay: (_m, s) => (s.cpus ? String(s.cpus) : "(default)"),
448
+ apply: (_m, _s, value) => ({ settings: { cpus: value ? parseInt(value, 10) || undefined : undefined } }),
449
+ },
450
+ {
451
+ key: "memory",
452
+ label: "Memory (MB)",
453
+ type: "text",
454
+ placeholder: "e.g. 4096",
455
+ getDisplay: (_m, s) => (s.memory ? String(s.memory) : "(default)"),
456
+ apply: (_m, _s, value) => ({ settings: { memory: value ? parseInt(value, 10) || undefined : undefined } }),
457
+ },
458
+ {
459
+ key: "diskSize",
460
+ label: "Disk size (MB)",
461
+ type: "text",
462
+ placeholder: "e.g. 8192",
463
+ getDisplay: (_m, s) => (s.diskSize ? String(s.diskSize) : "(default)"),
464
+ apply: (_m, _s, value) => ({ settings: { diskSize: value ? parseInt(value, 10) || undefined : undefined } }),
465
+ },
466
+ {
467
+ key: "from",
468
+ label: "Checkpoint",
469
+ type: "text",
470
+ placeholder: "checkpoint name",
471
+ getDisplay: (_m, s) => s.from || "(none)",
472
+ apply: (_m, _s, value) => ({ settings: { from: value || undefined } }),
473
+ },
474
+ ];
475
+ function getSandboxVisibleRows(mode) {
476
+ return mode === "shuru" ? SANDBOX_ROWS : SANDBOX_ROWS.slice(0, 1);
477
+ }
478
+ const WALLET_ROWS = [
479
+ {
480
+ key: "enabled",
481
+ label: "Payments",
482
+ type: "toggle",
483
+ getDisplay: (s) => (s.enabled ? "enabled" : "disabled"),
484
+ getOptions: () => ["enabled", "disabled"],
485
+ apply: (_s, v) => ({ enabled: v === "enabled" }),
486
+ },
487
+ {
488
+ key: "chain",
489
+ label: "Chain",
490
+ type: "toggle",
491
+ getDisplay: (s) => s.chain,
492
+ getOptions: () => ["base-sepolia", "base"],
493
+ apply: (_s, v) => ({ chain: v }),
494
+ },
495
+ {
496
+ key: "autoApprove",
497
+ label: "Auto-approve",
498
+ type: "toggle",
499
+ getDisplay: (s) => (s.approval.autoApprove ? "on" : "off"),
500
+ getOptions: () => ["off", "on"],
501
+ apply: (s, v) => ({ approval: { ...s.approval, autoApprove: v === "on" } }),
502
+ },
503
+ {
504
+ key: "address",
505
+ label: "Address",
506
+ type: "readonly",
507
+ getDisplay: (_s, info) => info.address ?? "No wallet",
508
+ },
509
+ {
510
+ key: "eth",
511
+ label: "ETH",
512
+ type: "readonly",
513
+ getDisplay: (_s, info) => info.ethBalance ?? "...",
514
+ },
515
+ {
516
+ key: "usdc",
517
+ label: "USDC",
518
+ type: "readonly",
519
+ getDisplay: (_s, info) => info.usdcBalance ?? "...",
520
+ },
521
+ ];
522
+ function parseCustomSubagentSlashCommand(cmd, subagents) {
523
+ const trimmed = cmd.trim();
524
+ if (!trimmed.startsWith("/"))
525
+ return null;
526
+ const body = trimmed.slice(1).trim();
527
+ if (!body)
528
+ return null;
529
+ const commandToken = body.split(/\s+/, 1)[0]?.toLowerCase();
530
+ if (commandToken && BUILTIN_TYPED_SLASH_COMMANDS.has(`/${commandToken}`)) {
531
+ return null;
532
+ }
533
+ const lowerBody = body.toLowerCase();
534
+ const sortedSubagents = [...subagents].sort((a, b) => b.name.length - a.name.length);
535
+ const match = sortedSubagents.find((item) => {
536
+ const lowerName = item.name.trim().toLowerCase();
537
+ return lowerBody === lowerName || lowerBody.startsWith(`${lowerName} `);
538
+ });
539
+ if (!match)
540
+ return null;
541
+ return {
542
+ agentName: match.name,
543
+ prompt: body.slice(match.name.length).trim(),
544
+ };
545
+ }
546
+ function buildCustomSubagentSlashPrompt(agentName, prompt) {
547
+ return `Use the custom sub-agent "${agentName}" for this task.
548
+
549
+ Delegate the work with the \`task\` tool using:
550
+ - \`agent\`: "${agentName}"
551
+ - \`description\`: a short summary of the work
552
+ - \`prompt\`: a detailed prompt based on the user's request
553
+
554
+ User request:
555
+ ${prompt}`;
556
+ }
557
+ const CONNECT_CHANNELS = [
558
+ { id: "telegram", label: "Telegram", description: "Chat from Telegram" },
559
+ ];
560
+ const MCP_REMOTE_FIELDS = ["transport", "label", "url", "headers", "env"];
561
+ const MCP_STDIO_FIELDS = ["transport", "label", "command", "args", "cwd", "env"];
562
+ export function App({ agent, startupConfig, initialMessage, onExit }) {
563
+ const t = dark;
564
+ const renderer = useRenderer();
565
+ // Set initial status bar values synchronously before first render
566
+ useMemo(() => {
567
+ statusBarStore.setState({
568
+ provider: agent.getProviderId(),
569
+ model: agent.getModel(),
570
+ });
571
+ }, []);
572
+ // Wire status bar subscriptions once at boot (Plan 06)
573
+ useEffect(() => wireStatusBar(), []);
574
+ const initialHasApiKey = agent.hasApiKey();
575
+ const [hasApiKey, setHasApiKey] = useState(initialHasApiKey);
576
+ const [messages, setMessages] = useState(() => agent.getChatEntries());
577
+ const [streamContent, setStreamContent] = useState("");
578
+ const [_streamReasoning, setStreamReasoning] = useState("");
579
+ const [isProcessing, setIsProcessing] = useState(false);
580
+ const [liveTurnSourceLabel, setLiveTurnSourceLabel] = useState(null);
581
+ const [model, setModel] = useState(agent.getModel());
582
+ const [sandboxMode, setSandboxModeState] = useState(agent.getSandboxMode());
583
+ const [mode, setModeState] = useState(agent.getMode());
584
+ const [showModelPicker, setShowModelPicker] = useState(false);
585
+ const [modelPickerIndex, setModelPickerIndex] = useState(0);
586
+ const [modelSearchQuery, setModelSearchQuery] = useState("");
587
+ const [showSandboxPicker, setShowSandboxPicker] = useState(false);
588
+ const [sandboxSettings, setSandboxSettingsState] = useState(() => agent.getSandboxSettings());
589
+ const [sandboxSettingsFocusIndex, setSandboxSettingsFocusIndex] = useState(0);
590
+ const [sandboxSettingsEditing, setSandboxSettingsEditing] = useState(null);
591
+ const [sandboxSettingsEditBuffer, setSandboxSettingsEditBuffer] = useState("");
592
+ const [showWalletPicker, setShowWalletPicker] = useState(false);
593
+ const [walletSettings, setWalletSettings] = useState(() => loadPaymentSettings());
594
+ const [walletFocusIndex, setWalletFocusIndex] = useState(0);
595
+ const [walletDisplayInfo, setWalletDisplayInfo] = useState({
596
+ address: null,
597
+ ethBalance: null,
598
+ usdcBalance: null,
599
+ });
600
+ const [pendingPaymentApproval, setPendingPaymentApproval] = useState(null);
601
+ const [activeToolCalls, setActiveToolCalls] = useState([]);
602
+ const [sessionTitle, setSessionTitle] = useState(() => agent.getSessionTitle());
603
+ const [sessionId, setSessionId] = useState(() => agent.getSessionId());
604
+ const [showApiKeyModal, setShowApiKeyModal] = useState(() => !initialHasApiKey);
605
+ const [apiKeyError, setApiKeyError] = useState(null);
606
+ const [showSlashMenu, setShowSlashMenu] = useState(false);
607
+ const [slashMenuIndex, setSlashMenuIndex] = useState(0);
608
+ const [slashSearchQuery, setSlashSearchQuery] = useState("");
609
+ const [btwState, setBtwState] = useState(null);
610
+ const btwAbortRef = useRef(null);
611
+ const btwStateRef = useRef(null);
612
+ const [reasoningEffortByModel, setReasoningEffortByModel] = useState(() => Object.fromEntries(Object.entries(loadUserSettings().reasoningEffortByModel ?? {}).map(([modelId, effort]) => [
613
+ normalizeModelId(modelId),
614
+ effort,
615
+ ])));
616
+ const [pasteBlocks, setPasteBlocks] = useState([]);
617
+ const [activePlan, setActivePlan] = useState(null);
618
+ /** Incremented on each successful TUI copy; drives a brief "Copied" banner. */
619
+ const [copyFlashId, setCopyFlashId] = useState(0);
620
+ const [expandedMessages, setExpandedMessages] = useState(() => new Set());
621
+ const [activeSubagent, setActiveSubagent] = useState(null);
622
+ const [pqs, setPqs] = useState(initialPlanQuestionsState());
623
+ const pasteCounterRef = useRef(0);
624
+ const pasteBlocksRef = useRef([]);
625
+ const apiKeyInputRef = useRef(null);
626
+ const inputRef = useRef(null);
627
+ const scrollRef = useRef(null);
628
+ const { width, height } = useTerminalDimensions();
629
+ const processedInitial = useRef(false);
630
+ const contentAccRef = useRef("");
631
+ const startTimeRef = useRef(0);
632
+ const isProcessingRef = useRef(false);
633
+ const hasApiKeyRef = useRef(initialHasApiKey);
634
+ const showApiKeyModalRef = useRef(!initialHasApiKey);
635
+ const queuedMessagesRef = useRef([]);
636
+ const processMessageRef = useRef(() => { });
637
+ const [queuedMessages, setQueuedMessages] = useState([]);
638
+ const modeInfoRef = useRef(MODES[0]);
639
+ const activeRunIdRef = useRef(0);
640
+ const interruptedRunIdRef = useRef(null);
641
+ const activeTurnRef = useRef(null);
642
+ const coordinatorRef = useRef(createTurnCoordinator());
643
+ const bridgeRef = useRef(null);
644
+ const telegramAgentsRef = useRef(new Map());
645
+ const telegramEntryCountsRef = useRef(new Map());
646
+ const telegramSubagentUnsubsRef = useRef(new Map());
647
+ const [showConnectModal, setShowConnectModal] = useState(false);
648
+ const [showTelegramTokenModal, setShowTelegramTokenModal] = useState(false);
649
+ const [showTelegramPairModal, setShowTelegramPairModal] = useState(false);
650
+ const [telegramTokenError, setTelegramTokenError] = useState(null);
651
+ const [telegramPairError, setTelegramPairError] = useState(null);
652
+ const [connectModalIndex, setConnectModalIndex] = useState(0);
653
+ const telegramTokenInputRef = useRef(null);
654
+ const telegramPairInputRef = useRef(null);
655
+ const showConnectModalRef = useRef(false);
656
+ const showTelegramTokenModalRef = useRef(false);
657
+ const showTelegramPairModalRef = useRef(false);
658
+ const [showMcpModal, setShowMcpModal] = useState(false);
659
+ const [showMcpEditor, setShowMcpEditor] = useState(false);
660
+ const [mcpSearchQuery, setMcpSearchQuery] = useState("");
661
+ const [mcpModalIndex, setMcpModalIndex] = useState(0);
662
+ const [mcpServers, setMcpServers] = useState(() => loadMcpServers());
663
+ const [mcpEditorDraft, setMcpEditorDraft] = useState(createEmptyMcpEditorDraft());
664
+ const [mcpEditorField, setMcpEditorField] = useState("transport");
665
+ const [mcpEditorSyncKey, setMcpEditorSyncKey] = useState(0);
666
+ const [mcpEditorError, setMcpEditorError] = useState(null);
667
+ const [editingMcpId, setEditingMcpId] = useState(null);
668
+ const showMcpModalRef = useRef(false);
669
+ const showMcpEditorRef = useRef(false);
670
+ const mcpLabelRef = useRef(null);
671
+ const mcpUrlRef = useRef(null);
672
+ const mcpHeadersRef = useRef(null);
673
+ const mcpCommandRef = useRef(null);
674
+ const mcpArgsRef = useRef(null);
675
+ const mcpCwdRef = useRef(null);
676
+ const mcpEnvRef = useRef(null);
677
+ const [showAgentsModal, setShowAgentsModal] = useState(false);
678
+ const [showAgentsEditor, setShowAgentsEditor] = useState(false);
679
+ const [subAgents, setSubAgents] = useState(() => loadValidSubAgents());
680
+ const [agentsSearchQuery, setAgentsSearchQuery] = useState("");
681
+ const [agentsModalIndex, setAgentsModalIndex] = useState(0);
682
+ const [editingSubagent, setEditingSubagent] = useState(null);
683
+ const [agentsEditorDraft, setAgentsEditorDraft] = useState({ name: "", instruction: "" });
684
+ const [agentsEditorField, setAgentsEditorField] = useState("name");
685
+ const [agentsEditorModelIndex, setAgentsEditorModelIndex] = useState(() => Math.max(0, MODELS.findIndex((model) => model.id === DEFAULT_MODEL)));
686
+ const [agentsEditorSyncKey, setAgentsEditorSyncKey] = useState(0);
687
+ const [agentsEditorError, setAgentsEditorError] = useState(null);
688
+ const showAgentsModalRef = useRef(false);
689
+ const showAgentsEditorRef = useRef(false);
690
+ const subagentNameRef = useRef(null);
691
+ const subagentInstructionRef = useRef(null);
692
+ const [showScheduleModal, setShowScheduleModal] = useState(false);
693
+ const [schedules, setSchedules] = useState([]);
694
+ const [scheduleSearchQuery, setScheduleSearchQuery] = useState("");
695
+ const [scheduleModalIndex, setScheduleModalIndex] = useState(0);
696
+ const showScheduleModalRef = useRef(false);
697
+ const [updateInfo, setUpdateInfo] = useState(null);
698
+ const [showUpdateModal, setShowUpdateModal] = useState(false);
699
+ const [isUpdating, setIsUpdating] = useState(false);
700
+ const [updateOutput, setUpdateOutput] = useState(null);
701
+ const showUpdateModalRef = useRef(false);
702
+ const fileIndexRef = useRef(null);
703
+ if (!fileIndexRef.current) {
704
+ fileIndexRef.current = new FileIndex(agent.getCwd());
705
+ }
706
+ const fileMentionCounterRef = useRef(0);
707
+ const fileMentionBlocksRef = useRef([]);
708
+ const handleFileAccept = useCallback((filePath, tokenInfo) => {
709
+ const ta = inputRef.current;
710
+ if (!ta)
711
+ return;
712
+ const id = ++fileMentionCounterRef.current;
713
+ const block = { id, path: fileIndexRef.current?.resolvePath(filePath) ?? filePath };
714
+ fileMentionBlocksRef.current = [...fileMentionBlocksRef.current, block];
715
+ const text = ta.plainText;
716
+ const before = text.slice(0, tokenInfo.startPos);
717
+ const after = text.slice(tokenInfo.endPos);
718
+ const token = getFileMentionToken(block);
719
+ const newText = `${before}${token} ${after}`;
720
+ ta.setText(newText);
721
+ ta.cursorOffset = before.length + token.length + 1;
722
+ }, []);
723
+ const typeahead = useTypeahead(inputRef, fileIndexRef.current, handleFileAccept);
724
+ const typeaheadRef = useRef(typeahead);
725
+ typeaheadRef.current = typeahead;
726
+ const setMode = useCallback((m) => {
727
+ if (m === "agent" && mode === "plan" && activePlan) {
728
+ const planText = [
729
+ `# ${activePlan.title}`,
730
+ activePlan.summary,
731
+ "",
732
+ ...activePlan.steps.map((s, i) => `${i + 1}. ${s.title}: ${s.description}${s.filePaths?.length ? ` (${s.filePaths.join(", ")})` : ""}`),
733
+ ].join("\n");
734
+ agent.setPlanContext(planText);
735
+ }
736
+ agent.setMode(m);
737
+ setModeState(m);
738
+ setModel(agent.getModel());
739
+ }, [agent, mode, activePlan]);
740
+ const cycleMode = useCallback(() => {
741
+ const idx = MODES.findIndex((m) => m.id === mode);
742
+ setMode(MODES[(idx + 1) % MODES.length].id);
743
+ }, [mode, setMode]);
744
+ const modeInfo = MODES.find((m) => m.id === mode);
745
+ modeInfoRef.current = modeInfo;
746
+ const modelInfo = getModelInfo(model);
747
+ const contextStats = modelInfo ? agent.getContextStats(modelInfo.contextWindow, streamContent) : null;
748
+ // UI Loading logic for dynamic models
749
+ const modelList = isLoading ? [] : MODELS;
750
+ const filteredModels = modelSearchQuery
751
+ ? modelList.filter((m) => m.name.toLowerCase().includes(modelSearchQuery.toLowerCase()) ||
752
+ m.id.toLowerCase().includes(modelSearchQuery.toLowerCase()))
753
+ : [...modelList];
754
+ const filteredModelIds = filteredModels.map((m) => m.id);
755
+ const filteredSlashItems = slashSearchQuery
756
+ ? SLASH_MENU_ITEMS.filter((item) => item.label.toLowerCase().includes(slashSearchQuery.toLowerCase()) ||
757
+ item.description.toLowerCase().includes(slashSearchQuery.toLowerCase()))
758
+ : SLASH_MENU_ITEMS;
759
+ const mcpRows = buildMcpBrowseRows(mcpServers, POPULAR_MCP_CATALOG, mcpSearchQuery);
760
+ const mcpEditorFields = mcpEditorDraft.transport === "stdio" ? MCP_STDIO_FIELDS : MCP_REMOTE_FIELDS;
761
+ const agentRows = useMemo(() => buildSubagentBrowseRows(subAgents, agentsSearchQuery), [subAgents, agentsSearchQuery]);
762
+ const scheduleRows = useMemo(() => buildScheduleBrowseRows(schedules, scheduleSearchQuery), [schedules, scheduleSearchQuery]);
763
+ const syncStoredMcpServers = useCallback((servers) => {
764
+ setMcpServers(servers);
765
+ saveMcpServers(servers);
766
+ }, []);
767
+ const applySandboxMode = useCallback((next) => {
768
+ agent.setSandboxMode(next);
769
+ for (const telegramAgent of telegramAgentsRef.current.values()) {
770
+ telegramAgent.setSandboxMode(next);
771
+ }
772
+ setSandboxModeState(next);
773
+ saveProjectSettings({ sandboxMode: next });
774
+ saveUserSettings({ sandboxMode: next });
775
+ }, [agent]);
776
+ const applySandboxSettings = useCallback((next) => {
777
+ agent.setSandboxSettings(next);
778
+ for (const telegramAgent of telegramAgentsRef.current.values()) {
779
+ telegramAgent.setSandboxSettings(next);
780
+ }
781
+ setSandboxSettingsState(next);
782
+ saveProjectSettings({ sandbox: next });
783
+ saveUserSettings({ sandbox: next });
784
+ }, [agent]);
785
+ const openSandboxPicker = useCallback(() => {
786
+ setSandboxSettingsFocusIndex(0);
787
+ setSandboxSettingsEditing(null);
788
+ setSandboxSettingsEditBuffer("");
789
+ setShowSandboxPicker(true);
790
+ }, []);
791
+ const applyWalletSettings = useCallback((next) => {
792
+ setWalletSettings(next);
793
+ savePaymentSettings(next);
794
+ }, []);
795
+ const openWalletPicker = useCallback(() => {
796
+ setWalletFocusIndex(0);
797
+ setWalletSettings(loadPaymentSettings());
798
+ setShowWalletPicker(true);
799
+ // Wallet UI disabled — Stripe billing pending.
800
+ setWalletDisplayInfo({ address: null, ethBalance: null, usdcBalance: null });
801
+ }, []);
802
+ const setReasoningEfforts = useCallback((next) => {
803
+ setReasoningEffortByModel(next);
804
+ saveUserSettings({ reasoningEffortByModel: next });
805
+ }, []);
806
+ const replacePasteBlocks = useCallback((next) => {
807
+ pasteBlocksRef.current = next;
808
+ setPasteBlocks(next);
809
+ }, []);
810
+ const getModelReasoningEffort = useCallback((modelId) => {
811
+ const normalizedModelId = normalizeModelId(modelId);
812
+ return getEffectiveReasoningEffort(normalizedModelId, reasoningEffortByModel[normalizedModelId]);
813
+ }, [reasoningEffortByModel]);
814
+ const adjustModelReasoningEffort = useCallback((modelId, direction) => {
815
+ const normalizedModelId = normalizeModelId(modelId);
816
+ const supported = getSupportedReasoningEfforts(normalizedModelId);
817
+ if (supported.length === 0)
818
+ return;
819
+ const current = getModelReasoningEffort(normalizedModelId);
820
+ if (!current) {
821
+ if (direction > 0) {
822
+ setReasoningEfforts({ ...reasoningEffortByModel, [normalizedModelId]: supported[0] });
823
+ }
824
+ return;
825
+ }
826
+ const currentIndex = supported.indexOf(current);
827
+ if (direction < 0 && currentIndex <= 0) {
828
+ const { [normalizedModelId]: _, ...rest } = reasoningEffortByModel;
829
+ setReasoningEfforts(rest);
830
+ }
831
+ else {
832
+ const nextIndex = direction < 0 ? currentIndex - 1 : Math.min(supported.length - 1, currentIndex + 1);
833
+ setReasoningEfforts({ ...reasoningEffortByModel, [normalizedModelId]: supported[nextIndex] });
834
+ }
835
+ }, [getModelReasoningEffort, reasoningEffortByModel, setReasoningEfforts]);
836
+ const snapshotMcpEditorDraft = useCallback(() => {
837
+ return {
838
+ ...mcpEditorDraft,
839
+ label: mcpLabelRef.current?.plainText ?? mcpEditorDraft.label,
840
+ url: mcpUrlRef.current?.plainText ?? mcpEditorDraft.url,
841
+ headersText: mcpHeadersRef.current?.plainText ?? mcpEditorDraft.headersText,
842
+ command: mcpCommandRef.current?.plainText ?? mcpEditorDraft.command,
843
+ argsText: mcpArgsRef.current?.plainText ?? mcpEditorDraft.argsText,
844
+ cwd: mcpCwdRef.current?.plainText ?? mcpEditorDraft.cwd,
845
+ envText: mcpEnvRef.current?.plainText ?? mcpEditorDraft.envText,
846
+ };
847
+ }, [mcpEditorDraft]);
848
+ const openMcpModal = useCallback(() => {
849
+ const latest = loadMcpServers();
850
+ setMcpServers(latest);
851
+ setMcpSearchQuery("");
852
+ setMcpModalIndex(0);
853
+ setShowMcpModal(true);
854
+ setShowMcpEditor(false);
855
+ setEditingMcpId(null);
856
+ setMcpEditorError(null);
857
+ }, []);
858
+ const openMcpEditor = useCallback((draft, editingId = null) => {
859
+ setMcpEditorDraft(draft);
860
+ setEditingMcpId(editingId);
861
+ setMcpEditorField("transport");
862
+ setMcpEditorError(null);
863
+ setMcpEditorSyncKey((n) => n + 1);
864
+ setShowMcpEditor(true);
865
+ setShowMcpModal(true);
866
+ }, []);
867
+ const openCatalogMcp = useCallback((entry) => {
868
+ const existing = mcpServers.find((server) => toMcpServerId(server.id) === toMcpServerId(entry.id));
869
+ if (existing) {
870
+ openMcpEditor({
871
+ label: existing.label,
872
+ transport: existing.transport,
873
+ url: existing.url ?? "",
874
+ headersText: Object.entries(existing.headers ?? {})
875
+ .map(([key, value]) => `${key}: ${value}`)
876
+ .join("\n"),
877
+ command: existing.command ?? "",
878
+ argsText: (existing.args ?? []).join("\n"),
879
+ cwd: existing.cwd ?? "",
880
+ envText: Object.entries(existing.env ?? {})
881
+ .map(([key, value]) => `${key}=${value}`)
882
+ .join("\n"),
883
+ }, existing.id);
884
+ return;
885
+ }
886
+ openMcpEditor({
887
+ ...createEmptyMcpEditorDraft(),
888
+ label: entry.name,
889
+ transport: entry.starterTransport ?? "stdio",
890
+ });
891
+ }, [mcpServers, openMcpEditor]);
892
+ const editSavedMcp = useCallback((server) => {
893
+ openMcpEditor({
894
+ label: server.label,
895
+ transport: server.transport,
896
+ url: server.url ?? "",
897
+ headersText: Object.entries(server.headers ?? {})
898
+ .map(([key, value]) => `${key}: ${value}`)
899
+ .join("\n"),
900
+ command: server.command ?? "",
901
+ argsText: (server.args ?? []).join("\n"),
902
+ cwd: server.cwd ?? "",
903
+ envText: Object.entries(server.env ?? {})
904
+ .map(([key, value]) => `${key}=${value}`)
905
+ .join("\n"),
906
+ }, server.id);
907
+ }, [openMcpEditor]);
908
+ const toggleSavedMcp = useCallback((server) => {
909
+ syncStoredMcpServers(mcpServers.map((item) => (item.id === server.id ? { ...item, enabled: !item.enabled } : item)));
910
+ }, [mcpServers, syncStoredMcpServers]);
911
+ const deleteSavedMcp = useCallback((server) => {
912
+ syncStoredMcpServers(mcpServers.filter((item) => item.id !== server.id));
913
+ setMcpModalIndex((idx) => Math.max(0, Math.min(idx, Math.max(0, mcpRows.length - 2))));
914
+ }, [mcpRows.length, mcpServers, syncStoredMcpServers]);
915
+ const openAgentsModal = useCallback(() => {
916
+ setSubAgents(loadValidSubAgents());
917
+ setAgentsSearchQuery("");
918
+ setAgentsModalIndex(0);
919
+ setEditingSubagent(null);
920
+ setAgentsEditorError(null);
921
+ setShowAgentsEditor(false);
922
+ setShowAgentsModal(true);
923
+ }, []);
924
+ const openScheduleModal = useCallback(() => {
925
+ void agent
926
+ .listSchedules()
927
+ .then((latest) => {
928
+ setSchedules(latest);
929
+ setScheduleSearchQuery("");
930
+ setScheduleModalIndex(0);
931
+ setShowScheduleModal(true);
932
+ })
933
+ .catch((err) => {
934
+ const message = err instanceof Error ? err.message : String(err);
935
+ setMessages((prev) => [...prev, buildAssistantEntry(`Failed to load schedules: ${message}`)]);
936
+ });
937
+ }, [agent]);
938
+ const showScheduleDetails = useCallback((schedule) => {
939
+ void agent
940
+ .getScheduleDaemonStatus()
941
+ .then((status) => {
942
+ setMessages((prev) => [...prev, buildAssistantEntry(formatScheduleDetails(schedule, status))]);
943
+ setShowScheduleModal(false);
944
+ setScheduleSearchQuery("");
945
+ setTimeout(() => {
946
+ try {
947
+ scrollRef.current?.scrollTo(scrollRef.current?.scrollHeight ?? 99999);
948
+ }
949
+ catch {
950
+ /* */
951
+ }
952
+ }, 10);
953
+ })
954
+ .catch((err) => {
955
+ const message = err instanceof Error ? err.message : String(err);
956
+ setMessages((prev) => [...prev, buildAssistantEntry(`Failed to load schedule details: ${message}`)]);
957
+ });
958
+ }, [agent]);
959
+ const removeSchedule = useCallback((schedule) => {
960
+ void agent
961
+ .removeSchedule(schedule.id)
962
+ .then(async (message) => {
963
+ const latest = await agent.listSchedules();
964
+ setSchedules(latest);
965
+ setScheduleModalIndex((index) => Math.max(0, Math.min(index, Math.max(0, latest.length - 1))));
966
+ setMessages((prev) => [...prev, buildAssistantEntry(message)]);
967
+ setTimeout(() => {
968
+ try {
969
+ scrollRef.current?.scrollTo(scrollRef.current?.scrollHeight ?? 99999);
970
+ }
971
+ catch {
972
+ /* */
973
+ }
974
+ }, 10);
975
+ })
976
+ .catch((err) => {
977
+ const message = err instanceof Error ? err.message : String(err);
978
+ setMessages((prev) => [...prev, buildAssistantEntry(`Failed to remove schedule: ${message}`)]);
979
+ });
980
+ }, [agent]);
981
+ const openSubagentEditor = useCallback((agent) => {
982
+ setEditingSubagent(agent);
983
+ if (agent) {
984
+ setAgentsEditorDraft({ name: agent.name, instruction: agent.instruction });
985
+ setAgentsEditorModelIndex(Math.max(0, MODELS.findIndex((model) => model.id === normalizeModelId(agent.model))));
986
+ }
987
+ else {
988
+ setAgentsEditorDraft({ name: "", instruction: "" });
989
+ setAgentsEditorModelIndex(Math.max(0, MODELS.findIndex((model) => model.id === DEFAULT_MODEL)));
990
+ }
991
+ setAgentsEditorField("name");
992
+ setAgentsEditorError(null);
993
+ setAgentsEditorSyncKey((n) => n + 1);
994
+ setShowAgentsEditor(true);
995
+ setShowAgentsModal(true);
996
+ }, []);
997
+ const submitSubagentEditor = useCallback(() => {
998
+ const name = (subagentNameRef.current?.plainText || "").trim();
999
+ const instruction = subagentInstructionRef.current?.plainText || "";
1000
+ const model = MODELS[agentsEditorModelIndex]?.id;
1001
+ if (!name) {
1002
+ setAgentsEditorError("Name is required.");
1003
+ return;
1004
+ }
1005
+ if (isReservedSubagentName(name)) {
1006
+ setAgentsEditorError('Names "general" and "explore" are reserved.');
1007
+ return;
1008
+ }
1009
+ if (!model || !getModelIds().includes(model)) {
1010
+ setAgentsEditorError("Pick a valid model.");
1011
+ return;
1012
+ }
1013
+ const next = [...subAgents];
1014
+ if (editingSubagent) {
1015
+ const index = next.findIndex((item) => item.name === editingSubagent.name);
1016
+ if (index >= 0)
1017
+ next.splice(index, 1);
1018
+ }
1019
+ if (next.some((item) => item.name.toLowerCase() === name.toLowerCase())) {
1020
+ setAgentsEditorError("Another sub-agent already uses this name.");
1021
+ return;
1022
+ }
1023
+ next.push({ name, model, instruction });
1024
+ saveUserSettings({ subAgents: next });
1025
+ setSubAgents(loadValidSubAgents());
1026
+ setShowAgentsEditor(false);
1027
+ setEditingSubagent(null);
1028
+ setAgentsEditorError(null);
1029
+ }, [agentsEditorModelIndex, editingSubagent, subAgents]);
1030
+ const removeEditingSubagent = useCallback(() => {
1031
+ if (!editingSubagent)
1032
+ return;
1033
+ const next = subAgents.filter((item) => item.name !== editingSubagent.name);
1034
+ saveUserSettings({ subAgents: next });
1035
+ setSubAgents(loadValidSubAgents());
1036
+ setShowAgentsEditor(false);
1037
+ setEditingSubagent(null);
1038
+ setAgentsEditorError(null);
1039
+ setAgentsModalIndex(0);
1040
+ }, [editingSubagent, subAgents]);
1041
+ const submitMcpEditor = useCallback(() => {
1042
+ const draft = {
1043
+ label: mcpLabelRef.current?.plainText || "",
1044
+ transport: mcpEditorDraft.transport,
1045
+ url: mcpUrlRef.current?.plainText || "",
1046
+ headersText: mcpHeadersRef.current?.plainText || "",
1047
+ command: mcpCommandRef.current?.plainText || "",
1048
+ argsText: mcpArgsRef.current?.plainText || "",
1049
+ cwd: mcpCwdRef.current?.plainText || "",
1050
+ envText: mcpEnvRef.current?.plainText || "",
1051
+ };
1052
+ const baseId = toMcpServerId(draft.label);
1053
+ const currentServers = loadMcpServers();
1054
+ const conflictingServer = currentServers.find((s) => s.id === baseId && s.id !== editingMcpId);
1055
+ if (conflictingServer) {
1056
+ setMcpEditorError(`Only one protocol is supported per MCP. Edit "${conflictingServer.label}" instead.`);
1057
+ return;
1058
+ }
1059
+ const id = editingMcpId ?? baseId;
1060
+ const server = {
1061
+ id,
1062
+ label: draft.label.trim(),
1063
+ enabled: true,
1064
+ transport: draft.transport,
1065
+ ...(draft.transport === "stdio"
1066
+ ? {
1067
+ command: draft.command.trim(),
1068
+ args: draft.argsText
1069
+ .split("\n")
1070
+ .map((line) => line.trim())
1071
+ .filter(Boolean),
1072
+ cwd: draft.cwd.trim() || undefined,
1073
+ env: Object.keys(parseEnvLines(draft.envText)).length ? parseEnvLines(draft.envText) : undefined,
1074
+ }
1075
+ : {
1076
+ url: draft.url.trim(),
1077
+ headers: Object.keys(parseHeaderLines(draft.headersText)).length
1078
+ ? parseHeaderLines(draft.headersText)
1079
+ : undefined,
1080
+ env: Object.keys(parseEnvLines(draft.envText)).length ? parseEnvLines(draft.envText) : undefined,
1081
+ }),
1082
+ };
1083
+ const validation = validateMcpServerConfig(server);
1084
+ if (!validation.ok) {
1085
+ setMcpEditorError(validation.error);
1086
+ return;
1087
+ }
1088
+ const nextServers = editingMcpId
1089
+ ? currentServers.map((item) => item.id === editingMcpId ? { ...server, id: editingMcpId, enabled: item.enabled } : item)
1090
+ : [...currentServers, server];
1091
+ saveMcpServers(nextServers);
1092
+ setMcpServers(nextServers);
1093
+ setShowMcpEditor(false);
1094
+ setEditingMcpId(null);
1095
+ setMcpEditorError(null);
1096
+ setMcpSearchQuery("");
1097
+ setMcpModalIndex(Math.max(0, nextServers.findIndex((item) => item.id === (editingMcpId ?? server.id))));
1098
+ }, [editingMcpId, mcpEditorDraft.transport]);
1099
+ const cycleMcpEditorTransport = useCallback((direction = 1) => {
1100
+ const draft = snapshotMcpEditorDraft();
1101
+ const order = ["stdio", "http", "sse"];
1102
+ const currentIndex = order.indexOf(draft.transport);
1103
+ const nextTransport = order[(currentIndex + direction + order.length) % order.length];
1104
+ const nextDraft = { ...draft, transport: nextTransport };
1105
+ setMcpEditorDraft(nextDraft);
1106
+ setMcpEditorField("transport");
1107
+ setMcpEditorSyncKey((n) => n + 1);
1108
+ if (!editingMcpId)
1109
+ return;
1110
+ const existing = mcpServers.find((server) => server.id === editingMcpId);
1111
+ if (!existing)
1112
+ return;
1113
+ const optimisticServer = {
1114
+ id: existing.id,
1115
+ label: nextDraft.label.trim() || existing.label,
1116
+ enabled: existing.enabled,
1117
+ transport: nextTransport,
1118
+ ...(nextTransport === "stdio"
1119
+ ? {
1120
+ command: nextDraft.command.trim() || existing.command,
1121
+ args: nextDraft.argsText
1122
+ .split("\n")
1123
+ .map((line) => line.trim())
1124
+ .filter(Boolean),
1125
+ cwd: nextDraft.cwd.trim() || undefined,
1126
+ env: Object.keys(parseEnvLines(nextDraft.envText)).length ? parseEnvLines(nextDraft.envText) : undefined,
1127
+ }
1128
+ : {
1129
+ url: nextDraft.url.trim() || existing.url,
1130
+ headers: Object.keys(parseHeaderLines(nextDraft.headersText)).length
1131
+ ? parseHeaderLines(nextDraft.headersText)
1132
+ : undefined,
1133
+ env: Object.keys(parseEnvLines(nextDraft.envText)).length ? parseEnvLines(nextDraft.envText) : undefined,
1134
+ }),
1135
+ };
1136
+ syncStoredMcpServers(mcpServers.map((server) => (server.id === editingMcpId ? optimisticServer : server)));
1137
+ }, [editingMcpId, mcpServers, snapshotMcpEditorDraft, syncStoredMcpServers]);
1138
+ useEffect(() => {
1139
+ if (!showMcpEditor || !editingMcpId)
1140
+ return;
1141
+ const existing = mcpServers.find((server) => server.id === editingMcpId);
1142
+ if (!existing)
1143
+ return;
1144
+ if (existing.transport === mcpEditorDraft.transport)
1145
+ return;
1146
+ const syncedServer = {
1147
+ id: existing.id,
1148
+ label: mcpEditorDraft.label.trim() || existing.label,
1149
+ enabled: existing.enabled,
1150
+ transport: mcpEditorDraft.transport,
1151
+ ...(mcpEditorDraft.transport === "stdio"
1152
+ ? {
1153
+ command: mcpEditorDraft.command.trim() || undefined,
1154
+ args: mcpEditorDraft.argsText
1155
+ .split("\n")
1156
+ .map((line) => line.trim())
1157
+ .filter(Boolean),
1158
+ cwd: mcpEditorDraft.cwd.trim() || undefined,
1159
+ env: Object.keys(parseEnvLines(mcpEditorDraft.envText)).length
1160
+ ? parseEnvLines(mcpEditorDraft.envText)
1161
+ : undefined,
1162
+ }
1163
+ : {
1164
+ url: mcpEditorDraft.url.trim() || undefined,
1165
+ headers: Object.keys(parseHeaderLines(mcpEditorDraft.headersText)).length
1166
+ ? parseHeaderLines(mcpEditorDraft.headersText)
1167
+ : undefined,
1168
+ env: Object.keys(parseEnvLines(mcpEditorDraft.envText)).length
1169
+ ? parseEnvLines(mcpEditorDraft.envText)
1170
+ : undefined,
1171
+ }),
1172
+ };
1173
+ syncStoredMcpServers(mcpServers.map((server) => (server.id === editingMcpId ? syncedServer : server)));
1174
+ }, [editingMcpId, mcpEditorDraft, mcpServers, showMcpEditor, syncStoredMcpServers]);
1175
+ useEffect(() => {
1176
+ setMcpModalIndex((idx) => Math.max(0, Math.min(idx, Math.max(0, mcpRows.length - 1))));
1177
+ }, [mcpRows.length]);
1178
+ useEffect(() => {
1179
+ setScheduleModalIndex((idx) => Math.max(0, Math.min(idx, Math.max(0, scheduleRows.length - 1))));
1180
+ }, [scheduleRows.length]);
1181
+ const scrollToBottom = useCallback(() => {
1182
+ try {
1183
+ scrollRef.current?.scrollTo(scrollRef.current?.scrollHeight ?? 99999);
1184
+ }
1185
+ catch {
1186
+ /* */
1187
+ }
1188
+ }, []);
1189
+ const clearLiveTurnUi = useCallback(() => {
1190
+ setStreamContent("");
1191
+ setStreamReasoning("");
1192
+ setActiveToolCalls([]);
1193
+ setActiveSubagent(null);
1194
+ setLiveTurnSourceLabel(null);
1195
+ contentAccRef.current = "";
1196
+ }, []);
1197
+ const finishTurnProcessing = useCallback(() => {
1198
+ const nextQueued = queuedMessagesRef.current.shift();
1199
+ if (nextQueued) {
1200
+ setQueuedMessages(queuedMessagesRef.current.map((msg) => msg.displayText));
1201
+ isProcessingRef.current = false;
1202
+ void processMessageRef.current(nextQueued.text, nextQueued.displayText);
1203
+ return;
1204
+ }
1205
+ isProcessingRef.current = false;
1206
+ setIsProcessing(false);
1207
+ }, []);
1208
+ const beginLiveTurn = useCallback((turn) => {
1209
+ clearLiveTurnUi();
1210
+ activeTurnRef.current = {
1211
+ ...turn,
1212
+ latestAssistantText: "",
1213
+ flushedAssistantChars: 0,
1214
+ };
1215
+ isProcessingRef.current = true;
1216
+ setIsProcessing(true);
1217
+ setLiveTurnSourceLabel(turn.sourceLabel ?? null);
1218
+ startTimeRef.current = Date.now();
1219
+ }, [clearLiveTurnUi]);
1220
+ const flushPendingAssistantMessage = useCallback(() => {
1221
+ const activeTurn = activeTurnRef.current;
1222
+ if (!activeTurn)
1223
+ return;
1224
+ const cleaned = sanitizeContent(contentAccRef.current);
1225
+ if (!cleaned) {
1226
+ contentAccRef.current = "";
1227
+ setStreamContent("");
1228
+ if (activeTurn.kind === "telegram") {
1229
+ activeTurn.flushedAssistantChars = activeTurn.latestAssistantText.length;
1230
+ }
1231
+ return;
1232
+ }
1233
+ setMessages((prev) => [
1234
+ ...prev,
1235
+ buildAssistantEntry(cleaned, {
1236
+ modeColor: activeTurn.modeColor,
1237
+ remoteKey: activeTurn.remoteKey,
1238
+ sourceLabel: activeTurn.sourceLabel,
1239
+ }),
1240
+ ]);
1241
+ if (activeTurn.kind === "telegram") {
1242
+ activeTurn.flushedAssistantChars = activeTurn.latestAssistantText.length;
1243
+ }
1244
+ contentAccRef.current = "";
1245
+ setStreamContent("");
1246
+ }, []);
1247
+ const applyLocalAssistantDelta = useCallback((delta) => {
1248
+ contentAccRef.current += delta;
1249
+ setStreamContent(sanitizeContent(contentAccRef.current));
1250
+ setTimeout(scrollToBottom, 10);
1251
+ }, [scrollToBottom]);
1252
+ const applyTelegramAssistantPreview = useCallback((fullContent) => {
1253
+ const activeTurn = activeTurnRef.current;
1254
+ if (!activeTurn || activeTurn.kind !== "telegram")
1255
+ return;
1256
+ activeTurn.latestAssistantText = fullContent;
1257
+ contentAccRef.current = getUnflushedTelegramAssistantContent(fullContent, activeTurn.flushedAssistantChars);
1258
+ setStreamContent(sanitizeContent(contentAccRef.current));
1259
+ setTimeout(scrollToBottom, 10);
1260
+ }, [scrollToBottom]);
1261
+ const showLiveToolCalls = useCallback((toolCalls) => {
1262
+ flushPendingAssistantMessage();
1263
+ setActiveToolCalls(toolCalls);
1264
+ setTimeout(scrollToBottom, 10);
1265
+ }, [flushPendingAssistantMessage, scrollToBottom]);
1266
+ const appendLiveToolResult = useCallback((toolCall, toolResult) => {
1267
+ const activeTurn = activeTurnRef.current;
1268
+ if (!activeTurn)
1269
+ return;
1270
+ setMessages((prev) => [
1271
+ ...prev,
1272
+ buildToolResultEntry(toolCall, toolResult, {
1273
+ modeColor: activeTurn.modeColor,
1274
+ remoteKey: activeTurn.remoteKey,
1275
+ sourceLabel: activeTurn.sourceLabel,
1276
+ }),
1277
+ ]);
1278
+ if (toolResult.plan?.questions?.length) {
1279
+ setActivePlan(toolResult.plan);
1280
+ setPqs(initialPlanQuestionsState());
1281
+ }
1282
+ setActiveToolCalls([]);
1283
+ setTimeout(scrollToBottom, 10);
1284
+ }, [scrollToBottom]);
1285
+ const syncTelegramTurnEntries = useCallback((activeTurn) => {
1286
+ if (activeTurn.kind !== "telegram" || activeTurn.userId === undefined || !activeTurn.remoteKey)
1287
+ return;
1288
+ const currentEntries = activeTurn.agent.getChatEntries();
1289
+ const syncedCount = telegramEntryCountsRef.current.get(activeTurn.userId) ?? 0;
1290
+ if (currentEntries.length <= syncedCount)
1291
+ return;
1292
+ const delta = decorateTelegramEntries(currentEntries.slice(syncedCount), activeTurn.userId, activeTurn.remoteKey);
1293
+ telegramEntryCountsRef.current.set(activeTurn.userId, currentEntries.length);
1294
+ setMessages((prev) => replaceTurnEntries(prev, activeTurn.remoteKey, delta));
1295
+ }, []);
1296
+ const finalizeActiveTurn = useCallback(({ wasInterrupted = false, hadError = false } = {}) => {
1297
+ const activeTurn = activeTurnRef.current;
1298
+ if (!activeTurn) {
1299
+ finishTurnProcessing();
1300
+ return;
1301
+ }
1302
+ const finalContent = sanitizeContent(contentAccRef.current);
1303
+ if (!wasInterrupted && finalContent) {
1304
+ setMessages((prev) => [
1305
+ ...prev,
1306
+ buildAssistantEntry(finalContent, {
1307
+ modeColor: activeTurn.modeColor,
1308
+ remoteKey: activeTurn.remoteKey,
1309
+ sourceLabel: activeTurn.sourceLabel,
1310
+ }),
1311
+ ]);
1312
+ }
1313
+ if (!wasInterrupted && !hadError) {
1314
+ if (activeTurn.kind === "local" && activeTurn.agent.getSessionId()) {
1315
+ setMessages((prev) => {
1316
+ const fresh = activeTurn.agent.getChatEntries();
1317
+ let prevUserIdx = 0;
1318
+ for (let i = 0; i < fresh.length; i++) {
1319
+ if (fresh[i].type !== "user")
1320
+ continue;
1321
+ while (prevUserIdx < prev.length && prev[prevUserIdx].type !== "user")
1322
+ prevUserIdx++;
1323
+ if (prevUserIdx < prev.length) {
1324
+ fresh[i] = { ...fresh[i], content: prev[prevUserIdx].content };
1325
+ prevUserIdx++;
1326
+ }
1327
+ }
1328
+ return fresh;
1329
+ });
1330
+ setSessionTitle(activeTurn.agent.getSessionTitle());
1331
+ setSessionId(activeTurn.agent.getSessionId());
1332
+ }
1333
+ else if (activeTurn.kind === "telegram") {
1334
+ syncTelegramTurnEntries(activeTurn);
1335
+ }
1336
+ }
1337
+ activeTurnRef.current = null;
1338
+ clearLiveTurnUi();
1339
+ finishTurnProcessing();
1340
+ setTimeout(scrollToBottom, 50);
1341
+ }, [clearLiveTurnUi, finishTurnProcessing, scrollToBottom, syncTelegramTurnEntries]);
1342
+ const wireTelegramAgentUi = useCallback((userId, telegramAgent) => {
1343
+ if (!telegramEntryCountsRef.current.has(userId)) {
1344
+ telegramEntryCountsRef.current.set(userId, telegramAgent.getChatEntries().length);
1345
+ }
1346
+ if (telegramSubagentUnsubsRef.current.has(userId)) {
1347
+ return;
1348
+ }
1349
+ const unsubscribe = telegramAgent.onSubagentStatus((status) => {
1350
+ if (activeTurnRef.current?.agent !== telegramAgent)
1351
+ return;
1352
+ setActiveSubagent(status);
1353
+ });
1354
+ telegramSubagentUnsubsRef.current.set(userId, unsubscribe);
1355
+ }, []);
1356
+ const getTelegramAgent = useCallback((userId) => {
1357
+ const map = telegramAgentsRef.current;
1358
+ const existing = map.get(userId);
1359
+ if (existing) {
1360
+ wireTelegramAgentUi(userId, existing);
1361
+ return existing;
1362
+ }
1363
+ const apiKey = getApiKey();
1364
+ if (!apiKey) {
1365
+ throw new Error("API key required. Set MUONROI_API_KEY or add via CLI.");
1366
+ }
1367
+ const u = loadUserSettings();
1368
+ const sid = u.telegram?.sessionsByUserId?.[String(userId)];
1369
+ const a = new Agent(apiKey, startupConfig.baseURL, startupConfig.model, startupConfig.maxToolRounds, {
1370
+ session: sid,
1371
+ sandboxMode,
1372
+ sandboxSettings,
1373
+ });
1374
+ if (!sid && a.getSessionId()) {
1375
+ saveUserSettings({
1376
+ telegram: {
1377
+ ...u.telegram,
1378
+ sessionsByUserId: {
1379
+ ...u.telegram?.sessionsByUserId,
1380
+ [String(userId)]: a.getSessionId(),
1381
+ },
1382
+ },
1383
+ });
1384
+ }
1385
+ wireTelegramAgentUi(userId, a);
1386
+ map.set(userId, a);
1387
+ return a;
1388
+ }, [sandboxMode, sandboxSettings, startupConfig, wireTelegramAgentUi]);
1389
+ const appendTelegramUserMessage = useCallback((event) => {
1390
+ const telegramAgent = getTelegramAgent(event.userId);
1391
+ beginLiveTurn({
1392
+ kind: "telegram",
1393
+ agent: telegramAgent,
1394
+ remoteKey: event.turnKey,
1395
+ userId: event.userId,
1396
+ sourceLabel: getTelegramSourceLabel("assistant", event.userId),
1397
+ });
1398
+ setMessages((prev) => [
1399
+ ...prev,
1400
+ buildUserEntry(event.content, {
1401
+ remoteKey: event.turnKey,
1402
+ sourceLabel: getTelegramSourceLabel("user", event.userId),
1403
+ }),
1404
+ ]);
1405
+ setTimeout(scrollToBottom, 10);
1406
+ }, [beginLiveTurn, getTelegramAgent, scrollToBottom]);
1407
+ const upsertTelegramAssistantMessage = useCallback((event) => {
1408
+ if (activeTurnRef.current?.remoteKey !== event.turnKey) {
1409
+ const telegramAgent = getTelegramAgent(event.userId);
1410
+ beginLiveTurn({
1411
+ kind: "telegram",
1412
+ agent: telegramAgent,
1413
+ remoteKey: event.turnKey,
1414
+ userId: event.userId,
1415
+ sourceLabel: getTelegramSourceLabel("assistant", event.userId),
1416
+ });
1417
+ }
1418
+ applyTelegramAssistantPreview(event.content);
1419
+ if (event.done) {
1420
+ finalizeActiveTurn();
1421
+ }
1422
+ }, [applyTelegramAssistantPreview, beginLiveTurn, finalizeActiveTurn, getTelegramAgent]);
1423
+ const showTelegramToolCalls = useCallback((event) => {
1424
+ if (activeTurnRef.current?.remoteKey !== event.turnKey) {
1425
+ const telegramAgent = getTelegramAgent(event.userId);
1426
+ beginLiveTurn({
1427
+ kind: "telegram",
1428
+ agent: telegramAgent,
1429
+ remoteKey: event.turnKey,
1430
+ userId: event.userId,
1431
+ sourceLabel: getTelegramSourceLabel("assistant", event.userId),
1432
+ });
1433
+ }
1434
+ showLiveToolCalls(event.toolCalls);
1435
+ }, [beginLiveTurn, getTelegramAgent, showLiveToolCalls]);
1436
+ const appendTelegramToolResult = useCallback((event) => {
1437
+ if (activeTurnRef.current?.remoteKey !== event.turnKey) {
1438
+ const telegramAgent = getTelegramAgent(event.userId);
1439
+ beginLiveTurn({
1440
+ kind: "telegram",
1441
+ agent: telegramAgent,
1442
+ remoteKey: event.turnKey,
1443
+ userId: event.userId,
1444
+ sourceLabel: getTelegramSourceLabel("assistant", event.userId),
1445
+ });
1446
+ }
1447
+ appendLiveToolResult(event.toolCall, event.toolResult);
1448
+ }, [appendLiveToolResult, beginLiveTurn, getTelegramAgent]);
1449
+ const startTelegramBridge = useCallback(() => {
1450
+ const token = getTelegramBotToken();
1451
+ if (!token || !getApiKey())
1452
+ return;
1453
+ if (bridgeRef.current)
1454
+ return;
1455
+ const bridge = createTelegramBridge({
1456
+ token,
1457
+ getApprovedUserIds: () => loadUserSettings().telegram?.approvedUserIds ?? [],
1458
+ coordinator: coordinatorRef.current,
1459
+ getTelegramAgent,
1460
+ onUserMessage: appendTelegramUserMessage,
1461
+ onAssistantMessage: upsertTelegramAssistantMessage,
1462
+ onToolCalls: showTelegramToolCalls,
1463
+ onToolResult: appendTelegramToolResult,
1464
+ onError: (msg) => {
1465
+ setMessages((p) => [...p, { type: "assistant", content: `Telegram: ${msg}`, timestamp: new Date() }]);
1466
+ },
1467
+ });
1468
+ bridgeRef.current = bridge;
1469
+ bridge.start();
1470
+ }, [
1471
+ appendTelegramToolResult,
1472
+ appendTelegramUserMessage,
1473
+ getTelegramAgent,
1474
+ showTelegramToolCalls,
1475
+ upsertTelegramAssistantMessage,
1476
+ ]);
1477
+ /** Start long polling when a bot token is already saved (pairing UI is optional if already approved). */
1478
+ useEffect(() => {
1479
+ if (!hasApiKey)
1480
+ return;
1481
+ if (!getTelegramBotToken())
1482
+ return;
1483
+ startTelegramBridge();
1484
+ }, [hasApiKey, startTelegramBridge]);
1485
+ const handleExit = useCallback(() => {
1486
+ // Best-effort EE session-end reconciliation (fire-and-forget)
1487
+ try {
1488
+ const { getDefaultEEClient: getEE, getLastSurfacedState: getSurfaced } = require("../ee/intercept.js");
1489
+ const { surfacedIds, timestamp } = getSurfaced();
1490
+ const ee = getEE();
1491
+ const cwd = agent.getCwd();
1492
+ // Prompt-stale: mark surfaced suggestions as stale on session end
1493
+ if (surfacedIds.length > 0) {
1494
+ ee.promptStale({
1495
+ state: { surfacedIds, timestamp },
1496
+ nextPromptMeta: { trigger: "session-end", cwd, tenantId: "local" },
1497
+ }).catch(() => { });
1498
+ }
1499
+ }
1500
+ catch {
1501
+ // Swallow all errors — exit must never fail due to EE
1502
+ }
1503
+ void bridgeRef.current?.stop();
1504
+ bridgeRef.current = null;
1505
+ onExit?.();
1506
+ }, [onExit, agent]);
1507
+ const showCopyBanner = useCallback(() => {
1508
+ setCopyFlashId((n) => n + 1);
1509
+ }, []);
1510
+ /** Match OpenCode: OSC 52 + real OS clipboard; used from keyboard and root onMouseUp. */
1511
+ const copyTuiSelectionToHost = useCallback(() => {
1512
+ if (!renderer.hasSelection)
1513
+ return false;
1514
+ const sel = renderer.getSelection();
1515
+ const text = sel ? getCompactTuiSelectionText(sel) : "";
1516
+ if (!text)
1517
+ return false;
1518
+ renderer.copyToClipboardOSC52(text);
1519
+ copyTextToHostClipboard(text);
1520
+ renderer.clearSelection();
1521
+ showCopyBanner();
1522
+ return true;
1523
+ }, [renderer, showCopyBanner]);
1524
+ const handleRootMouseUp = useCallback(() => {
1525
+ copyTuiSelectionToHost();
1526
+ inputRef.current?.focus();
1527
+ }, [copyTuiSelectionToHost]);
1528
+ const handleRootMouseDown = useCallback(() => {
1529
+ setTimeout(() => inputRef.current?.focus(), 0);
1530
+ }, []);
1531
+ useEffect(() => {
1532
+ if (copyFlashId === 0)
1533
+ return;
1534
+ const id = setTimeout(() => setCopyFlashId(0), 2000);
1535
+ return () => clearTimeout(id);
1536
+ }, [copyFlashId]);
1537
+ const openApiKeyModal = useCallback(() => {
1538
+ showApiKeyModalRef.current = true;
1539
+ setApiKeyError(null);
1540
+ setShowApiKeyModal(true);
1541
+ }, []);
1542
+ const closeApiKeyModal = useCallback(() => {
1543
+ showApiKeyModalRef.current = false;
1544
+ setApiKeyError(null);
1545
+ setShowApiKeyModal(false);
1546
+ }, []);
1547
+ const submitApiKey = useCallback(() => {
1548
+ const apiKey = (apiKeyInputRef.current?.plainText || "").trim();
1549
+ if (!apiKey) {
1550
+ setApiKeyError("Enter an API key to continue.");
1551
+ return;
1552
+ }
1553
+ if (!apiKey.startsWith("xai-")) {
1554
+ setApiKeyError("API keys should start with xai-.");
1555
+ return;
1556
+ }
1557
+ saveUserSettings({ apiKey });
1558
+ agent.setApiKey(apiKey);
1559
+ hasApiKeyRef.current = true;
1560
+ showApiKeyModalRef.current = false;
1561
+ setHasApiKey(true);
1562
+ setApiKeyError(null);
1563
+ setShowApiKeyModal(false);
1564
+ apiKeyInputRef.current?.clear();
1565
+ if (getTelegramBotToken()) {
1566
+ startTelegramBridge();
1567
+ }
1568
+ }, [agent, startTelegramBridge]);
1569
+ useEffect(() => {
1570
+ hasApiKeyRef.current = hasApiKey;
1571
+ }, [hasApiKey]);
1572
+ useEffect(() => {
1573
+ showApiKeyModalRef.current = showApiKeyModal;
1574
+ }, [showApiKeyModal]);
1575
+ useEffect(() => {
1576
+ showConnectModalRef.current = showConnectModal;
1577
+ }, [showConnectModal]);
1578
+ useEffect(() => {
1579
+ showTelegramTokenModalRef.current = showTelegramTokenModal;
1580
+ }, [showTelegramTokenModal]);
1581
+ useEffect(() => {
1582
+ showTelegramPairModalRef.current = showTelegramPairModal;
1583
+ }, [showTelegramPairModal]);
1584
+ useEffect(() => {
1585
+ showMcpModalRef.current = showMcpModal;
1586
+ }, [showMcpModal]);
1587
+ useEffect(() => {
1588
+ showMcpEditorRef.current = showMcpEditor;
1589
+ }, [showMcpEditor]);
1590
+ useEffect(() => {
1591
+ showAgentsModalRef.current = showAgentsModal;
1592
+ }, [showAgentsModal]);
1593
+ useEffect(() => {
1594
+ showAgentsEditorRef.current = showAgentsEditor;
1595
+ }, [showAgentsEditor]);
1596
+ useEffect(() => {
1597
+ showScheduleModalRef.current = showScheduleModal;
1598
+ }, [showScheduleModal]);
1599
+ useEffect(() => {
1600
+ showUpdateModalRef.current = showUpdateModal;
1601
+ }, [showUpdateModal]);
1602
+ useEffect(() => {
1603
+ let cancelled = false;
1604
+ checkForUpdate(startupConfig.version).then((result) => {
1605
+ if (cancelled || !result?.hasUpdate)
1606
+ return;
1607
+ setUpdateInfo(result);
1608
+ setShowUpdateModal(true);
1609
+ });
1610
+ return () => {
1611
+ cancelled = true;
1612
+ };
1613
+ }, [startupConfig.version]);
1614
+ useEffect(() => {
1615
+ return () => {
1616
+ void bridgeRef.current?.stop();
1617
+ bridgeRef.current = null;
1618
+ };
1619
+ }, []);
1620
+ const submitTelegramToken = useCallback(() => {
1621
+ const token = (telegramTokenInputRef.current?.plainText || "").trim();
1622
+ if (!token) {
1623
+ setTelegramTokenError("Paste your bot token from @BotFather.");
1624
+ return;
1625
+ }
1626
+ if (!getApiKey()) {
1627
+ setTelegramTokenError("Add an API key first.");
1628
+ return;
1629
+ }
1630
+ const u = loadUserSettings();
1631
+ saveUserSettings({ telegram: { ...u.telegram, botToken: token } });
1632
+ telegramTokenInputRef.current?.clear();
1633
+ setShowTelegramTokenModal(false);
1634
+ setTelegramTokenError(null);
1635
+ startTelegramBridge();
1636
+ setShowTelegramPairModal(true);
1637
+ setTelegramPairError(null);
1638
+ setMessages((p) => [
1639
+ ...p,
1640
+ {
1641
+ type: "assistant",
1642
+ content: "Telegram polling started. In Telegram, DM your bot and send /pair. Copy the code, then enter it below.",
1643
+ timestamp: new Date(),
1644
+ },
1645
+ ]);
1646
+ }, [startTelegramBridge]);
1647
+ const submitTelegramPair = useCallback(async () => {
1648
+ const code = (telegramPairInputRef.current?.plainText || "").trim();
1649
+ if (!code) {
1650
+ setTelegramPairError("Enter the pairing code.");
1651
+ return;
1652
+ }
1653
+ const result = approvePairingCode(code);
1654
+ if (!result.ok) {
1655
+ setTelegramPairError(result.error);
1656
+ return;
1657
+ }
1658
+ saveApprovedTelegramUserId(result.userId);
1659
+ telegramPairInputRef.current?.clear();
1660
+ setShowTelegramPairModal(false);
1661
+ setTelegramPairError(null);
1662
+ setMessages((p) => [
1663
+ ...p,
1664
+ {
1665
+ type: "assistant",
1666
+ content: `Telegram user ${result.userId} paired. Keep this CLI open while you use the bot.`,
1667
+ timestamp: new Date(),
1668
+ },
1669
+ ]);
1670
+ try {
1671
+ await bridgeRef.current?.sendDm(result.userId, "Pairing approved. You can message muonroi-cli here.");
1672
+ }
1673
+ catch {
1674
+ /* optional DM */
1675
+ }
1676
+ }, []);
1677
+ const beginTelegramFromConnect = useCallback(() => {
1678
+ setShowConnectModal(false);
1679
+ if (!getApiKey()) {
1680
+ setMessages((p) => [...p, { type: "assistant", content: "Add an API key first.", timestamp: new Date() }]);
1681
+ openApiKeyModal();
1682
+ return;
1683
+ }
1684
+ if (!getTelegramBotToken()) {
1685
+ setShowTelegramTokenModal(true);
1686
+ setTelegramTokenError(null);
1687
+ return;
1688
+ }
1689
+ startTelegramBridge();
1690
+ const alreadyPaired = (loadUserSettings().telegram?.approvedUserIds?.length ?? 0) > 0;
1691
+ if (!alreadyPaired) {
1692
+ setShowTelegramPairModal(true);
1693
+ setTelegramPairError(null);
1694
+ setMessages((p) => [
1695
+ ...p,
1696
+ {
1697
+ type: "assistant",
1698
+ content: "Telegram polling started. In Telegram, DM your bot and send /pair. Copy the code, then enter it below.",
1699
+ timestamp: new Date(),
1700
+ },
1701
+ ]);
1702
+ }
1703
+ else {
1704
+ setMessages((p) => [
1705
+ ...p,
1706
+ {
1707
+ type: "assistant",
1708
+ content: "Telegram polling is running. Your chat is already paired.",
1709
+ timestamp: new Date(),
1710
+ },
1711
+ ]);
1712
+ }
1713
+ }, [openApiKeyModal, startTelegramBridge]);
1714
+ const interruptActiveRun = useCallback((key) => {
1715
+ if (btwStateRef.current) {
1716
+ btwAbortRef.current?.abort();
1717
+ btwAbortRef.current = null;
1718
+ btwStateRef.current = null;
1719
+ setBtwState(null);
1720
+ key?.preventDefault();
1721
+ key?.stopPropagation();
1722
+ return true;
1723
+ }
1724
+ if (!isProcessingRef.current)
1725
+ return false;
1726
+ key?.preventDefault();
1727
+ key?.stopPropagation();
1728
+ interruptedRunIdRef.current = activeRunIdRef.current;
1729
+ queuedMessagesRef.current = [];
1730
+ setQueuedMessages([]);
1731
+ const activeAgent = activeTurnRef.current?.agent ?? agent;
1732
+ activeTurnRef.current = null;
1733
+ clearLiveTurnUi();
1734
+ activeAgent.abort();
1735
+ return true;
1736
+ }, [agent, clearLiveTurnUi]);
1737
+ useEffect(() => {
1738
+ const onInternalKey = (key) => {
1739
+ if (isEscapeKey(key)) {
1740
+ interruptActiveRun(key);
1741
+ }
1742
+ };
1743
+ renderer._internalKeyInput.onInternal("keypress", onInternalKey);
1744
+ return () => {
1745
+ renderer._internalKeyInput.offInternal("keypress", onInternalKey);
1746
+ };
1747
+ }, [interruptActiveRun, renderer]);
1748
+ useEffect(() => {
1749
+ const onRawInput = (sequence) => {
1750
+ const parsed = parseKeypress(sequence, { useKittyKeyboard: renderer.useKittyKeyboard });
1751
+ if (parsed?.name === "escape" || sequence === "\u001b" || sequence === "\u001b\u001b") {
1752
+ return interruptActiveRun();
1753
+ }
1754
+ return false;
1755
+ };
1756
+ renderer.prependInputHandler(onRawInput);
1757
+ return () => {
1758
+ renderer.removeInputHandler(onRawInput);
1759
+ };
1760
+ }, [interruptActiveRun, renderer]);
1761
+ useEffect(() => {
1762
+ const onStdinData = (chunk) => {
1763
+ const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1764
+ if (data.length === 1 && data[0] === 27) {
1765
+ interruptActiveRun();
1766
+ }
1767
+ };
1768
+ renderer.stdin.on("data", onStdinData);
1769
+ return () => {
1770
+ renderer.stdin.off("data", onStdinData);
1771
+ };
1772
+ }, [interruptActiveRun, renderer]);
1773
+ const resetToNewSession = useCallback(() => {
1774
+ const snapshot = agent.startNewSession();
1775
+ setMessages(snapshot?.entries ?? []);
1776
+ setExpandedMessages(new Set());
1777
+ activeTurnRef.current = null;
1778
+ clearLiveTurnUi();
1779
+ setSessionTitle(snapshot?.session.title ?? null);
1780
+ setSessionId(snapshot?.session.id ?? agent.getSessionId());
1781
+ setActivePlan(null);
1782
+ setPqs(initialPlanQuestionsState());
1783
+ replacePasteBlocks([]);
1784
+ queuedMessagesRef.current = [];
1785
+ setQueuedMessages([]);
1786
+ }, [agent, clearLiveTurnUi, replacePasteBlocks]);
1787
+ const processMessage = useCallback(async (text, displayText, images) => {
1788
+ if (!text.trim() || isProcessingRef.current)
1789
+ return;
1790
+ const runId = ++activeRunIdRef.current;
1791
+ const isStale = () => activeRunIdRef.current !== runId;
1792
+ isProcessingRef.current = true;
1793
+ setIsProcessing(true);
1794
+ if (!sessionTitle)
1795
+ agent
1796
+ .generateTitle((displayText ?? text).trim())
1797
+ .then(setSessionTitle)
1798
+ .catch(() => { });
1799
+ await coordinatorRef.current.run(async () => {
1800
+ const color = modeInfoRef.current.color;
1801
+ beginLiveTurn({ kind: "local", agent, modeColor: color });
1802
+ setMessages((prev) => [...prev, buildUserEntry((displayText ?? text).trim(), { modeColor: color })]);
1803
+ setTimeout(scrollToBottom, 50);
1804
+ await new Promise((r) => setTimeout(r, 0));
1805
+ let turnHadError = false;
1806
+ let turnHadAuthError = false;
1807
+ try {
1808
+ for await (const chunk of agent.processMessage(text.trim(), undefined, images)) {
1809
+ if (isStale()) {
1810
+ break;
1811
+ }
1812
+ switch (chunk.type) {
1813
+ case "content":
1814
+ applyLocalAssistantDelta(chunk.content || "");
1815
+ break;
1816
+ case "reasoning":
1817
+ setStreamReasoning((p) => p + (chunk.content || ""));
1818
+ break;
1819
+ case "tool_calls":
1820
+ if (chunk.toolCalls) {
1821
+ showLiveToolCalls(chunk.toolCalls);
1822
+ }
1823
+ break;
1824
+ case "tool_result":
1825
+ if (chunk.toolCall && chunk.toolResult) {
1826
+ appendLiveToolResult(chunk.toolCall, chunk.toolResult);
1827
+ }
1828
+ break;
1829
+ case "structured_response":
1830
+ if (chunk.structuredResponse) {
1831
+ flushPendingAssistantMessage();
1832
+ setMessages((prev) => [
1833
+ ...prev,
1834
+ {
1835
+ type: "structured_response",
1836
+ content: "",
1837
+ timestamp: new Date(),
1838
+ modeColor: activeTurnRef.current?.modeColor,
1839
+ structuredResponse: chunk.structuredResponse,
1840
+ },
1841
+ ]);
1842
+ setTimeout(scrollToBottom, 10);
1843
+ }
1844
+ break;
1845
+ case "tool_approval_request":
1846
+ if (chunk.toolCall && chunk.approvalId) {
1847
+ let args = {};
1848
+ try {
1849
+ args = JSON.parse(chunk.toolCall.function.arguments);
1850
+ }
1851
+ catch {
1852
+ /* ignore */
1853
+ }
1854
+ const pc = chunk.paymentPrecheck;
1855
+ setPendingPaymentApproval({
1856
+ url: args?.url ?? "",
1857
+ description: pc?.description ?? "",
1858
+ security: pc?.security ?? "",
1859
+ securityLabel: pc?.securityLabel ?? "",
1860
+ securityUrl: pc?.securityUrl ?? "",
1861
+ amount: pc?.amount ?? "",
1862
+ network: pc?.network ?? "",
1863
+ asset: pc?.asset ?? "",
1864
+ approvalId: chunk.approvalId,
1865
+ selected: 0,
1866
+ });
1867
+ }
1868
+ break;
1869
+ case "error":
1870
+ turnHadError = true;
1871
+ if (chunk.isAuthError) {
1872
+ turnHadAuthError = true;
1873
+ }
1874
+ contentAccRef.current += `\n${chunk.content || "Unknown error"}`;
1875
+ setStreamContent(contentAccRef.current);
1876
+ break;
1877
+ case "done":
1878
+ break;
1879
+ }
1880
+ }
1881
+ }
1882
+ catch {
1883
+ turnHadError = true;
1884
+ if (!isStale()) {
1885
+ contentAccRef.current += "\nAn unexpected error occurred.";
1886
+ setStreamContent(contentAccRef.current);
1887
+ }
1888
+ }
1889
+ const wasInterrupted = interruptedRunIdRef.current === runId;
1890
+ if (isStale()) {
1891
+ contentAccRef.current = "";
1892
+ return;
1893
+ }
1894
+ if (turnHadAuthError) {
1895
+ setApiKeyError("Your API key is invalid or expired. Please enter a new key.");
1896
+ setShowApiKeyModal(true);
1897
+ showApiKeyModalRef.current = true;
1898
+ }
1899
+ if (!isStale()) {
1900
+ finalizeActiveTurn({ wasInterrupted, hadError: turnHadError });
1901
+ }
1902
+ if (wasInterrupted) {
1903
+ interruptedRunIdRef.current = null;
1904
+ }
1905
+ });
1906
+ }, [
1907
+ agent,
1908
+ appendLiveToolResult,
1909
+ applyLocalAssistantDelta,
1910
+ beginLiveTurn,
1911
+ finalizeActiveTurn,
1912
+ scrollToBottom,
1913
+ sessionTitle,
1914
+ showLiveToolCalls,
1915
+ ]);
1916
+ useEffect(() => {
1917
+ if (initialMessage && hasApiKey && !processedInitial.current) {
1918
+ processedInitial.current = true;
1919
+ processMessage(initialMessage);
1920
+ }
1921
+ }, [hasApiKey, initialMessage, processMessage]);
1922
+ useEffect(() => {
1923
+ processMessageRef.current = processMessage;
1924
+ }, [processMessage]);
1925
+ useEffect(() => agent.onSubagentStatus((status) => {
1926
+ if (activeTurnRef.current?.agent !== agent)
1927
+ return;
1928
+ setActiveSubagent(status);
1929
+ }), [agent]);
1930
+ useEffect(() => () => {
1931
+ for (const unsubscribe of telegramSubagentUnsubsRef.current.values()) {
1932
+ unsubscribe();
1933
+ }
1934
+ telegramSubagentUnsubsRef.current.clear();
1935
+ }, []);
1936
+ useEffect(() => {
1937
+ let active = true;
1938
+ const id = setInterval(() => {
1939
+ agent
1940
+ .consumeBackgroundNotifications()
1941
+ .then((notifications) => {
1942
+ if (!active || notifications.length === 0)
1943
+ return;
1944
+ setMessages((prev) => [
1945
+ ...prev,
1946
+ ...notifications.map((message) => ({
1947
+ type: "assistant",
1948
+ content: message,
1949
+ timestamp: new Date(),
1950
+ })),
1951
+ ]);
1952
+ setTimeout(scrollToBottom, 10);
1953
+ })
1954
+ .catch(() => { });
1955
+ }, 2000);
1956
+ return () => {
1957
+ active = false;
1958
+ clearInterval(id);
1959
+ };
1960
+ }, [agent, scrollToBottom]);
1961
+ const handleCommand = useCallback((cmd) => {
1962
+ const c = cmd.trim().toLowerCase();
1963
+ if (c === "/clear") {
1964
+ resetToNewSession();
1965
+ return true;
1966
+ }
1967
+ if (c === "/model" || c === "/models") {
1968
+ setShowModelPicker(true);
1969
+ setModelPickerIndex(0);
1970
+ setModelSearchQuery("");
1971
+ return true;
1972
+ }
1973
+ if (c === "/sandbox") {
1974
+ openSandboxPicker();
1975
+ return true;
1976
+ }
1977
+ if (c === "/wallet") {
1978
+ openWalletPicker();
1979
+ return true;
1980
+ }
1981
+ if (c === "/remote-control") {
1982
+ setConnectModalIndex(0);
1983
+ setShowConnectModal(true);
1984
+ return true;
1985
+ }
1986
+ if (c === "/mcp" || c === "/mcps") {
1987
+ openMcpModal();
1988
+ return true;
1989
+ }
1990
+ if (c === "/agents" || c === "/agent") {
1991
+ openAgentsModal();
1992
+ return true;
1993
+ }
1994
+ if (c === "/schedule" || c === "/schedules") {
1995
+ openScheduleModal();
1996
+ return true;
1997
+ }
1998
+ if (c === "/quit" || c === "/exit" || c === "/q") {
1999
+ handleExit();
2000
+ return true;
2001
+ }
2002
+ if (c === "/review") {
2003
+ processMessage(REVIEW_PROMPT);
2004
+ return true;
2005
+ }
2006
+ if (c === "/verify") {
2007
+ processMessage(buildVerifyPrompt(agent.getCwd()));
2008
+ return true;
2009
+ }
2010
+ if (c === "/commit-push") {
2011
+ processMessage(COMMIT_PUSH_PROMPT);
2012
+ return true;
2013
+ }
2014
+ if (c === "/commit-pr") {
2015
+ processMessage(COMMIT_PR_PROMPT);
2016
+ return true;
2017
+ }
2018
+ if (c.startsWith("/btw ") || c === "/btw") {
2019
+ const question = cmd.trim().slice(4).trim();
2020
+ if (!question) {
2021
+ setMessages((prev) => [
2022
+ ...prev,
2023
+ buildAssistantEntry("Usage: /btw <question>\nExample: /btw what does useEffect cleanup do?"),
2024
+ ]);
2025
+ return true;
2026
+ }
2027
+ const ac = new AbortController();
2028
+ btwAbortRef.current = ac;
2029
+ const loadingState = { status: "loading", question };
2030
+ btwStateRef.current = loadingState;
2031
+ setBtwState(loadingState);
2032
+ agent
2033
+ .askSideQuestion(question, ac.signal)
2034
+ .then((result) => {
2035
+ if (ac.signal.aborted)
2036
+ return;
2037
+ const doneState = { status: "done", question, answer: result.response };
2038
+ btwStateRef.current = doneState;
2039
+ setBtwState(doneState);
2040
+ })
2041
+ .catch((err) => {
2042
+ if (ac.signal.aborted)
2043
+ return;
2044
+ const errState = {
2045
+ status: "error",
2046
+ question,
2047
+ error: err instanceof Error ? err.message : String(err),
2048
+ };
2049
+ btwStateRef.current = errState;
2050
+ setBtwState(errState);
2051
+ });
2052
+ return true;
2053
+ }
2054
+ const customSubagentCommand = parseCustomSubagentSlashCommand(cmd, subAgents);
2055
+ if (customSubagentCommand) {
2056
+ if (!customSubagentCommand.prompt) {
2057
+ setMessages((prev) => [
2058
+ ...prev,
2059
+ buildAssistantEntry(`Usage: /${customSubagentCommand.agentName} <task>\nExample: /${customSubagentCommand.agentName} review the latest changes`),
2060
+ ]);
2061
+ return true;
2062
+ }
2063
+ processMessage(buildCustomSubagentSlashPrompt(customSubagentCommand.agentName, customSubagentCommand.prompt));
2064
+ return true;
2065
+ }
2066
+ // Plan 06: fallback to slash registry (dispatchSlash) for custom commands like /route
2067
+ if (c.startsWith("/")) {
2068
+ const parts = c.slice(1).split(/\s+/);
2069
+ const name = parts[0] ?? "";
2070
+ const args = parts.slice(1);
2071
+ dispatchSlash(name, args, {
2072
+ cwd: agent.getCwd(),
2073
+ tenantId: "local",
2074
+ defaultProvider: "anthropic",
2075
+ defaultModel: model,
2076
+ lastPrompt: messages[messages.length - 1]?.content,
2077
+ }).then(async (result) => {
2078
+ if (result === null)
2079
+ return;
2080
+ if (result.startsWith("__COMPACT__")) {
2081
+ const flowDir = path.join(agent.getCwd(), ".muonroi-flow");
2082
+ try {
2083
+ const cr = await deliberateCompact(flowDir, agent.getMessages(), "", 4096);
2084
+ setMessages((prev) => [
2085
+ ...prev,
2086
+ buildAssistantEntry(`Compaction: ${cr.decisionsExtracted} decisions extracted, ${cr.tokensBeforeCompress} → ${cr.tokensAfterCompress} tokens.\nSnapshot: ${cr.historyPath}`),
2087
+ ]);
2088
+ }
2089
+ catch (e) {
2090
+ setMessages((prev) => [...prev, buildAssistantEntry(`Compaction failed: ${e}`)]);
2091
+ }
2092
+ return;
2093
+ }
2094
+ if (result.startsWith("__EXPAND__")) {
2095
+ const content = result.replace(/^__EXPAND__\n[^\n]*\n?/, "");
2096
+ setMessages((prev) => [...prev, buildAssistantEntry(`Restored session context:\n${content}`)]);
2097
+ return;
2098
+ }
2099
+ if (result.startsWith("__CLEAR__")) {
2100
+ const summary = result.replace(/^__CLEAR__\n/, "");
2101
+ agent.clearHistory();
2102
+ setMessages([buildAssistantEntry(`Session cleared and relocked.\n\n${summary}`)]);
2103
+ return;
2104
+ }
2105
+ if (result.startsWith("__COUNCIL__")) {
2106
+ const lines = result.split("\n");
2107
+ const roundsStr = lines[1] ?? "";
2108
+ const rounds = roundsStr ? parseInt(roundsStr, 10) : undefined;
2109
+ const topic = lines.slice(2).join("\n");
2110
+ setMessages((prev) => [...prev, buildUserEntry(`/council ${topic}`), buildAssistantEntry("Council convening...\n")]);
2111
+ try {
2112
+ const gen = agent.runCouncilRound(topic, undefined, rounds || undefined);
2113
+ for await (const chunk of gen) {
2114
+ if (chunk.type === "content") {
2115
+ setMessages((prev) => {
2116
+ const last = prev[prev.length - 1];
2117
+ if (last?.type === "assistant") {
2118
+ return [...prev.slice(0, -1), { ...last, content: (last.content ?? "") + chunk.content }];
2119
+ }
2120
+ return [...prev, buildAssistantEntry(chunk.content ?? "")];
2121
+ });
2122
+ }
2123
+ if (chunk.type === "done")
2124
+ break;
2125
+ }
2126
+ }
2127
+ catch (e) {
2128
+ setMessages((prev) => [...prev, buildAssistantEntry(`Council error: ${e}`)]);
2129
+ }
2130
+ return;
2131
+ }
2132
+ setMessages((prev) => [...prev, buildAssistantEntry(result)]);
2133
+ });
2134
+ return true;
2135
+ }
2136
+ return false;
2137
+ }, [
2138
+ agent,
2139
+ handleExit,
2140
+ openAgentsModal,
2141
+ openMcpModal,
2142
+ openSandboxPicker,
2143
+ openWalletPicker,
2144
+ openScheduleModal,
2145
+ processMessage,
2146
+ resetToNewSession,
2147
+ subAgents,
2148
+ model,
2149
+ messages.length,
2150
+ messages,
2151
+ ]);
2152
+ const handleSlashMenuSelect = useCallback((item) => {
2153
+ setShowSlashMenu(false);
2154
+ inputRef.current?.clear();
2155
+ switch (item.id) {
2156
+ case "new":
2157
+ resetToNewSession();
2158
+ break;
2159
+ case "models":
2160
+ setShowModelPicker(true);
2161
+ setModelPickerIndex(0);
2162
+ setModelSearchQuery("");
2163
+ break;
2164
+ case "sandbox":
2165
+ openSandboxPicker();
2166
+ break;
2167
+ case "wallet":
2168
+ openWalletPicker();
2169
+ break;
2170
+ case "remote-control":
2171
+ setConnectModalIndex(0);
2172
+ setShowConnectModal(true);
2173
+ break;
2174
+ case "exit":
2175
+ handleExit();
2176
+ break;
2177
+ case "help":
2178
+ setMessages((p) => [
2179
+ ...p,
2180
+ {
2181
+ type: "assistant",
2182
+ content: SLASH_MENU_ITEMS.map((i) => `/${i.label} — ${i.description}`).join("\n"),
2183
+ timestamp: new Date(),
2184
+ },
2185
+ ]);
2186
+ break;
2187
+ case "skills":
2188
+ setMessages((p) => [
2189
+ ...p,
2190
+ {
2191
+ type: "assistant",
2192
+ content: formatSkillsForChat(discoverSkills(agent.getCwd()), agent.getCwd()),
2193
+ timestamp: new Date(),
2194
+ },
2195
+ ]);
2196
+ break;
2197
+ case "mcp":
2198
+ openMcpModal();
2199
+ break;
2200
+ case "agents":
2201
+ openAgentsModal();
2202
+ break;
2203
+ case "schedule":
2204
+ openScheduleModal();
2205
+ break;
2206
+ case "review":
2207
+ processMessage(REVIEW_PROMPT);
2208
+ break;
2209
+ case "verify":
2210
+ processMessage(buildVerifyPrompt(agent.getCwd()));
2211
+ break;
2212
+ case "commit-push":
2213
+ processMessage(COMMIT_PUSH_PROMPT);
2214
+ break;
2215
+ case "commit-pr":
2216
+ processMessage(COMMIT_PR_PROMPT);
2217
+ break;
2218
+ case "btw":
2219
+ inputRef.current?.clear();
2220
+ inputRef.current?.insertText("/btw ");
2221
+ break;
2222
+ case "update":
2223
+ setIsUpdating(true);
2224
+ setUpdateOutput(null);
2225
+ runUpdate(startupConfig.version).then((result) => {
2226
+ setIsUpdating(false);
2227
+ setUpdateOutput(result.success ? result.output : `Update failed: ${result.output}`);
2228
+ });
2229
+ break;
2230
+ case "clear":
2231
+ agent.clearHistory();
2232
+ resetToNewSession();
2233
+ break;
2234
+ default: {
2235
+ // Dispatch to slash registry for registered commands (compact, cost, ee, route, plan, execute, discuss, expand, optimize, debug)
2236
+ dispatchSlash(item.id, [], {
2237
+ cwd: agent.getCwd(),
2238
+ tenantId: "local",
2239
+ defaultProvider: "anthropic",
2240
+ defaultModel: model,
2241
+ lastPrompt: messages[messages.length - 1]?.content,
2242
+ }).then(async (result) => {
2243
+ if (result === null)
2244
+ return;
2245
+ if (result.startsWith("__COMPACT__")) {
2246
+ const flowDir = path.join(agent.getCwd(), ".muonroi-flow");
2247
+ try {
2248
+ const cr = await deliberateCompact(flowDir, agent.getMessages(), "", 4096);
2249
+ setMessages((prev) => [
2250
+ ...prev,
2251
+ buildAssistantEntry(`Compaction: ${cr.decisionsExtracted} decisions extracted, ${cr.tokensBeforeCompress} → ${cr.tokensAfterCompress} tokens.\nSnapshot: ${cr.historyPath}`),
2252
+ ]);
2253
+ }
2254
+ catch (e) {
2255
+ setMessages((prev) => [...prev, buildAssistantEntry(`Compaction failed: ${e}`)]);
2256
+ }
2257
+ return;
2258
+ }
2259
+ if (result.startsWith("__EXPAND__")) {
2260
+ const content = result.replace(/^__EXPAND__\n[^\n]*\n?/, "");
2261
+ setMessages((prev) => [...prev, buildAssistantEntry(`Restored session context:\n${content}`)]);
2262
+ return;
2263
+ }
2264
+ if (result.startsWith("__CLEAR__")) {
2265
+ const summary = result.replace(/^__CLEAR__\n/, "");
2266
+ agent.clearHistory();
2267
+ setMessages([buildAssistantEntry(`Session cleared and relocked.\n\n${summary}`)]);
2268
+ return;
2269
+ }
2270
+ if (result.startsWith("__COUNCIL__")) {
2271
+ const topic = result.replace(/^__COUNCIL__\n/, "");
2272
+ setMessages((prev) => [...prev, buildUserEntry(`/council ${topic}`), buildAssistantEntry("Council convening...\n")]);
2273
+ try {
2274
+ const gen = agent.runCouncilRound(topic);
2275
+ for await (const chunk of gen) {
2276
+ if (chunk.type === "content") {
2277
+ setMessages((prev) => {
2278
+ const last = prev[prev.length - 1];
2279
+ if (last?.type === "assistant") {
2280
+ return [...prev.slice(0, -1), { ...last, content: (last.content ?? "") + chunk.content }];
2281
+ }
2282
+ return [...prev, buildAssistantEntry(chunk.content ?? "")];
2283
+ });
2284
+ }
2285
+ if (chunk.type === "done")
2286
+ break;
2287
+ }
2288
+ }
2289
+ catch (e) {
2290
+ setMessages((prev) => [...prev, buildAssistantEntry(`Council error: ${e}`)]);
2291
+ }
2292
+ return;
2293
+ }
2294
+ setMessages((prev) => [...prev, buildAssistantEntry(result)]);
2295
+ });
2296
+ break;
2297
+ }
2298
+ }
2299
+ }, [
2300
+ agent,
2301
+ handleExit,
2302
+ model,
2303
+ messages,
2304
+ openAgentsModal,
2305
+ openMcpModal,
2306
+ openSandboxPicker,
2307
+ openWalletPicker,
2308
+ openScheduleModal,
2309
+ processMessage,
2310
+ resetToNewSession,
2311
+ startupConfig.version,
2312
+ ]);
2313
+ const blockPrompt = showConnectModal ||
2314
+ showTelegramTokenModal ||
2315
+ showTelegramPairModal ||
2316
+ showMcpModal ||
2317
+ showSandboxPicker ||
2318
+ showWalletPicker ||
2319
+ !!pendingPaymentApproval ||
2320
+ showScheduleModal ||
2321
+ showAgentsModal ||
2322
+ showAgentsEditor ||
2323
+ showUpdateModal;
2324
+ const showPlanPanel = !!activePlan?.questions?.length;
2325
+ const planQuestions = activePlan?.questions ?? [];
2326
+ const isSinglePlan = planQuestions.length === 1 && planQuestions[0]?.type !== "multiselect";
2327
+ const planTabCount = isSinglePlan ? 1 : planQuestions.length + 1;
2328
+ const isPlanConfirmTab = !isSinglePlan && pqs.tab === planQuestions.length;
2329
+ const dismissPlan = useCallback(() => {
2330
+ setActivePlan(null);
2331
+ setPqs(initialPlanQuestionsState());
2332
+ }, []);
2333
+ const submitPlanAnswers = useCallback(() => {
2334
+ if (!activePlan?.questions?.length)
2335
+ return;
2336
+ const text = formatPlanAnswers(activePlan.questions, pqs.answers);
2337
+ setActivePlan(null);
2338
+ setPqs(initialPlanQuestionsState());
2339
+ processMessage(text);
2340
+ }, [activePlan, pqs.answers, processMessage]);
2341
+ const handlePlanSelect = useCallback((q, idx, options, showCustom) => {
2342
+ const isCustom = showCustom && idx === options.length;
2343
+ if (isCustom) {
2344
+ if (q.type === "multiselect") {
2345
+ const customVal = pqs.customInputs[q.id] ?? "";
2346
+ if (customVal) {
2347
+ const existing = pqs.answers[q.id] ?? [];
2348
+ if (existing.includes(customVal)) {
2349
+ setPqs((s) => ({ ...s, answers: { ...s.answers, [q.id]: existing.filter((x) => x !== customVal) } }));
2350
+ }
2351
+ else {
2352
+ setPqs((s) => ({ ...s, editing: true }));
2353
+ }
2354
+ }
2355
+ else {
2356
+ setPqs((s) => ({ ...s, editing: true }));
2357
+ }
2358
+ }
2359
+ else {
2360
+ setPqs((s) => ({ ...s, editing: true }));
2361
+ }
2362
+ return;
2363
+ }
2364
+ const opt = options[idx];
2365
+ if (!opt)
2366
+ return;
2367
+ if (q.type === "multiselect") {
2368
+ setPqs((s) => {
2369
+ const existing = s.answers[q.id] ?? [];
2370
+ const next = existing.includes(opt.id) ? existing.filter((x) => x !== opt.id) : [...existing, opt.id];
2371
+ return { ...s, answers: { ...s.answers, [q.id]: next } };
2372
+ });
2373
+ }
2374
+ else {
2375
+ setPqs((s) => ({ ...s, answers: { ...s.answers, [q.id]: opt.id } }));
2376
+ if (isSinglePlan) {
2377
+ submitPlanAnswers();
2378
+ return;
2379
+ }
2380
+ setPqs((s) => ({ ...s, tab: s.tab + 1, selected: 0 }));
2381
+ }
2382
+ }, [pqs, isSinglePlan, submitPlanAnswers]);
2383
+ const dismissBtw = useCallback(() => {
2384
+ btwAbortRef.current?.abort();
2385
+ btwAbortRef.current = null;
2386
+ btwStateRef.current = null;
2387
+ setBtwState(null);
2388
+ }, []);
2389
+ const handleKey = useCallback((key) => {
2390
+ if (btwState) {
2391
+ if (isEscapeKey(key) || key.name === "return") {
2392
+ dismissBtw();
2393
+ }
2394
+ return;
2395
+ }
2396
+ if (showPlanPanel) {
2397
+ const q = planQuestions[pqs.tab];
2398
+ // Escape always dismisses
2399
+ if (isEscapeKey(key)) {
2400
+ dismissPlan();
2401
+ return;
2402
+ }
2403
+ // When editing custom text input
2404
+ if (pqs.editing && !isPlanConfirmTab) {
2405
+ if (key.name === "return") {
2406
+ const qId = q?.id;
2407
+ if (qId) {
2408
+ const text = (pqs.customInputs[qId] ?? "").trim();
2409
+ if (text) {
2410
+ if (q.type === "multiselect") {
2411
+ const existing = pqs.answers[qId] ?? [];
2412
+ const next = existing.includes(text) ? existing : [...existing, text];
2413
+ setPqs((s) => ({ ...s, editing: false, answers: { ...s.answers, [qId]: next } }));
2414
+ }
2415
+ else if (q.type === "text") {
2416
+ setPqs((s) => ({ ...s, editing: false, answers: { ...s.answers, [qId]: text } }));
2417
+ if (isSinglePlan) {
2418
+ submitPlanAnswers();
2419
+ return;
2420
+ }
2421
+ setPqs((s) => ({ ...s, tab: s.tab + 1, selected: 0 }));
2422
+ }
2423
+ else {
2424
+ setPqs((s) => ({ ...s, editing: false, answers: { ...s.answers, [qId]: text } }));
2425
+ if (isSinglePlan) {
2426
+ submitPlanAnswers();
2427
+ return;
2428
+ }
2429
+ setPqs((s) => ({ ...s, tab: s.tab + 1, selected: 0 }));
2430
+ }
2431
+ }
2432
+ else {
2433
+ setPqs((s) => ({ ...s, editing: false }));
2434
+ }
2435
+ }
2436
+ return;
2437
+ }
2438
+ if (key.name === "backspace") {
2439
+ const qId = q?.id;
2440
+ if (qId)
2441
+ setPqs((s) => ({
2442
+ ...s,
2443
+ customInputs: { ...s.customInputs, [qId]: (s.customInputs[qId] ?? "").slice(0, -1) },
2444
+ }));
2445
+ return;
2446
+ }
2447
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
2448
+ const qId = q?.id;
2449
+ if (qId)
2450
+ setPqs((s) => ({
2451
+ ...s,
2452
+ customInputs: { ...s.customInputs, [qId]: (s.customInputs[qId] ?? "") + key.sequence },
2453
+ }));
2454
+ return;
2455
+ }
2456
+ return;
2457
+ }
2458
+ // Tab / left / right — switch between question tabs
2459
+ if (key.name === "tab") {
2460
+ const dir = key.shift ? -1 : 1;
2461
+ setPqs((s) => ({ ...s, tab: (s.tab + dir + planTabCount) % planTabCount, selected: 0 }));
2462
+ return;
2463
+ }
2464
+ if (key.name === "left" || key.name === "h") {
2465
+ setPqs((s) => ({ ...s, tab: (s.tab - 1 + planTabCount) % planTabCount, selected: 0 }));
2466
+ return;
2467
+ }
2468
+ if (key.name === "right" || key.name === "l") {
2469
+ setPqs((s) => ({ ...s, tab: (s.tab + 1) % planTabCount, selected: 0 }));
2470
+ return;
2471
+ }
2472
+ // Confirm tab
2473
+ if (isPlanConfirmTab) {
2474
+ if (key.name === "return") {
2475
+ submitPlanAnswers();
2476
+ return;
2477
+ }
2478
+ return;
2479
+ }
2480
+ if (!q)
2481
+ return;
2482
+ // Text-only question (no options)
2483
+ if (q.type === "text") {
2484
+ setPqs((s) => ({ ...s, editing: true }));
2485
+ return;
2486
+ }
2487
+ // Up/down — navigate options
2488
+ const options = q.options ?? [];
2489
+ const showCustom = true;
2490
+ const totalItems = options.length + 1;
2491
+ if (key.name === "up" || key.name === "k") {
2492
+ setPqs((s) => ({ ...s, selected: (s.selected - 1 + totalItems) % totalItems }));
2493
+ return;
2494
+ }
2495
+ if (key.name === "down" || key.name === "j") {
2496
+ setPqs((s) => ({ ...s, selected: (s.selected + 1) % totalItems }));
2497
+ return;
2498
+ }
2499
+ // Number keys 1-9 for quick selection
2500
+ const digit = Number(key.name);
2501
+ if (!Number.isNaN(digit) && digit >= 1 && digit <= Math.min(totalItems, 9)) {
2502
+ const idx = digit - 1;
2503
+ setPqs((s) => ({ ...s, selected: idx }));
2504
+ handlePlanSelect(q, idx, options, showCustom);
2505
+ return;
2506
+ }
2507
+ // Enter — select current option
2508
+ if (key.name === "return") {
2509
+ handlePlanSelect(q, pqs.selected, options, showCustom);
2510
+ return;
2511
+ }
2512
+ return;
2513
+ }
2514
+ if (showUpdateModalRef.current) {
2515
+ if (isEscapeKey(key)) {
2516
+ setShowUpdateModal(false);
2517
+ return;
2518
+ }
2519
+ if (key.name === "return") {
2520
+ setIsUpdating(true);
2521
+ setShowUpdateModal(false);
2522
+ runUpdate(startupConfig.version).then((result) => {
2523
+ setIsUpdating(false);
2524
+ setUpdateOutput(result.output);
2525
+ });
2526
+ return;
2527
+ }
2528
+ return;
2529
+ }
2530
+ if (showMcpEditorRef.current) {
2531
+ if (isEscapeKey(key)) {
2532
+ setShowMcpEditor(false);
2533
+ setMcpEditorError(null);
2534
+ setMcpSearchQuery("");
2535
+ return;
2536
+ }
2537
+ if (key.name === "return") {
2538
+ submitMcpEditor();
2539
+ return;
2540
+ }
2541
+ if (mcpEditorField === "transport" && (key.name === "left" || key.name === "right")) {
2542
+ cycleMcpEditorTransport(key.name === "left" ? -1 : 1);
2543
+ return;
2544
+ }
2545
+ if (key.name === "tab") {
2546
+ const idx = mcpEditorFields.indexOf(mcpEditorField);
2547
+ const nextIdx = (idx + (key.shift ? -1 : 1) + mcpEditorFields.length) % mcpEditorFields.length;
2548
+ setMcpEditorField(mcpEditorFields[nextIdx]);
2549
+ return;
2550
+ }
2551
+ if (mcpEditorField === "transport") {
2552
+ return;
2553
+ }
2554
+ }
2555
+ if (showAgentsEditorRef.current) {
2556
+ if (isEscapeKey(key)) {
2557
+ setShowAgentsEditor(false);
2558
+ setAgentsEditorError(null);
2559
+ return;
2560
+ }
2561
+ if (key.name === "x" && key.ctrl && editingSubagent) {
2562
+ removeEditingSubagent();
2563
+ return;
2564
+ }
2565
+ if (key.name === "return") {
2566
+ submitSubagentEditor();
2567
+ return;
2568
+ }
2569
+ if (agentsEditorField === "model" &&
2570
+ (key.name === "up" ||
2571
+ key.name === "down" ||
2572
+ key.name === "left" ||
2573
+ key.name === "right" ||
2574
+ key.name === "j" ||
2575
+ key.name === "k")) {
2576
+ const decrement = key.name === "up" || key.name === "left" || key.name === "k";
2577
+ setAgentsEditorModelIndex((index) => decrement ? Math.max(0, index - 1) : Math.min(MODELS.length - 1, index + 1));
2578
+ return;
2579
+ }
2580
+ if (key.name === "tab") {
2581
+ const index = SUBAGENT_EDITOR_FIELDS.indexOf(agentsEditorField);
2582
+ const nextIndex = (index + (key.shift ? -1 : 1) + SUBAGENT_EDITOR_FIELDS.length) % SUBAGENT_EDITOR_FIELDS.length;
2583
+ setAgentsEditorField(SUBAGENT_EDITOR_FIELDS[nextIndex]);
2584
+ return;
2585
+ }
2586
+ if (agentsEditorField === "model") {
2587
+ return;
2588
+ }
2589
+ }
2590
+ if (showMcpModalRef.current) {
2591
+ const row = mcpRows[mcpModalIndex];
2592
+ if (isEscapeKey(key)) {
2593
+ setShowMcpEditor(false);
2594
+ setShowMcpModal(false);
2595
+ setMcpSearchQuery("");
2596
+ setEditingMcpId(null);
2597
+ setMcpEditorError(null);
2598
+ return;
2599
+ }
2600
+ if (key.name === "up") {
2601
+ setMcpModalIndex((i) => Math.max(0, i - 1));
2602
+ return;
2603
+ }
2604
+ if (key.name === "down") {
2605
+ setMcpModalIndex((i) => Math.min(mcpRows.length - 1, i + 1));
2606
+ return;
2607
+ }
2608
+ if (key.name === "return") {
2609
+ if (row?.kind === "server") {
2610
+ toggleSavedMcp(row.server);
2611
+ }
2612
+ else if (row?.kind === "catalog") {
2613
+ openCatalogMcp(row.entry);
2614
+ }
2615
+ else {
2616
+ openMcpEditor(createEmptyMcpEditorDraft());
2617
+ }
2618
+ return;
2619
+ }
2620
+ if (key.name === "a" && key.ctrl) {
2621
+ openMcpEditor(createEmptyMcpEditorDraft());
2622
+ return;
2623
+ }
2624
+ if (key.name === "e" && key.ctrl && row?.kind === "server") {
2625
+ editSavedMcp(row.server);
2626
+ return;
2627
+ }
2628
+ if (key.name === "x" && key.ctrl && row?.kind === "server") {
2629
+ deleteSavedMcp(row.server);
2630
+ return;
2631
+ }
2632
+ if (key.name === "backspace") {
2633
+ setMcpSearchQuery((q) => q.slice(0, -1));
2634
+ setMcpModalIndex(0);
2635
+ return;
2636
+ }
2637
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
2638
+ setMcpSearchQuery((q) => q + key.sequence);
2639
+ setMcpModalIndex(0);
2640
+ return;
2641
+ }
2642
+ return;
2643
+ }
2644
+ if (showScheduleModalRef.current) {
2645
+ const row = scheduleRows[scheduleModalIndex];
2646
+ if (isEscapeKey(key)) {
2647
+ setShowScheduleModal(false);
2648
+ setScheduleSearchQuery("");
2649
+ return;
2650
+ }
2651
+ if (key.name === "up") {
2652
+ setScheduleModalIndex((index) => Math.max(0, index - 1));
2653
+ return;
2654
+ }
2655
+ if (key.name === "down") {
2656
+ setScheduleModalIndex((index) => Math.min(Math.max(0, scheduleRows.length - 1), index + 1));
2657
+ return;
2658
+ }
2659
+ if (key.name === "return") {
2660
+ if (row?.kind === "schedule") {
2661
+ showScheduleDetails(row.schedule);
2662
+ }
2663
+ return;
2664
+ }
2665
+ if (key.name === "x" && key.ctrl && row?.kind === "schedule") {
2666
+ removeSchedule(row.schedule);
2667
+ return;
2668
+ }
2669
+ if (key.name === "backspace") {
2670
+ setScheduleSearchQuery((query) => query.slice(0, -1));
2671
+ setScheduleModalIndex(0);
2672
+ return;
2673
+ }
2674
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
2675
+ setScheduleSearchQuery((query) => query + key.sequence);
2676
+ setScheduleModalIndex(0);
2677
+ return;
2678
+ }
2679
+ return;
2680
+ }
2681
+ if (showAgentsModalRef.current && !showAgentsEditorRef.current) {
2682
+ const row = agentRows[agentsModalIndex];
2683
+ if (isEscapeKey(key)) {
2684
+ setShowAgentsModal(false);
2685
+ setShowAgentsEditor(false);
2686
+ setAgentsSearchQuery("");
2687
+ setEditingSubagent(null);
2688
+ setAgentsEditorError(null);
2689
+ return;
2690
+ }
2691
+ if (key.name === "up") {
2692
+ setAgentsModalIndex((index) => Math.max(0, index - 1));
2693
+ return;
2694
+ }
2695
+ if (key.name === "down") {
2696
+ setAgentsModalIndex((index) => Math.min(Math.max(0, agentRows.length - 1), index + 1));
2697
+ return;
2698
+ }
2699
+ if (key.name === "return") {
2700
+ if (row?.kind === "agent") {
2701
+ openSubagentEditor(row.agent);
2702
+ }
2703
+ return;
2704
+ }
2705
+ if (key.name === "a" && key.ctrl) {
2706
+ openSubagentEditor(null);
2707
+ return;
2708
+ }
2709
+ if (key.name === "backspace") {
2710
+ setAgentsSearchQuery((query) => query.slice(0, -1));
2711
+ setAgentsModalIndex(0);
2712
+ return;
2713
+ }
2714
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
2715
+ setAgentsSearchQuery((query) => query + key.sequence);
2716
+ setAgentsModalIndex(0);
2717
+ return;
2718
+ }
2719
+ return;
2720
+ }
2721
+ if (showTelegramTokenModalRef.current) {
2722
+ if (isEscapeKey(key)) {
2723
+ setShowTelegramTokenModal(false);
2724
+ setTelegramTokenError(null);
2725
+ return;
2726
+ }
2727
+ if (key.name === "return") {
2728
+ submitTelegramToken();
2729
+ }
2730
+ return;
2731
+ }
2732
+ if (showTelegramPairModalRef.current) {
2733
+ if (isEscapeKey(key)) {
2734
+ setShowTelegramPairModal(false);
2735
+ setTelegramPairError(null);
2736
+ return;
2737
+ }
2738
+ if (key.name === "return") {
2739
+ void submitTelegramPair();
2740
+ }
2741
+ return;
2742
+ }
2743
+ if (showConnectModalRef.current) {
2744
+ if (isEscapeKey(key)) {
2745
+ setShowConnectModal(false);
2746
+ return;
2747
+ }
2748
+ if (key.name === "up") {
2749
+ setConnectModalIndex((i) => Math.max(0, i - 1));
2750
+ return;
2751
+ }
2752
+ if (key.name === "down") {
2753
+ setConnectModalIndex((i) => Math.min(CONNECT_CHANNELS.length - 1, i + 1));
2754
+ return;
2755
+ }
2756
+ if (key.name === "return") {
2757
+ const ch = CONNECT_CHANNELS[connectModalIndex];
2758
+ if (ch?.id === "telegram")
2759
+ beginTelegramFromConnect();
2760
+ return;
2761
+ }
2762
+ return;
2763
+ }
2764
+ if (showApiKeyModalRef.current) {
2765
+ if (isEscapeKey(key)) {
2766
+ closeApiKeyModal();
2767
+ return;
2768
+ }
2769
+ if (key.name === "return") {
2770
+ submitApiKey();
2771
+ }
2772
+ return;
2773
+ }
2774
+ if (showSlashMenu) {
2775
+ if (isEscapeKey(key)) {
2776
+ setShowSlashMenu(false);
2777
+ setSlashSearchQuery("");
2778
+ inputRef.current?.clear();
2779
+ return;
2780
+ }
2781
+ if (key.name === "up") {
2782
+ setSlashMenuIndex((i) => Math.max(0, i - 1));
2783
+ return;
2784
+ }
2785
+ if (key.name === "down") {
2786
+ setSlashMenuIndex((i) => Math.min(filteredSlashItems.length - 1, i + 1));
2787
+ return;
2788
+ }
2789
+ if (key.name === "return") {
2790
+ const item = filteredSlashItems[slashMenuIndex];
2791
+ if (item)
2792
+ handleSlashMenuSelect(item);
2793
+ setSlashSearchQuery("");
2794
+ return;
2795
+ }
2796
+ if (key.name === "backspace") {
2797
+ setSlashSearchQuery((q) => q.slice(0, -1));
2798
+ setSlashMenuIndex(0);
2799
+ return;
2800
+ }
2801
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
2802
+ setSlashSearchQuery((q) => q + key.sequence);
2803
+ setSlashMenuIndex(0);
2804
+ return;
2805
+ }
2806
+ return;
2807
+ }
2808
+ if (showModelPicker) {
2809
+ if (isEscapeKey(key)) {
2810
+ setShowModelPicker(false);
2811
+ setModelSearchQuery("");
2812
+ return;
2813
+ }
2814
+ if (key.name === "up") {
2815
+ setModelPickerIndex((i) => Math.max(0, i - 1));
2816
+ return;
2817
+ }
2818
+ if (key.name === "down") {
2819
+ setModelPickerIndex((i) => Math.min(filteredModelIds.length - 1, i + 1));
2820
+ return;
2821
+ }
2822
+ if (key.name === "left" || key.name === "right") {
2823
+ const sel = filteredModelIds[modelPickerIndex];
2824
+ if (sel) {
2825
+ adjustModelReasoningEffort(sel, key.name === "left" ? -1 : 1);
2826
+ }
2827
+ return;
2828
+ }
2829
+ if (key.name === "return") {
2830
+ const sel = filteredModelIds[modelPickerIndex];
2831
+ if (sel) {
2832
+ agent.setModel(sel);
2833
+ setModel(sel);
2834
+ statusBarStore.setState({ model: sel, provider: agent.getProviderId() });
2835
+ saveProjectSettings({ model: sel });
2836
+ saveUserSettings({ defaultModel: sel });
2837
+ }
2838
+ setShowModelPicker(false);
2839
+ setModelSearchQuery("");
2840
+ return;
2841
+ }
2842
+ if (key.name === "backspace") {
2843
+ setModelSearchQuery((q) => q.slice(0, -1));
2844
+ setModelPickerIndex(0);
2845
+ return;
2846
+ }
2847
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
2848
+ setModelSearchQuery((q) => q + key.sequence);
2849
+ setModelPickerIndex(0);
2850
+ return;
2851
+ }
2852
+ return;
2853
+ }
2854
+ if (pendingPaymentApproval) {
2855
+ if (isEscapeKey(key)) {
2856
+ setPendingPaymentApproval(null);
2857
+ return;
2858
+ }
2859
+ if (key.name === "up" || key.name === "down") {
2860
+ setPendingPaymentApproval((p) => (p ? { ...p, selected: p.selected === 0 ? 1 : 0 } : p));
2861
+ return;
2862
+ }
2863
+ if (key.name === "return") {
2864
+ const approved = pendingPaymentApproval.selected === 0;
2865
+ const aid = pendingPaymentApproval.approvalId;
2866
+ setPendingPaymentApproval(null);
2867
+ if (aid) {
2868
+ agent.respondToToolApproval(aid, approved);
2869
+ if (approved) {
2870
+ processMessage("[Payment approved]");
2871
+ }
2872
+ }
2873
+ return;
2874
+ }
2875
+ return;
2876
+ }
2877
+ if (showWalletPicker) {
2878
+ if (isEscapeKey(key)) {
2879
+ setShowWalletPicker(false);
2880
+ return;
2881
+ }
2882
+ if (key.name === "up") {
2883
+ setWalletFocusIndex((i) => Math.max(0, i - 1));
2884
+ return;
2885
+ }
2886
+ if (key.name === "down") {
2887
+ setWalletFocusIndex((i) => Math.min(WALLET_ROWS.length - 1, i + 1));
2888
+ return;
2889
+ }
2890
+ const focusedWalletRow = WALLET_ROWS[walletFocusIndex];
2891
+ if (!focusedWalletRow || focusedWalletRow.type === "readonly")
2892
+ return;
2893
+ if (key.name === "left" || key.name === "right") {
2894
+ const options = focusedWalletRow.getOptions();
2895
+ const current = focusedWalletRow.getDisplay(walletSettings, walletDisplayInfo);
2896
+ const idx = options.indexOf(current);
2897
+ const next = key.name === "right" ? options[Math.min(options.length - 1, idx + 1)] : options[Math.max(0, idx - 1)];
2898
+ if (next && next !== current && focusedWalletRow.apply) {
2899
+ const patch = focusedWalletRow.apply(walletSettings, next);
2900
+ applyWalletSettings({ ...walletSettings, ...patch });
2901
+ }
2902
+ return;
2903
+ }
2904
+ if (key.name === "return") {
2905
+ const options = focusedWalletRow.getOptions();
2906
+ const current = focusedWalletRow.getDisplay(walletSettings, walletDisplayInfo);
2907
+ const idx = options.indexOf(current);
2908
+ const next = options[(idx + 1) % options.length];
2909
+ if (next && focusedWalletRow.apply) {
2910
+ const patch = focusedWalletRow.apply(walletSettings, next);
2911
+ applyWalletSettings({ ...walletSettings, ...patch });
2912
+ }
2913
+ return;
2914
+ }
2915
+ return;
2916
+ }
2917
+ if (showSandboxPicker) {
2918
+ const visibleRows = getSandboxVisibleRows(sandboxMode);
2919
+ if (sandboxSettingsEditing) {
2920
+ if (isEscapeKey(key)) {
2921
+ setSandboxSettingsEditing(null);
2922
+ setSandboxSettingsEditBuffer("");
2923
+ return;
2924
+ }
2925
+ if (key.name === "return") {
2926
+ const row = visibleRows.find((r) => r.key === sandboxSettingsEditing);
2927
+ if (row) {
2928
+ const result = row.apply(sandboxMode, sandboxSettings, sandboxSettingsEditBuffer.trim());
2929
+ if (result.mode !== undefined)
2930
+ applySandboxMode(result.mode);
2931
+ if (result.settings)
2932
+ applySandboxSettings({ ...sandboxSettings, ...result.settings });
2933
+ }
2934
+ setSandboxSettingsEditing(null);
2935
+ setSandboxSettingsEditBuffer("");
2936
+ return;
2937
+ }
2938
+ if (key.name === "backspace") {
2939
+ setSandboxSettingsEditBuffer((b) => b.slice(0, -1));
2940
+ return;
2941
+ }
2942
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta) {
2943
+ setSandboxSettingsEditBuffer((b) => b + key.sequence);
2944
+ return;
2945
+ }
2946
+ return;
2947
+ }
2948
+ if (isEscapeKey(key)) {
2949
+ setShowSandboxPicker(false);
2950
+ return;
2951
+ }
2952
+ if (key.name === "up") {
2953
+ setSandboxSettingsFocusIndex((i) => Math.max(0, i - 1));
2954
+ return;
2955
+ }
2956
+ if (key.name === "down") {
2957
+ setSandboxSettingsFocusIndex((i) => Math.min(visibleRows.length - 1, i + 1));
2958
+ return;
2959
+ }
2960
+ const focusedRow = visibleRows[sandboxSettingsFocusIndex];
2961
+ if (!focusedRow)
2962
+ return;
2963
+ if (focusedRow.type === "toggle" && (key.name === "left" || key.name === "right")) {
2964
+ const options = focusedRow.getOptions();
2965
+ const current = focusedRow.getDisplay(sandboxMode, sandboxSettings);
2966
+ const idx = options.indexOf(current);
2967
+ const next = key.name === "right" ? options[Math.min(options.length - 1, idx + 1)] : options[Math.max(0, idx - 1)];
2968
+ if (next && next !== current) {
2969
+ const result = focusedRow.apply(sandboxMode, sandboxSettings, next);
2970
+ if (result.mode !== undefined)
2971
+ applySandboxMode(result.mode);
2972
+ if (result.settings)
2973
+ applySandboxSettings({ ...sandboxSettings, ...result.settings });
2974
+ }
2975
+ return;
2976
+ }
2977
+ if (key.name === "return") {
2978
+ if (focusedRow.type === "toggle") {
2979
+ const options = focusedRow.getOptions();
2980
+ const current = focusedRow.getDisplay(sandboxMode, sandboxSettings);
2981
+ const idx = options.indexOf(current);
2982
+ const next = options[(idx + 1) % options.length];
2983
+ const result = focusedRow.apply(sandboxMode, sandboxSettings, next);
2984
+ if (result.mode !== undefined)
2985
+ applySandboxMode(result.mode);
2986
+ if (result.settings)
2987
+ applySandboxSettings({ ...sandboxSettings, ...result.settings });
2988
+ }
2989
+ else {
2990
+ setSandboxSettingsEditing(focusedRow.key);
2991
+ const current = sandboxSettings[focusedRow.key];
2992
+ setSandboxSettingsEditBuffer(Array.isArray(current) ? current.join(", ") : current != null ? String(current) : "");
2993
+ }
2994
+ return;
2995
+ }
2996
+ return;
2997
+ }
2998
+ if (isEscapeKey(key) && interruptActiveRun(key)) {
2999
+ return;
3000
+ }
3001
+ if (!hasApiKeyRef.current && shouldOpenApiKeyModalForKey(key)) {
3002
+ openApiKeyModal();
3003
+ return;
3004
+ }
3005
+ if (key.sequence === "/" && !isProcessing) {
3006
+ const text = inputRef.current?.plainText || "";
3007
+ if (!text.trim()) {
3008
+ setShowSlashMenu(true);
3009
+ setSlashMenuIndex(0);
3010
+ setSlashSearchQuery("");
3011
+ return;
3012
+ }
3013
+ }
3014
+ if (key.name === "e" && key.ctrl) {
3015
+ let lastUserIdx = -1;
3016
+ for (let i = messages.length - 1; i >= 0; i--) {
3017
+ if (messages[i].type === "user") {
3018
+ lastUserIdx = i;
3019
+ break;
3020
+ }
3021
+ }
3022
+ if (lastUserIdx >= 0) {
3023
+ setExpandedMessages((prev) => {
3024
+ const next = new Set(prev);
3025
+ if (next.has(lastUserIdx))
3026
+ next.delete(lastUserIdx);
3027
+ else
3028
+ next.add(lastUserIdx);
3029
+ return next;
3030
+ });
3031
+ }
3032
+ return;
3033
+ }
3034
+ if (key.name === "c" && key.ctrl && key.shift) {
3035
+ if (copyTuiSelectionToHost()) {
3036
+ key.preventDefault();
3037
+ key.stopPropagation();
3038
+ }
3039
+ return;
3040
+ }
3041
+ if (key.name === "y" && key.ctrl && copyTuiSelectionToHost()) {
3042
+ key.preventDefault();
3043
+ key.stopPropagation();
3044
+ return;
3045
+ }
3046
+ // ⌘C: Kitty / iTerm report Command as `super`; some setups use `meta` instead.
3047
+ if (key.name === "c" && !key.ctrl && (key.meta || key.super)) {
3048
+ if (copyTuiSelectionToHost()) {
3049
+ key.preventDefault();
3050
+ key.stopPropagation();
3051
+ return;
3052
+ }
3053
+ }
3054
+ // Alt+V: paste image from clipboard (like Claude Code / Codex / Gemini CLI)
3055
+ if (key.name === "v" && (key.meta || key.option)) {
3056
+ const clip = readClipboardImage();
3057
+ if (clip) {
3058
+ const id = ++pasteCounterRef.current;
3059
+ const block = { id, content: `__clipboard_image_${id}__`, lines: 1, isImage: true, clipboardBase64: clip.base64, clipboardMediaType: clip.mediaType };
3060
+ replacePasteBlocks([...pasteBlocksRef.current, block]);
3061
+ inputRef.current?.insertText(getPasteBlockToken(block));
3062
+ }
3063
+ key.preventDefault();
3064
+ key.stopPropagation();
3065
+ return;
3066
+ }
3067
+ if (key.name === "c" && key.ctrl) {
3068
+ if (copyTuiSelectionToHost()) {
3069
+ key.preventDefault();
3070
+ key.stopPropagation();
3071
+ return;
3072
+ }
3073
+ const text = inputRef.current?.plainText || "";
3074
+ if (text.trim()) {
3075
+ inputRef.current?.clear();
3076
+ replacePasteBlocks([]);
3077
+ }
3078
+ else {
3079
+ handleExit();
3080
+ }
3081
+ return;
3082
+ }
3083
+ if (typeaheadRef.current.visible) {
3084
+ if (key.name === "up") {
3085
+ typeaheadRef.current.navigateUp();
3086
+ return;
3087
+ }
3088
+ if (key.name === "down") {
3089
+ typeaheadRef.current.navigateDown();
3090
+ return;
3091
+ }
3092
+ if (key.name === "tab" || key.name === "return") {
3093
+ key.preventDefault();
3094
+ key.stopPropagation();
3095
+ typeaheadRef.current.accept();
3096
+ return;
3097
+ }
3098
+ if (isEscapeKey(key)) {
3099
+ typeaheadRef.current.dismiss();
3100
+ return;
3101
+ }
3102
+ }
3103
+ if (key.name === "tab" && !isProcessing) {
3104
+ cycleMode();
3105
+ return;
3106
+ }
3107
+ }, [
3108
+ agent,
3109
+ agentRows,
3110
+ agentsEditorField,
3111
+ agentsModalIndex,
3112
+ beginTelegramFromConnect,
3113
+ btwState,
3114
+ closeApiKeyModal,
3115
+ connectModalIndex,
3116
+ cycleMode,
3117
+ cycleMcpEditorTransport,
3118
+ deleteSavedMcp,
3119
+ dismissBtw,
3120
+ dismissPlan,
3121
+ editingSubagent,
3122
+ editSavedMcp,
3123
+ adjustModelReasoningEffort,
3124
+ filteredModelIds,
3125
+ filteredSlashItems,
3126
+ handleExit,
3127
+ handlePlanSelect,
3128
+ handleSlashMenuSelect,
3129
+ interruptActiveRun,
3130
+ isPlanConfirmTab,
3131
+ isProcessing,
3132
+ isSinglePlan,
3133
+ mcpEditorField,
3134
+ mcpEditorFields,
3135
+ mcpModalIndex,
3136
+ mcpRows,
3137
+ modelPickerIndex,
3138
+ openApiKeyModal,
3139
+ openCatalogMcp,
3140
+ openMcpEditor,
3141
+ replacePasteBlocks,
3142
+ openSubagentEditor,
3143
+ removeSchedule,
3144
+ scheduleModalIndex,
3145
+ scheduleRows,
3146
+ showScheduleDetails,
3147
+ submitTelegramPair,
3148
+ submitTelegramToken,
3149
+ submitMcpEditor,
3150
+ submitSubagentEditor,
3151
+ planQuestions,
3152
+ planTabCount,
3153
+ pqs,
3154
+ removeEditingSubagent,
3155
+ applySandboxMode,
3156
+ applySandboxSettings,
3157
+ sandboxSettings,
3158
+ sandboxSettingsEditing,
3159
+ sandboxSettingsEditBuffer,
3160
+ sandboxSettingsFocusIndex,
3161
+ sandboxMode,
3162
+ showModelPicker,
3163
+ showPlanPanel,
3164
+ showSandboxPicker,
3165
+ pendingPaymentApproval,
3166
+ processMessage,
3167
+ showWalletPicker,
3168
+ walletSettings,
3169
+ walletFocusIndex,
3170
+ walletDisplayInfo,
3171
+ applyWalletSettings,
3172
+ showSlashMenu,
3173
+ slashMenuIndex,
3174
+ submitApiKey,
3175
+ submitPlanAnswers,
3176
+ copyTuiSelectionToHost,
3177
+ toggleSavedMcp,
3178
+ messages,
3179
+ startupConfig.version,
3180
+ ]);
3181
+ useKeyboard(handleKey);
3182
+ const handlePaste = useCallback((event) => {
3183
+ if (!hasApiKeyRef.current) {
3184
+ event.preventDefault();
3185
+ openApiKeyModal();
3186
+ return;
3187
+ }
3188
+ const text = decodePasteBytes(event.bytes);
3189
+ const trimmed = text.trim();
3190
+ const imageExts = /\.(png|jpe?g|gif|webp|svg|bmp|ico|tiff?)$/i;
3191
+ if (imageExts.test(trimmed) && !trimmed.includes("\n")) {
3192
+ event.preventDefault();
3193
+ const id = ++pasteCounterRef.current;
3194
+ const block = { id, content: trimmed, lines: 1, isImage: true };
3195
+ replacePasteBlocks([...pasteBlocksRef.current, block]);
3196
+ inputRef.current?.insertText(getPasteBlockToken(block));
3197
+ return;
3198
+ }
3199
+ const lineCount = text.split("\n").length;
3200
+ if (lineCount < 2)
3201
+ return;
3202
+ event.preventDefault();
3203
+ const id = ++pasteCounterRef.current;
3204
+ const block = { id, content: text, lines: lineCount };
3205
+ replacePasteBlocks([...pasteBlocksRef.current, block]);
3206
+ inputRef.current?.insertText(getPasteBlockToken(block));
3207
+ }, [openApiKeyModal, replacePasteBlocks]);
3208
+ const handleSubmit = useCallback(() => {
3209
+ const raw = inputRef.current?.plainText || "";
3210
+ if (!raw.trim() && pasteBlocksRef.current.length === 0) {
3211
+ if (queuedMessagesRef.current.length > 0 && isProcessingRef.current) {
3212
+ interruptedRunIdRef.current = activeRunIdRef.current;
3213
+ const activeAgent = activeTurnRef.current?.agent ?? agent;
3214
+ activeTurnRef.current = null;
3215
+ clearLiveTurnUi();
3216
+ activeAgent.abort();
3217
+ }
3218
+ return;
3219
+ }
3220
+ inputRef.current?.clear();
3221
+ let message = raw;
3222
+ const blocks = [...pasteBlocksRef.current];
3223
+ replacePasteBlocks([]);
3224
+ const imageBlocks = blocks.filter((b) => b.isImage);
3225
+ const textBlocks = blocks.filter((b) => !b.isImage);
3226
+ for (const block of textBlocks) {
3227
+ message = message.replace(getPasteBlockToken(block), block.content);
3228
+ }
3229
+ // Load images into base64 for multimodal messages
3230
+ const images = [];
3231
+ for (const block of imageBlocks) {
3232
+ // Clipboard image (Alt+V): already has base64 data
3233
+ if (block.clipboardBase64) {
3234
+ message = message.replace(getPasteBlockToken(block), "[clipboard image]");
3235
+ images.push({ path: "clipboard", mediaType: block.clipboardMediaType ?? "image/png", base64: block.clipboardBase64 });
3236
+ continue;
3237
+ }
3238
+ // File path image: read from disk
3239
+ const filePath = block.content.trim();
3240
+ message = message.replace(getPasteBlockToken(block), `[image: ${filePath}]`);
3241
+ try {
3242
+ const fs = require("node:fs");
3243
+ const path = require("node:path");
3244
+ const resolved = path.isAbsolute(filePath) ? filePath : path.resolve(agent.getCwd(), filePath);
3245
+ const buf = fs.readFileSync(resolved);
3246
+ const ext = path.extname(resolved).toLowerCase().replace(".", "");
3247
+ const mimeMap = {
3248
+ png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
3249
+ gif: "image/gif", webp: "image/webp", svg: "image/svg+xml",
3250
+ bmp: "image/bmp", ico: "image/x-icon", tif: "image/tiff", tiff: "image/tiff",
3251
+ };
3252
+ images.push({ path: resolved, mediaType: mimeMap[ext] ?? "image/png", base64: buf.toString("base64") });
3253
+ }
3254
+ catch {
3255
+ // File unreadable — keep path as text fallback
3256
+ }
3257
+ }
3258
+ const displayText = message.trim();
3259
+ const fileBlocks = [...fileMentionBlocksRef.current];
3260
+ fileMentionBlocksRef.current = [];
3261
+ for (const block of fileBlocks) {
3262
+ message = message.replace(getFileMentionToken(block), `@${block.path}`);
3263
+ }
3264
+ if (!message.trim())
3265
+ return;
3266
+ if (!hasApiKeyRef.current) {
3267
+ openApiKeyModal();
3268
+ return;
3269
+ }
3270
+ if (handleCommand(message))
3271
+ return;
3272
+ const { enhancedMessage } = processAtMentions(message.trim(), agent.getCwd());
3273
+ if (isProcessingRef.current) {
3274
+ queuedMessagesRef.current.push({ text: enhancedMessage, displayText });
3275
+ setQueuedMessages(queuedMessagesRef.current.map((msg) => msg.displayText));
3276
+ setTimeout(scrollToBottom, 10);
3277
+ return;
3278
+ }
3279
+ processMessage(enhancedMessage, displayText, images.length > 0 ? images : undefined);
3280
+ }, [agent, clearLiveTurnUi, handleCommand, openApiKeyModal, processMessage, replacePasteBlocks, scrollToBottom]);
3281
+ const hasMessages = messages.length > 0 || streamContent || isProcessing;
3282
+ return (
3283
+ // biome-ignore lint/a11y/noStaticElementInteractions: OpenCode-style copy-on-mouse-up on root surface
3284
+ _jsxs("box", { width: width, height: height, backgroundColor: t.background, flexDirection: "column", onMouseUp: handleRootMouseUp, onMouseDown: handleRootMouseDown, children: [copyFlashId > 0 ? _jsx(CopyFlashBanner, { t: t, width: width }) : null, hasMessages ? (_jsxs("box", { flexGrow: 1, flexDirection: "column", children: [_jsx(SessionHeader, { t: t, modeInfo: modeInfo, sessionTitle: sessionTitle, sessionId: sessionId }), _jsxs("box", { flexGrow: 1, paddingBottom: 1, paddingTop: 1, paddingLeft: 2, paddingRight: 2, gap: 1, children: [_jsxs("scrollbox", { ref: scrollRef, flexGrow: 1, stickyScroll: true, stickyStart: "bottom", children: [messages.map((msg, i) => (_jsx(MessageView, { entry: msg, index: i, t: t, modeColor: modeInfo.color, expandedMessages: expandedMessages }, `${msg.timestamp?.getTime?.() ?? i}-${msg.type}-${msg.remoteKey ?? ""}-${String(msg.content ?? "").slice(0, 24)}`))), liveTurnSourceLabel && (activeToolCalls.length > 0 || streamContent || isProcessing) && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx("text", { fg: t.textMuted, children: liveTurnSourceLabel }) })), activeToolCalls.map((tc) => tc.function.name === "task" ? (_jsx(SubagentTaskLine, { t: t, agent: tryParseArg(tc, "agent") || "sub-agent", label: toolArgs(tc) || "Working", pending: true }, tc.id)) : tc.function.name === "delegate" ? (_jsx(DelegationTaskLine, { t: t, label: toolArgs(tc) || "Background research", pending: true, id: undefined }, tc.id)) : (_jsx(InlineTool, { t: t, pending: true, children: toolLabel(tc) }, tc.id))), activeSubagent && _jsx(SubagentActivity, { t: t, status: activeSubagent }), streamContent && (_jsx("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, children: _jsx(Markdown, { content: streamContent, t: t }) })), isProcessing && !streamContent && activeToolCalls.length === 0 && (_jsx(ShimmerText, { t: t, text: "Planning next moves" })), showPlanPanel && _jsx(PlanQuestionsPanel, { t: t, questions: planQuestions, state: pqs }), pendingPaymentApproval && _jsx(PaymentApprovalPanel, { t: t, payment: pendingPaymentApproval })] }), btwState && _jsx(BtwOverlay, { state: btwState, theme: t }), _jsx("box", { flexShrink: 0, children: _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, queuedCount: queuedMessages.length, queuedMessages: queuedMessages, typeahead: typeahead }) })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 })] })] })) : (
3285
+ /* ── Home ───────────────────────────────────────── */
3286
+ _jsxs(_Fragment, { children: [_jsxs("box", { flexGrow: 1, alignItems: "center", paddingLeft: 2, paddingRight: 2, children: [_jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { flexShrink: 0, alignItems: "center", children: _jsx(HeroLogo, { t: t }) }), _jsx("box", { height: 1, minHeight: 0, flexShrink: 1 }), _jsx("box", { width: "100%", maxWidth: 75, flexShrink: 0, children: _jsx(PromptBox, { t: t, inputRef: inputRef, isProcessing: isProcessing, showModelPicker: showModelPicker, showSandboxPicker: showSandboxPicker, showWalletPicker: showWalletPicker, showSlashMenu: showSlashMenu, showPlanQuestions: showPlanPanel, showApiKeyModal: showApiKeyModal, blockPrompt: blockPrompt, onSubmit: handleSubmit, onPaste: handlePaste, pasteBlocks: pasteBlocks, modeInfo: modeInfo, model: model, modelInfo: modelInfo, contextStats: contextStats, placeholder: "What are we building?", typeahead: typeahead }) }), _jsx("box", { height: 2, minHeight: 0, flexShrink: 1 }), _jsx("box", { flexGrow: 1, minHeight: 0 })] }), updateInfo?.hasUpdate && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: "#f59e0b", children: ["┃ Update available: v", startupConfig.version, " → v", updateInfo.latestVersion, " — run /update to install"] }) })), isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsx("text", { fg: "#f59e0b", children: "┃ Updating..." }) })), updateOutput && !isUpdating && (_jsx("box", { paddingLeft: 2, paddingRight: 2, flexDirection: "row", flexShrink: 0, children: _jsxs("text", { fg: updateOutput.startsWith("Update complete") ? "#22c55e" : "#ef4444", children: ["┃ ", updateOutput] }) })), _jsx("box", { paddingLeft: 2, paddingRight: 2, flexShrink: 0, children: _jsx(StatusBar, {}) }), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingBottom: 1, flexDirection: "row", flexShrink: 0, children: [_jsx("text", { fg: t.textDim, children: agent.getCwd().replace(os.homedir(), "~") }), sandboxMode === "shuru" ? _jsx("text", { fg: "#f97316", children: " · sandbox" }) : null, _jsx("box", { flexGrow: 1 }), _jsx("text", { fg: t.textDim, children: `v${startupConfig.version}` })] })] })), showApiKeyModal && (_jsx(ApiKeyModal, { t: t, width: width, height: height, inputRef: apiKeyInputRef, error: apiKeyError, onSubmit: submitApiKey })), showUpdateModal && updateInfo && (_jsx(UpdateModal, { t: t, width: width, height: height, currentVersion: startupConfig.version, latestVersion: updateInfo.latestVersion })), showSlashMenu && (_jsx(SlashMenuModal, { t: t, selectedIndex: slashMenuIndex, width: width, height: height, searchQuery: slashSearchQuery, filteredItems: filteredSlashItems })), showMcpModal && !showMcpEditor && (_jsx(McpBrowserModal, { t: t, width: width, height: height, selectedIndex: mcpModalIndex, searchQuery: mcpSearchQuery, rows: mcpRows })), showMcpEditor && (_jsx(McpEditorModal, { t: t, width: width, height: height, draft: mcpEditorDraft, focusedField: mcpEditorField, syncKey: mcpEditorSyncKey, error: mcpEditorError, title: editingMcpId ? "Edit MCP Server" : "Add MCP Server", labelRef: mcpLabelRef, urlRef: mcpUrlRef, headersRef: mcpHeadersRef, commandRef: mcpCommandRef, argsRef: mcpArgsRef, cwdRef: mcpCwdRef, envRef: mcpEnvRef, onSubmit: submitMcpEditor })), showScheduleModal && (_jsx(ScheduleBrowserModal, { t: t, width: width, height: height, selectedIndex: scheduleModalIndex, searchQuery: scheduleSearchQuery, rows: scheduleRows })), showAgentsModal && !showAgentsEditor && (_jsx(SubagentsBrowserModal, { t: t, width: width, height: height, selectedIndex: agentsModalIndex, searchQuery: agentsSearchQuery, rows: agentRows })), showAgentsEditor && (_jsx(SubagentEditorModal, { t: t, width: width, height: height, draft: agentsEditorDraft, focusedField: agentsEditorField, modelIndex: agentsEditorModelIndex, error: agentsEditorError, title: editingSubagent ? `Edit sub-agent: ${formatSubagentName(editingSubagent.name)}` : "Add sub-agent", nameRef: subagentNameRef, instructionRef: subagentInstructionRef, onSubmit: submitSubagentEditor, showRemoveHint: !!editingSubagent }, `subagent-editor-${agentsEditorSyncKey}`)), showModelPicker && (_jsx(ModelPickerModal, { t: t, currentModel: model, selectedIndex: modelPickerIndex, width: width, height: height, searchQuery: modelSearchQuery, filteredModels: filteredModels, reasoningEffortByModel: reasoningEffortByModel })), showWalletPicker && (_jsx(WalletPickerModal, { t: t, settings: walletSettings, walletInfo: walletDisplayInfo, focusIndex: walletFocusIndex, width: width, height: height })), showSandboxPicker && (_jsx(SandboxPickerModal, { t: t, currentMode: sandboxMode, settings: sandboxSettings, focusIndex: sandboxSettingsFocusIndex, editing: sandboxSettingsEditing, editBuffer: sandboxSettingsEditBuffer, width: width, height: height })), showConnectModal && (_jsx(ConnectModal, { t: t, width: width, height: height, selectedIndex: connectModalIndex, channels: CONNECT_CHANNELS })), showTelegramTokenModal && (_jsx(TelegramTokenModal, { t: t, width: width, height: height, inputRef: telegramTokenInputRef, error: telegramTokenError, onSubmit: submitTelegramToken })), showTelegramPairModal && (_jsx(TelegramPairModal, { t: t, width: width, height: height, inputRef: telegramPairInputRef, error: telegramPairError, onSubmit: () => void submitTelegramPair() }))] }));
3287
+ }
3288
+ /* ── Session Header ──────────────────────────────────────────── */
3289
+ function SessionHeader({ t, modeInfo, sessionTitle, sessionId, }) {
3290
+ return (_jsx("box", { flexShrink: 0, width: "100%", children: _jsxs("box", { flexDirection: "row", width: "100%", paddingTop: 1, paddingBottom: 1, paddingLeft: 2, paddingRight: 2, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: modeInfo.color }, children: _jsx("b", { children: modeInfo.label }) }), sessionTitle ? (_jsx("span", { style: { fg: t.text }, children: _jsxs("b", { children: [": ", sessionTitle] }) })) : null] }), _jsx("box", { flexGrow: 1 }), sessionId ? _jsx("text", { fg: t.textDim, children: sessionId }) : null] }) }));
3291
+ }
3292
+ /* ── Prompt Box ──────────────────────────────────────────────── */
3293
+ const TEXTAREA_KEYBINDINGS = [
3294
+ { name: "return", action: "submit" },
3295
+ { name: "return", shift: true, action: "newline" },
3296
+ ];
3297
+ function formatTokenCount(tokens) {
3298
+ if (tokens >= 1_000_000)
3299
+ return `${(tokens / 1_000_000).toFixed(1).replace(/\.0$/, "")}M`;
3300
+ if (tokens >= 1_000)
3301
+ return `${Math.round(tokens / 1_000)}K`;
3302
+ return String(tokens);
3303
+ }
3304
+ function ContextMeter({ t, stats }) {
3305
+ return (_jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: `${Math.round(stats.ratioRemaining * 100)}%` }), _jsx("span", { style: { fg: t.textDim }, children: ` ${formatTokenCount(stats.remainingTokens)}` })] }));
3306
+ }
3307
+ function PromptBox({ t, inputRef, isProcessing, showModelPicker, showSandboxPicker, showWalletPicker, showSlashMenu, showPlanQuestions, showApiKeyModal, blockPrompt, onSubmit, onPaste, pasteBlocks: _pasteBlocks, modeInfo, model, modelInfo, contextStats, placeholder, queuedCount, queuedMessages, typeahead, }) {
3308
+ const hasQueue = (queuedMessages?.length ?? 0) > 0;
3309
+ const showSuggestions = typeahead?.visible ?? false;
3310
+ return (_jsxs("box", { backgroundColor: t.backgroundPanel, children: [_jsxs("box", { children: [hasQueue && (_jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.queueBg, flexShrink: 0, children: [queuedMessages.map((msg, i) => (
3311
+ // biome-ignore lint/suspicious/noArrayIndexKey: append-only queue of plain strings
3312
+ _jsxs("text", { fg: t.text, children: ["→ ", msg] }, i))), _jsx("box", { height: 1 }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "send now" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "↑ " }), _jsx("span", { style: { fg: t.textMuted }, children: "edit" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "cancel" })] })] })), showSuggestions && typeahead && (_jsx(SuggestionOverlay, { t: t, suggestions: typeahead.suggestions, selectedIndex: typeahead.selectedIndex })), _jsxs("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.backgroundElement, flexDirection: "row", gap: 2, alignItems: "flex-start", flexShrink: 0, children: [_jsx(PromptModeLabel, { t: t, modeInfo: modeInfo, isProcessing: isProcessing }), _jsx("box", { flexGrow: 1, children: _jsx("textarea", { ref: inputRef, focused: !showModelPicker &&
3313
+ !showSandboxPicker &&
3314
+ !showWalletPicker &&
3315
+ !showSlashMenu &&
3316
+ !showPlanQuestions &&
3317
+ !showApiKeyModal &&
3318
+ !blockPrompt, placeholder: isProcessing ? "Queue a follow-up... (esc to interrupt)" : placeholder || "Message muonroi-cli...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 10, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit, onPaste: onPaste }) })] })] }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingLeft: 2, paddingRight: 2, height: 1, flexShrink: 0, children: [_jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: [_jsx("text", { fg: t.text, children: modelInfo?.name || model }), contextStats ? _jsx(ContextMeter, { t: t, stats: contextStats }) : null] }), _jsx("box", { flexDirection: "row", gap: 1, alignItems: "center", height: 1, children: isProcessing ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["enter ", _jsx("span", { style: { fg: t.textMuted }, children: "queue" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: (queuedCount ?? 0) > 0 ? "clear queue" : "interrupt" })] })] })) : showSuggestions ? (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "accept" })] }), _jsxs("text", { fg: t.text, children: ["↑↓ ", _jsx("span", { style: { fg: t.textMuted }, children: "navigate" })] }), _jsxs("text", { fg: t.text, children: ["esc ", _jsx("span", { style: { fg: t.textMuted }, children: "dismiss" })] })] })) : (_jsxs(_Fragment, { children: [_jsxs("text", { fg: t.text, children: ["@ ", _jsx("span", { style: { fg: t.textMuted }, children: "files" })] }), _jsxs("text", { fg: t.text, children: ["shift+enter ", _jsx("span", { style: { fg: t.textMuted }, children: "new line" })] }), _jsxs("text", { fg: t.text, children: ["tab ", _jsx("span", { style: { fg: t.textMuted }, children: "modes" })] })] })) })] })] }));
3319
+ }
3320
+ function PromptModeLabel({ t, modeInfo, isProcessing, }) {
3321
+ if (!isProcessing) {
3322
+ return (_jsx("text", { fg: modeInfo.color, children: _jsx("b", { children: modeInfo.label }) }));
3323
+ }
3324
+ return _jsx(PromptLoadingBoxes, { t: t, color: modeInfo.color });
3325
+ }
3326
+ function PromptLoadingBoxes({ t: _t, color }) {
3327
+ const [frame, setFrame] = useState(0);
3328
+ useEffect(() => {
3329
+ const id = setInterval(() => setFrame((n) => (n + 1) % PROMPT_LOADING_FRAMES.length), 120);
3330
+ return () => clearInterval(id);
3331
+ }, []);
3332
+ const step = PROMPT_LOADING_FRAMES[frame] ?? PROMPT_LOADING_FRAMES[0];
3333
+ return (_jsx("text", { children: [0, 1, 2].map((idx) => (_jsx("span", { style: { fg: promptLoadingCellColor(color, idx, step.active, step.forward) }, children: promptLoadingCellGlyph(idx, step.active, step.forward) }, idx))) }));
3334
+ }
3335
+ function promptLoadingCellGlyph(index, active, forward) {
3336
+ const distance = forward ? active - index : index - active;
3337
+ return distance >= 0 && distance < 2 ? "■" : "⬝";
3338
+ }
3339
+ function promptLoadingCellColor(color, index, active, forward) {
3340
+ const distance = forward ? active - index : index - active;
3341
+ if (distance === 0)
3342
+ return color;
3343
+ if (distance === 1)
3344
+ return withAlpha(color, 0.72);
3345
+ return withAlpha(color, 0.22);
3346
+ }
3347
+ function withAlpha(color, alpha) {
3348
+ const normalized = color.trim();
3349
+ const hex = normalized.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
3350
+ if (!hex)
3351
+ return color;
3352
+ const body = hex[1];
3353
+ const expanded = body.length === 3
3354
+ ? body
3355
+ .split("")
3356
+ .map((ch) => ch + ch)
3357
+ .join("")
3358
+ : body;
3359
+ const alphaHex = Math.round(Math.max(0, Math.min(1, alpha)) * 255)
3360
+ .toString(16)
3361
+ .padStart(2, "0");
3362
+ return `#${expanded}${alphaHex}`;
3363
+ }
3364
+ function CopyFlashBanner({ t, width }) {
3365
+ return (_jsx("box", { position: "absolute", left: 0, top: 1, width: width, zIndex: 500, alignItems: "center", flexShrink: 0, backgroundColor: t.background, shouldFill: false, children: _jsx("box", { height: 3, paddingLeft: 2, paddingRight: 2, backgroundColor: t.queueBg, justifyContent: "center", alignItems: "center", children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: "✓ " }), _jsx("span", { style: { fg: t.text }, children: "Copied to clipboard" })] }) }) }));
3366
+ }
3367
+ function ApiKeyModal({ t, width, height, inputRef, error, onSubmit, }) {
3368
+ const overlayBg = "#000000cc";
3369
+ const panelWidth = Math.min(68, width - 6);
3370
+ const panelHeight = 13;
3371
+ const top = bottomAlignedModalTop(height, panelHeight);
3372
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Add API key" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "Paste your xAI API key to unlock chat. You can hide this prompt with esc." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "xai-...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 3, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "save key · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "hide" })] })) })] }) }));
3373
+ }
3374
+ /* ── Messages ────────────────────────────────────────────────── */
3375
+ const USER_MSG_COLLAPSED_LINES = 5;
3376
+ function UserMessageContent({ content, t, expanded }) {
3377
+ const lines = content.split("\n");
3378
+ const isLong = lines.length > USER_MSG_COLLAPSED_LINES;
3379
+ if (!isLong) {
3380
+ return _jsx("text", { fg: t.text, children: content });
3381
+ }
3382
+ if (expanded) {
3383
+ return (_jsxs(_Fragment, { children: [_jsx("text", { fg: t.text, children: content }), _jsx("box", { marginTop: 1, children: _jsxs("text", { fg: t.textDim, children: ["ctrl+e ", _jsx("span", { style: { fg: t.textMuted }, children: "collapse" })] }) })] }));
3384
+ }
3385
+ const preview = lines.slice(0, USER_MSG_COLLAPSED_LINES).join("\n");
3386
+ const hiddenCount = lines.length - USER_MSG_COLLAPSED_LINES;
3387
+ return (_jsxs(_Fragment, { children: [_jsx("text", { fg: t.text, children: preview }), _jsx("box", { marginTop: 1, children: _jsxs("text", { fg: t.textDim, children: ["ctrl+e ", _jsx("span", { style: { fg: t.textMuted }, children: `expand (${hiddenCount} more lines)` })] }) })] }));
3388
+ }
3389
+ function MessageView({ entry, index, t, modeColor, expandedMessages, }) {
3390
+ switch (entry.type) {
3391
+ case "user":
3392
+ return (_jsx("box", { border: ["left"], customBorderChars: SPLIT, borderColor: entry.modeColor || modeColor, marginTop: index === 0 ? 0 : 1, marginBottom: 1, children: _jsxs("box", { paddingTop: 1, paddingBottom: 1, paddingLeft: 2, backgroundColor: t.backgroundPanel, flexShrink: 0, flexDirection: "column", children: [entry.sourceLabel ? _jsx("text", { fg: t.textMuted, children: entry.sourceLabel }) : null, _jsx(UserMessageContent, { content: entry.content, t: t, expanded: expandedMessages?.has(index) ?? false })] }) }));
3393
+ case "assistant":
3394
+ return (_jsxs("box", { paddingLeft: 3, marginTop: 1, flexShrink: 0, flexDirection: "column", children: [entry.sourceLabel ? _jsx("text", { fg: t.textMuted, children: entry.sourceLabel }) : null, _jsx(Markdown, { content: entry.content, t: t })] }));
3395
+ case "tool_call":
3396
+ return (_jsx("box", { paddingLeft: 3, marginTop: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: entry.modeColor || modeColor }, children: "▣ " }), _jsx("span", { style: { fg: t.textMuted }, children: entry.content.replace("▣ ", "") })] }) }));
3397
+ case "tool_result": {
3398
+ const name = entry.toolCall?.function.name || "tool";
3399
+ const args = toolArgs(entry.toolCall);
3400
+ const diff = entry.toolResult?.diff;
3401
+ const plan = entry.toolResult?.plan;
3402
+ if (name === "generate_plan" && plan) {
3403
+ return _jsx(PlanView, { plan: plan, t: t });
3404
+ }
3405
+ if (name === "task" && entry.toolResult?.task) {
3406
+ return _jsx(TaskResultView, { t: t, entry: entry });
3407
+ }
3408
+ if (name === "delegate" && entry.toolResult?.delegation) {
3409
+ return _jsx(DelegationResultView, { t: t, entry: entry });
3410
+ }
3411
+ if (name === "delegation_list") {
3412
+ return _jsx(DelegationListView, { t: t, content: entry.content });
3413
+ }
3414
+ if (name === "delegation_read") {
3415
+ return _jsx(ToolTextOutputView, { t: t, label: toolLabel(entry.toolCall), content: entry.content });
3416
+ }
3417
+ if (name === "lsp") {
3418
+ const lspOp = tryParseArg(entry.toolCall, "operation") || "query";
3419
+ const lspFile = tryParseArg(entry.toolCall, "filePath") || "";
3420
+ const lspLine = tryParseArg(entry.toolCall, "line");
3421
+ const lspPos = lspLine ? `:${lspLine}` : "";
3422
+ return (_jsxs("box", { gap: 0, marginTop: 1, children: [_jsx(InlineTool, { t: t, pending: false, children: `lsp ${lspOp} ${lspFile}${lspPos}` }), _jsx(LspResultView, { t: t, operation: lspOp, filePath: lspFile, position: lspPos, content: entry.content })] }));
3423
+ }
3424
+ if ((entry.toolResult?.media?.length ?? 0) > 0) {
3425
+ if (name === "generate_image" || name === "generate_video") {
3426
+ return _jsx(MediaAutoOpenView, { t: t, label: toolLabel(entry.toolCall), toolResult: entry.toolResult });
3427
+ }
3428
+ return _jsx(MediaToolResultView, { t: t, label: toolLabel(entry.toolCall), toolResult: entry.toolResult });
3429
+ }
3430
+ if (name === "write_file" || name === "edit_file") {
3431
+ const filePath = diff?.filePath || tryParseArg(entry.toolCall, "file_path") || tryParseArg(entry.toolCall, "path") || args;
3432
+ const label = name === "write_file" ? `Write ${filePath}` : `Edit ${filePath}`;
3433
+ return (_jsxs("box", { gap: 0, children: [_jsx(InlineTool, { t: t, pending: false, children: label }), diff && _jsx(DiffView, { t: t, diff: diff }), (entry.toolResult?.lspDiagnostics?.length ?? 0) > 0 && (_jsx(LspDiagnosticsView, { t: t, diagnostics: entry.toolResult?.lspDiagnostics ?? [] }))] }));
3434
+ }
3435
+ if (name === "bash" && entry.toolResult?.backgroundProcess) {
3436
+ const bp = entry.toolResult.backgroundProcess;
3437
+ return _jsx(BackgroundProcessLine, { t: t, id: bp.id, pid: bp.pid, command: bp.command });
3438
+ }
3439
+ if (name === "process_logs") {
3440
+ return _jsx(ProcessLogsView, { t: t, content: entry.content });
3441
+ }
3442
+ if (name === "process_stop" || name === "process_list") {
3443
+ return (_jsx(InlineTool, { t: t, pending: false, children: entry.content }));
3444
+ }
3445
+ if (name === "read_file")
3446
+ return (_jsx(InlineTool, { t: t, pending: false, children: `Read ${trunc(tryParseArg(entry.toolCall, "file_path") || tryParseArg(entry.toolCall, "path") || args, 60)}` }));
3447
+ if (name === "grep")
3448
+ return (_jsx(InlineTool, { t: t, pending: false, children: `Grep ${trunc(args, 60)}` }));
3449
+ if (name === "search_web" || name === "search_x")
3450
+ return (_jsxs(InlineTool, { t: t, pending: false, children: [name === "search_web" ? "Web" : "X", ` Search "${trunc(args, 60)}"`] }));
3451
+ return (_jsx(InlineTool, { t: t, pending: false, children: trunc(name === "bash" ? args : `${name} ${args}`, 80) }));
3452
+ }
3453
+ case "structured_response": {
3454
+ const sr = entry.structuredResponse;
3455
+ if (!sr)
3456
+ return _jsx("text", { fg: t.textMuted, children: entry.content });
3457
+ return _jsx(StructuredResponseView, { t: t, sr: sr, modeColor: entry.modeColor || modeColor });
3458
+ }
3459
+ default:
3460
+ return _jsx("text", { fg: t.textMuted, children: entry.content });
3461
+ }
3462
+ }
3463
+ function StructuredResponseView({ t, sr, modeColor }) {
3464
+ const d = sr.data;
3465
+ switch (sr.taskType) {
3466
+ case "refactor": {
3467
+ const r = d;
3468
+ return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: modeColor }, children: "◆ " }), _jsx("span", { style: { fg: "#ffffff" }, children: r.summary ?? "Refactor" })] }), (r.changes ?? []).map((c, i) => (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: " ── " }), _jsx("span", { style: { fg: t.accent }, children: c.file }), _jsx("span", { style: { fg: t.accent }, children: " ──" })] }), c.diff.split("\n").map((line, j) => {
3469
+ const fg = line.startsWith("+") ? t.diffAddedFg : line.startsWith("-") ? t.diffRemovedFg : t.text;
3470
+ return _jsx("text", { fg: fg, children: ` ${line}` }, `rl${i}-${j}`);
3471
+ })] }, `rc${i}`))), r.verify_command && (_jsx("text", { fg: t.textMuted, marginTop: 1, children: ` verify: ${r.verify_command}` }))] }));
3472
+ }
3473
+ case "debug": {
3474
+ const r = d;
3475
+ return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: modeColor }, children: "◆ " }), _jsx("span", { style: { fg: t.textMuted }, children: "hypothesis: " }), _jsx("span", { children: r.hypothesis })] }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: " root cause: " }), _jsx("span", { style: { fg: "#ffffff" }, children: r.root_cause })] }), r.fix && (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: " ── fix: " }), _jsx("span", { style: { fg: t.accent }, children: r.fix.file }), _jsx("span", { style: { fg: t.accent }, children: " ──" })] }), r.fix.diff.split("\n").map((line, j) => {
3476
+ const fg = line.startsWith("+") ? t.diffAddedFg : line.startsWith("-") ? t.diffRemovedFg : t.text;
3477
+ return _jsx("text", { fg: fg, children: ` ${line}` }, `dl${j}`);
3478
+ })] })), r.verify_command && (_jsx("text", { fg: t.textMuted, marginTop: 1, children: ` verify: ${r.verify_command}` }))] }));
3479
+ }
3480
+ case "plan": {
3481
+ const r = d;
3482
+ return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [(r.steps ?? []).map((s, i) => (_jsxs("box", { flexDirection: "column", children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.planStepNum }, children: `${i + 1}. ` }), _jsx("span", { style: { fg: "#ffffff" }, children: s.action })] }), _jsx("text", { fg: t.planStepDesc, children: ` done when: ${s.criterion}` }), s.rationale && _jsx("text", { fg: t.textMuted, children: ` why: ${s.rationale}` })] }, `ps${i}`))), (r.assumptions?.length ?? 0) > 0 && (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsx("text", { fg: t.textMuted, children: " assumptions:" }), r.assumptions.map((a, i) => (_jsx("text", { fg: t.text, children: ` - ${a}` }, `pa${i}`)))] })), (r.risks?.length ?? 0) > 0 && (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsx("text", { fg: t.textMuted, children: " risks:" }), r.risks.map((rk, i) => (_jsx("text", { fg: t.diffRemovedFg, children: ` - ${rk}` }, `pr${i}`)))] }))] }));
3483
+ }
3484
+ case "analyze": {
3485
+ const r = d;
3486
+ const sevColor = (s) => s === "high" ? t.diffRemovedFg : s === "medium" ? t.planStepNum : t.textMuted;
3487
+ return (_jsx("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: (r.findings ?? []).map((f, i) => (_jsxs("box", { flexDirection: "column", children: [_jsxs("text", { children: [_jsx("span", { style: { fg: sevColor(f.severity) }, children: `[${f.severity.toUpperCase()}] ` }), _jsx("span", { children: f.text })] }), _jsx("text", { fg: t.textMuted, children: ` evidence: ${f.evidence}` })] }, `af${i}`))) }));
3488
+ }
3489
+ case "documentation": {
3490
+ const r = d;
3491
+ return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [_jsx("text", { children: r.content }), (r.examples ?? []).map((ex, i) => (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsx("text", { fg: t.textMuted, children: ex.description }), _jsx("text", { fg: t.mdCode, children: ex.code })] }, `de${i}`)))] }));
3492
+ }
3493
+ case "generate": {
3494
+ const r = d;
3495
+ return (_jsxs("box", { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: [r.explanation && _jsx("text", { fg: t.textMuted, children: r.explanation }), (r.files ?? []).map((f, i) => (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.accent }, children: "── " }), _jsx("span", { style: { fg: t.accent }, children: f.path }), _jsx("span", { style: { fg: t.textMuted }, children: ` (${f.language})` }), _jsx("span", { style: { fg: t.accent }, children: " ──" })] }), _jsx("text", { fg: t.mdCodeBlockFg, children: f.content })] }, `gf${i}`)))] }));
3496
+ }
3497
+ default:
3498
+ return _jsx("text", { fg: t.text, children: JSON.stringify(d, null, 2) });
3499
+ }
3500
+ }
3501
+ const MAX_DIFF_ROWS = 20;
3502
+ const LINE_NUM_WIDTH = 4;
3503
+ function parsePatch(patch) {
3504
+ const lines = patch.split("\n");
3505
+ const rows = [];
3506
+ let oldLine = 0;
3507
+ let newLine = 0;
3508
+ let prevOldEnd = 0;
3509
+ for (const line of lines) {
3510
+ const hunkMatch = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
3511
+ if (hunkMatch) {
3512
+ oldLine = parseInt(hunkMatch[1], 10);
3513
+ newLine = parseInt(hunkMatch[2], 10);
3514
+ const skipped = oldLine - prevOldEnd - 1;
3515
+ if (skipped > 0) {
3516
+ rows.push({ kind: "separator", count: skipped });
3517
+ }
3518
+ continue;
3519
+ }
3520
+ if (line.startsWith("---") || line.startsWith("+++") || line.startsWith("\\"))
3521
+ continue;
3522
+ if (line.startsWith("Index:") || line.startsWith("===="))
3523
+ continue;
3524
+ if (line.startsWith("-")) {
3525
+ rows.push({ kind: "removed", oldNum: oldLine, text: line.slice(1) });
3526
+ oldLine++;
3527
+ prevOldEnd = oldLine - 1;
3528
+ }
3529
+ else if (line.startsWith("+")) {
3530
+ rows.push({ kind: "added", newNum: newLine, text: line.slice(1) });
3531
+ newLine++;
3532
+ }
3533
+ else if (line.length > 0 || (oldLine > 0 && newLine > 0)) {
3534
+ const content = line.startsWith(" ") ? line.slice(1) : line;
3535
+ rows.push({ kind: "context", oldNum: oldLine, newNum: newLine, text: content });
3536
+ oldLine++;
3537
+ newLine++;
3538
+ prevOldEnd = oldLine - 1;
3539
+ }
3540
+ }
3541
+ return rows;
3542
+ }
3543
+ function DiffView({ t, diff }) {
3544
+ const rows = parsePatch(diff.patch);
3545
+ if (rows.length === 0)
3546
+ return null;
3547
+ const truncated = rows.length > MAX_DIFF_ROWS;
3548
+ const visible = truncated ? rows.slice(0, MAX_DIFF_ROWS) : rows;
3549
+ const pad = (n) => n !== undefined ? String(n).padStart(LINE_NUM_WIDTH) : " ".repeat(LINE_NUM_WIDTH);
3550
+ return (_jsx("box", { paddingLeft: 5, marginTop: 0, flexShrink: 0, children: _jsxs("box", { flexDirection: "column", children: [_jsx("box", { backgroundColor: t.diffHeader, paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.diffHeaderFg }, children: diff.filePath }), _jsx("span", { style: { fg: t.textDim }, children: " " }), _jsx("span", { style: { fg: t.diffRemovedFg }, children: `-${diff.removals}` }), _jsx("span", { style: { fg: t.textDim }, children: " " }), _jsx("span", { style: { fg: t.diffAddedFg }, children: `+${diff.additions}` })] }) }), visible.map((row, i) => {
3551
+ if (row.kind === "separator") {
3552
+ return (
3553
+ // biome-ignore lint/suspicious/noArrayIndexKey: separator rows lack unique identifiers
3554
+ _jsx("box", { backgroundColor: t.diffSeparator, paddingLeft: 1, children: _jsxs("text", { fg: t.diffSeparatorFg, children: ["⌃ ", row.count, " unmodified lines"] }) }, `sep-${i}`));
3555
+ }
3556
+ if (row.kind === "removed") {
3557
+ return (_jsxs("box", { backgroundColor: t.diffRemoved, flexDirection: "row", children: [_jsx("text", { fg: t.diffRemovedLineNum, children: pad(row.oldNum) }), _jsx("text", { fg: t.diffRemovedFg, children: ` ${row.text}` })] }, `rm-${row.oldNum}`));
3558
+ }
3559
+ if (row.kind === "added") {
3560
+ return (_jsxs("box", { backgroundColor: t.diffAdded, flexDirection: "row", children: [_jsx("text", { fg: t.diffAddedLineNum, children: pad(row.newNum) }), _jsx("text", { fg: t.diffAddedFg, children: ` ${row.text}` })] }, `add-${row.newNum}`));
3561
+ }
3562
+ return (_jsxs("box", { backgroundColor: t.diffContext, flexDirection: "row", children: [_jsx("text", { fg: t.diffLineNumber, children: pad(row.oldNum) }), _jsx("text", { fg: t.diffContextFg, children: ` ${row.text}` })] }, `ctx-${row.oldNum}`));
3563
+ }), truncated && (_jsx("box", { backgroundColor: t.diffSeparator, paddingLeft: 1, children: _jsxs("text", { fg: t.diffSeparatorFg, children: ["⌃ ", rows.length - MAX_DIFF_ROWS, " more lines"] }) }))] }) }));
3564
+ }
3565
+ const MAX_LSP_RESULT_LINES = 10;
3566
+ function LspResultView({ t, operation, filePath, position, content, }) {
3567
+ const body = content.trim();
3568
+ const lines = body.split("\n");
3569
+ const truncated = lines.length > MAX_LSP_RESULT_LINES;
3570
+ const visible = truncated ? lines.slice(0, MAX_LSP_RESULT_LINES).join("\n") : body;
3571
+ const label = `${operation} ${filePath}${position}`;
3572
+ return (_jsx("box", { paddingLeft: 5, marginTop: 0, flexShrink: 0, children: _jsxs("box", { flexDirection: "column", children: [_jsx("box", { backgroundColor: t.diffHeader, paddingLeft: 1, paddingRight: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "lsp" }), _jsx("span", { style: { fg: t.textDim }, children: " · " }), _jsx("span", { style: { fg: t.diffHeaderFg }, children: label })] }) }), _jsx("box", { backgroundColor: t.mdCodeBlockBg, paddingLeft: 1, paddingRight: 1, children: _jsx("text", { fg: t.mdCodeBlockFg, children: visible }) }), truncated && (_jsx("box", { backgroundColor: t.diffSeparator, paddingLeft: 1, children: _jsxs("text", { fg: t.diffSeparatorFg, children: ["⌃ ", lines.length - MAX_LSP_RESULT_LINES, " more lines"] }) }))] }) }));
3573
+ }
3574
+ function LspDiagnosticsView({ t, diagnostics }) {
3575
+ const files = diagnostics.slice(0, 3);
3576
+ return (_jsx("box", { paddingLeft: 5, marginTop: 1, children: _jsxs("box", { flexDirection: "column", children: [_jsx("box", { children: _jsx("text", { fg: t.textMuted, children: "LSP diagnostics" }) }), files.map((entry) => (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: t.textDim, children: `${entry.serverId} • ${entry.filePath}` }), entry.diagnostics.slice(0, 5).map((diagnostic, index) => (_jsx("text", { fg: diagnostic.severity === 1 ? t.diffRemovedFg : diagnostic.severity === 2 ? t.primary : t.textMuted, children: `${formatLspSeverity(diagnostic.severity)} ${diagnostic.range.start.line + 1}:${diagnostic.range.start.character + 1} ${diagnostic.message}` }, `${entry.serverId}:${entry.filePath}:${index}`)))] }, `${entry.serverId}:${entry.filePath}`)))] }) }));
3577
+ }
3578
+ function formatLspSeverity(severity) {
3579
+ switch (severity) {
3580
+ case 1:
3581
+ return "error";
3582
+ case 2:
3583
+ return "warning";
3584
+ case 3:
3585
+ return "info";
3586
+ case 4:
3587
+ return "hint";
3588
+ default:
3589
+ return "issue";
3590
+ }
3591
+ }
3592
+ function ShimmerText({ t, text }) {
3593
+ return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: _jsx(LoadingSpinner, {}) }), _jsxs("span", { style: { fg: t.textMuted }, children: [" ", text] })] }) }));
3594
+ }
3595
+ function InlineTool({ t, pending: _pending, children }) {
3596
+ return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { fg: t.textMuted, children: ["→ ", children] }) }));
3597
+ }
3598
+ function SubagentTaskLine({ t, agent, label, pending }) {
3599
+ const displayLabel = compactTaskLabel(label);
3600
+ const displayAgent = formatSubagentName(agent);
3601
+ return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [pending ? (_jsx("span", { style: { fg: t.subagentAccent }, children: _jsx(LoadingSpinner, {}) })) : null, pending ? " " : "", _jsx("span", { style: { fg: t.subagentAccent }, children: _jsx("b", { children: `${displayAgent}: ${displayLabel}` }) })] }) }));
3602
+ }
3603
+ function DelegationTaskLine({ t, label, pending, id }) {
3604
+ const displayLabel = compactTaskLabel(label);
3605
+ return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [pending ? (_jsx("span", { style: { fg: t.subagentAccent }, children: _jsx(LoadingSpinner, {}) })) : (_jsx("span", { style: { fg: t.subagentAccent }, children: "◆" })), " ", _jsx("span", { style: { fg: t.subagentAccent }, children: _jsx("b", { children: "Background" }) }), _jsxs("span", { style: { fg: t.textMuted }, children: [" — ", displayLabel] }), id ? _jsx("span", { style: { fg: t.textDim }, children: ` (${id})` }) : null] }) }));
3606
+ }
3607
+ function LoadingSpinner() {
3608
+ const [frame, setFrame] = useState(0);
3609
+ useEffect(() => {
3610
+ const id = setInterval(() => setFrame((n) => (n + 1) % LOADING_SPINNER_FRAMES.length), 120);
3611
+ return () => clearInterval(id);
3612
+ }, []);
3613
+ return _jsx(_Fragment, { children: LOADING_SPINNER_FRAMES[frame] });
3614
+ }
3615
+ function SubagentActivity({ t, status }) {
3616
+ return (_jsx("box", { paddingLeft: 5, children: _jsxs("text", { fg: t.textMuted, children: ["→ ", truncateLine(status.detail, 100)] }) }));
3617
+ }
3618
+ function TaskResultView({ t, entry }) {
3619
+ const task = entry.toolResult?.task;
3620
+ if (!task)
3621
+ return null;
3622
+ return (_jsxs("box", { gap: 0, children: [_jsx(SubagentTaskLine, { t: t, agent: task.agent, label: task.description, pending: false }), _jsx("box", { paddingLeft: 5, children: _jsxs("text", { fg: t.text, children: [formatSubagentName(task.agent), ": ", truncateLine(task.summary, 90)] }) })] }));
3623
+ }
3624
+ function DelegationResultView({ t, entry }) {
3625
+ const delegation = entry.toolResult?.delegation;
3626
+ if (!delegation)
3627
+ return null;
3628
+ return _jsx(DelegationTaskLine, { t: t, label: delegation.description, pending: false, id: delegation.id });
3629
+ }
3630
+ function DelegationListView({ t, content }) {
3631
+ const items = parseDelegationList(content);
3632
+ if (items.length === 0) {
3633
+ return (_jsx(InlineTool, { t: t, pending: false, children: "No background delegations" }));
3634
+ }
3635
+ return (_jsx("box", { paddingLeft: 3, gap: 0, children: items.map((item) => {
3636
+ const statusColor = item.status === "complete"
3637
+ ? "#8adf8a"
3638
+ : item.status === "running"
3639
+ ? t.subagentAccent
3640
+ : item.status === "error"
3641
+ ? "#df8a8a"
3642
+ : t.textMuted;
3643
+ return (_jsx("box", { children: _jsxs("text", { children: [_jsx("span", { style: { fg: statusColor }, children: "◆ " }), _jsx("span", { style: { fg: t.text }, children: item.id }), _jsx("span", { style: { fg: statusColor }, children: ` ${item.status}` }), _jsxs("span", { style: { fg: t.textMuted }, children: [" — ", truncateLine(item.label, 60)] })] }) }, item.id));
3644
+ }) }));
3645
+ }
3646
+ function parseDelegationList(content) {
3647
+ const items = [];
3648
+ for (const line of content.split("\n")) {
3649
+ const match = line.match(/`([^`]+)`\s+\[(\w+)]\s+(.*)/);
3650
+ if (match) {
3651
+ items.push({ id: match[1], status: match[2], label: match[3].trim() });
3652
+ }
3653
+ }
3654
+ return items;
3655
+ }
3656
+ function BackgroundProcessLine({ t, id, pid, command }) {
3657
+ return (_jsx("box", { paddingLeft: 3, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.subagentAccent }, children: "◆ " }), _jsx("span", { style: { fg: t.subagentAccent }, children: _jsx("b", { children: "Background process" }) }), _jsx("span", { style: { fg: t.textMuted }, children: ` id:${id} pid:${pid}` }), _jsxs("span", { style: { fg: t.textDim }, children: [" — ", truncateLine(command, 60)] })] }) }));
3658
+ }
3659
+ function formatScheduleDetails(schedule, daemonStatus) {
3660
+ const daemonText = daemonStatus.running
3661
+ ? `running${daemonStatus.pid ? ` (pid ${daemonStatus.pid})` : ""}`
3662
+ : "not running";
3663
+ return [
3664
+ `Schedule: ${schedule.name}`,
3665
+ `ID: ${schedule.id}`,
3666
+ `Type: ${schedule.cron ? "recurring" : "one-time"}`,
3667
+ `Cron: ${schedule.cron ?? "runs once immediately"}`,
3668
+ `Enabled: ${schedule.enabled ? "yes" : "no"}`,
3669
+ `Model: ${schedule.model}`,
3670
+ `Directory: ${schedule.directory}`,
3671
+ `Last run: ${schedule.lastRunAt ?? "never"}`,
3672
+ `Daemon: ${daemonText}`,
3673
+ "",
3674
+ "Instruction:",
3675
+ schedule.instruction,
3676
+ ].join("\n");
3677
+ }
3678
+ function ProcessLogsView({ t, content }) {
3679
+ const lines = content.split("\n");
3680
+ const header = lines[0] || "";
3681
+ const body = lines.slice(1).join("\n").trim();
3682
+ return (_jsxs("box", { paddingLeft: 3, gap: 0, children: [_jsxs("text", { fg: t.textMuted, children: ["→ ", header] }), body ? (_jsx("box", { paddingLeft: 2, marginTop: 0, children: _jsx("box", { backgroundColor: t.mdCodeBlockBg, paddingLeft: 1, paddingRight: 1, children: _jsx("text", { fg: t.mdCodeBlockFg, children: truncateBlock(body, 15) }) }) })) : null] }));
3683
+ }
3684
+ function truncateBlock(text, maxLines) {
3685
+ const lines = text.split("\n");
3686
+ if (lines.length <= maxLines)
3687
+ return text;
3688
+ return [...lines.slice(0, maxLines), `… ${lines.length - maxLines} more lines`].join("\n");
3689
+ }
3690
+ function ToolTextOutputView({ t, label, content }) {
3691
+ return (_jsxs("box", { gap: 0, children: [_jsx(InlineTool, { t: t, pending: false, children: label }), _jsx("box", { paddingLeft: 5, marginTop: 1, flexShrink: 0, children: _jsx(Markdown, { content: content, t: t }) })] }));
3692
+ }
3693
+ function openMediaFile(filePath) {
3694
+ try {
3695
+ const cmd = process.platform === "darwin" ? "open" : "xdg-open";
3696
+ require("child_process").execFile(cmd, [filePath]);
3697
+ }
3698
+ catch { }
3699
+ }
3700
+ function MediaAutoOpenView({ t, label, toolResult }) {
3701
+ const media = toolResult.media ?? [];
3702
+ const openedRef = useRef(new Set());
3703
+ useEffect(() => {
3704
+ for (const asset of media) {
3705
+ if (!openedRef.current.has(asset.path)) {
3706
+ openedRef.current.add(asset.path);
3707
+ openMediaFile(asset.path);
3708
+ }
3709
+ }
3710
+ }, [media]);
3711
+ return (_jsx("box", { gap: 0, children: _jsx(InlineTool, { t: t, pending: false, children: label }) }));
3712
+ }
3713
+ function MediaToolResultView({ t, label, toolResult }) {
3714
+ const media = toolResult.media ?? [];
3715
+ return (_jsxs("box", { gap: 0, children: [_jsx(InlineTool, { t: t, pending: false, children: label }), toolResult.output ? (_jsx("box", { paddingLeft: 5, marginTop: 1, flexShrink: 0, children: _jsx(Markdown, { content: toolResult.output, t: t }) })) : null, media.length > 0 ? (_jsx("box", { paddingLeft: 5, marginTop: toolResult.output ? 1 : 0, flexDirection: "column", children: media.map((asset) => (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: t.text, children: asset.path }), asset.url ? _jsx("text", { fg: t.textMuted, children: `url: ${asset.url}` }) : null, asset.sourcePath ? _jsx("text", { fg: t.textMuted, children: `source: ${asset.sourcePath}` }) : null, asset.sourceUrl ? _jsx("text", { fg: t.textMuted, children: `source_url: ${asset.sourceUrl}` }) : null] }, `${asset.path}-${asset.url ?? ""}-${asset.sourcePath ?? ""}-${asset.sourceUrl ?? ""}`))) })) : null] }));
3716
+ }
3717
+ /* ── Slash Menu ──────────────────────────────────────────────── */
3718
+ function bottomAlignedModalTop(height, panelHeight) {
3719
+ return Math.max(2, Math.floor((height - panelHeight) / 2));
3720
+ }
3721
+ /* ── Update Modal ────────────────────────────────────────────── */
3722
+ function UpdateModal({ t, width, height, currentVersion, latestVersion, }) {
3723
+ const overlayBg = "#000000cc";
3724
+ const panelWidth = Math.min(60, width - 6);
3725
+ const panelHeight = 9;
3726
+ const top = bottomAlignedModalTop(height, panelHeight);
3727
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: "#f59e0b", children: _jsx("b", { children: "Update Available" }) }), _jsx("text", { fg: t.textMuted, children: "esc to dismiss" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsxs("text", { fg: t.text, children: ["A new version of muonroi-cli is available: ", _jsxs("span", { style: { fg: t.textMuted }, children: ["v", currentVersion] }), " → ", _jsxs("span", { style: { fg: "#22c55e" }, children: ["v", latestVersion] })] }) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "Press enter to update now, or esc to dismiss" }) })] }) }));
3728
+ }
3729
+ function SlashMenuModal({ t, selectedIndex, width, height, searchQuery, filteredItems, }) {
3730
+ const listRef = useRef(null);
3731
+ useEffect(() => {
3732
+ const item = filteredItems[selectedIndex];
3733
+ if (item)
3734
+ listRef.current?.scrollChildIntoView(`slash-${item.id}`);
3735
+ }, [selectedIndex, filteredItems]);
3736
+ const itemCount = Math.max(filteredItems.length, 1);
3737
+ const contentHeight = itemCount + 5;
3738
+ const maxH = Math.floor(height * 0.6);
3739
+ const panelHeight = Math.min(contentHeight, maxH);
3740
+ const top = bottomAlignedModalTop(height, panelHeight);
3741
+ const overlayBg = "#000000cc";
3742
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(50, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Commands" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.text, children: searchQuery || _jsx("span", { style: { fg: t.textMuted }, children: "Search..." }) }) }), _jsxs("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: [filteredItems.map((item, idx) => (_jsx("box", { id: `slash-${item.id}`, backgroundColor: idx === selectedIndex ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, children: _jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsxs("text", { fg: idx === selectedIndex ? t.selected : t.text, children: ["/", item.label] }), _jsx("text", { fg: t.textMuted, children: item.description })] }) }, item.id))), filteredItems.length === 0 && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: "No commands match your search" }) }))] })] }) }));
3743
+ }
3744
+ function ConnectModal({ t, width, height, selectedIndex, channels, }) {
3745
+ const listRef = useRef(null);
3746
+ useEffect(() => {
3747
+ const ch = channels[selectedIndex];
3748
+ if (ch)
3749
+ listRef.current?.scrollChildIntoView(`connect-${ch.id}`);
3750
+ }, [selectedIndex, channels]);
3751
+ const panelHeight = Math.min(channels.length + 9, Math.floor(height * 0.5));
3752
+ const top = bottomAlignedModalTop(height, panelHeight);
3753
+ const overlayBg = "#000000cc";
3754
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(56, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Connect" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.textMuted, children: "Choose a channel" }) }), _jsx("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: channels.map((ch, idx) => (_jsx("box", { id: `connect-${ch.id}`, backgroundColor: idx === selectedIndex ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, children: _jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: idx === selectedIndex ? t.selected : t.text, children: ch.label }), _jsx("text", { fg: t.textMuted, children: ch.description })] }) }, ch.id))) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: _jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "select · " }), _jsx("span", { style: { fg: t.primary }, children: "↑↓ " }), _jsx("span", { style: { fg: t.textMuted }, children: "navigate · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "close" })] }) })] }) }));
3755
+ }
3756
+ function TelegramTokenModal({ t, width, height, inputRef, error, onSubmit, }) {
3757
+ const overlayBg = "#000000cc";
3758
+ const panelWidth = Math.min(68, width - 6);
3759
+ const panelHeight = 14;
3760
+ const top = bottomAlignedModalTop(height, panelHeight);
3761
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Telegram bot token" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "From @BotFather: /newbot, then paste the token here. Stored in ~/.muonroi-cli/user-settings.json." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "123456:ABC...", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 3, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "save token · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "close" })] })) })] }) }));
3762
+ }
3763
+ function TelegramPairModal({ t, width, height, inputRef, error, onSubmit, }) {
3764
+ const overlayBg = "#000000cc";
3765
+ const panelWidth = Math.min(68, width - 6);
3766
+ const panelHeight = 13;
3767
+ const top = bottomAlignedModalTop(height, panelHeight);
3768
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: panelWidth, height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Pairing code" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.text, children: "DM your bot with /pair, then paste the 6-character code." }) }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("box", { backgroundColor: t.backgroundElement, paddingLeft: 1, paddingRight: 1, width: "100%", children: _jsx("textarea", { ref: inputRef, focused: true, placeholder: "ABC123", textColor: t.text, backgroundColor: t.backgroundElement, placeholderColor: t.textMuted, minHeight: 1, maxHeight: 2, wrapMode: "word", keyBindings: TEXTAREA_KEYBINDINGS, onSubmit: onSubmit }) }) }), _jsx("box", { flexGrow: 1, minHeight: 0 }), _jsx("box", { paddingLeft: 2, paddingRight: 2, paddingTop: 2, paddingBottom: 1, children: error ? (_jsx("text", { fg: t.diffRemovedFg, children: error })) : (_jsxs("text", { children: [_jsx("span", { style: { fg: t.primary }, children: "enter " }), _jsx("span", { style: { fg: t.textMuted }, children: "approve pairing · " }), _jsx("span", { style: { fg: t.primary }, children: "esc " }), _jsx("span", { style: { fg: t.textMuted }, children: "close" })] })) })] }) }));
3769
+ }
3770
+ /* ── Model Picker ────────────────────────────────────────────── */
3771
+ function ModelPickerModal({ t, currentModel, selectedIndex, width, height, searchQuery, filteredModels, reasoningEffortByModel, }) {
3772
+ const listRef = useRef(null);
3773
+ useEffect(() => {
3774
+ const m = filteredModels[selectedIndex];
3775
+ if (m)
3776
+ listRef.current?.scrollChildIntoView(`model-${m.id}`);
3777
+ }, [selectedIndex, filteredModels]);
3778
+ const itemCount = Math.max(filteredModels.length, 1);
3779
+ const selectedModel = filteredModels[selectedIndex];
3780
+ const selectedSupportsReasoning = !!selectedModel && getSupportedReasoningEfforts(selectedModel.id).length > 0;
3781
+ const contentHeight = itemCount + 6;
3782
+ const maxH = Math.floor(height * 0.6);
3783
+ const panelHeight = Math.min(contentHeight, maxH);
3784
+ const top = bottomAlignedModalTop(height, panelHeight);
3785
+ const overlayBg = "#000000cc";
3786
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(60, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Select model" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, children: _jsx("text", { fg: t.text, children: searchQuery || _jsx("span", { style: { fg: t.textMuted }, children: "Search..." }) }) }), _jsxs("scrollbox", { ref: listRef, flexGrow: 1, minHeight: 0, children: [filteredModels.map((m, idx) => {
3787
+ const selected = idx === selectedIndex;
3788
+ const current = m.id === currentModel;
3789
+ const supportedReasoningEfforts = getSupportedReasoningEfforts(m.id);
3790
+ const reasoningEffort = getEffectiveReasoningEffort(m.id, reasoningEffortByModel[normalizeModelId(m.id)]) ?? "auto";
3791
+ return (_jsx("box", { id: `model-${m.id}`, backgroundColor: selected ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", children: _jsxs("box", { width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: current ? t.accent : selected ? t.selected : t.text, children: m.name }), supportedReasoningEfforts.length > 0 ? (_jsx("text", { fg: selected ? t.primary : t.textMuted, children: `[${reasoningEffort}]` })) : null] }) }, m.id));
3792
+ }), filteredModels.length === 0 && (_jsx("box", { paddingLeft: 2, children: _jsx("text", { fg: t.textMuted, children: "No models match your search" }) }))] }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: selectedSupportsReasoning ? "left/right reasoning enter select esc close" : "enter select esc close" }) })] }) }));
3793
+ }
3794
+ function SandboxPickerModal({ t, currentMode, settings, focusIndex, editing, editBuffer, width, height, }) {
3795
+ const visibleRows = getSandboxVisibleRows(currentMode);
3796
+ const panelHeight = Math.min(visibleRows.length + 6, Math.floor(height * 0.6));
3797
+ const top = bottomAlignedModalTop(height, panelHeight);
3798
+ const overlayBg = "#000000cc";
3799
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(64, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Sandbox settings" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("scrollbox", { flexGrow: 1, minHeight: 0, children: visibleRows.map((row, idx) => {
3800
+ const focused = idx === focusIndex;
3801
+ const isEditing = editing === row.key;
3802
+ const display = row.getDisplay(currentMode, settings);
3803
+ return (_jsx("box", { backgroundColor: focused ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", children: _jsxs("box", { width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: focused ? t.selected : t.text, children: row.label }), isEditing ? (_jsxs("text", { fg: t.accent, children: [editBuffer || row.placeholder || "", "_"] })) : row.type === "toggle" ? (_jsxs("text", { fg: focused ? t.primary : t.textMuted, children: ["< ", display, " >"] })) : (_jsx("text", { fg: focused ? t.primary : t.textMuted, children: display }))] }) }, row.key));
3804
+ }) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: editing
3805
+ ? "type value enter confirm esc cancel"
3806
+ : "arrows navigate left/right toggle enter edit esc close" }) })] }) }));
3807
+ }
3808
+ function PaymentApprovalPanel({ t, payment, }) {
3809
+ const options = ["Approve payment", "Reject"];
3810
+ return (_jsxs("box", { flexDirection: "column", border: ["left"], customBorderChars: {
3811
+ topLeft: "",
3812
+ bottomLeft: "",
3813
+ vertical: "┃",
3814
+ topRight: "",
3815
+ bottomRight: "",
3816
+ horizontal: " ",
3817
+ bottomT: "",
3818
+ topT: "",
3819
+ cross: "",
3820
+ leftT: "",
3821
+ rightT: "",
3822
+ }, borderColor: "#e5c07b", marginTop: 1, paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, backgroundColor: t.backgroundPanel, children: [_jsx("text", { children: _jsx("span", { style: { fg: t.planTitle ?? t.primary }, children: _jsx("b", { children: "Payment required" }) }) }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("text", { children: _jsx("span", { style: { fg: t.text }, children: payment.url }) }), payment.description ? (_jsx("text", { children: _jsx("span", { style: { fg: t.textMuted }, children: payment.description }) })) : null, payment.security ? (_jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: "Security: " }), _jsx("span", { style: { fg: "#60a5fa" }, children: payment.securityLabel })] })) : null, _jsxs("text", { children: [_jsx("span", { style: { fg: t.textMuted }, children: "Price: " }), _jsx("span", { style: { fg: "#22c55e" }, children: _jsx("b", { children: `${payment.amount} USDC` }) }), _jsx("span", { style: { fg: t.textMuted }, children: ` on ${payment.network}` })] })] }), _jsx("box", { marginTop: 1, flexDirection: "column", children: options.map((label, i) => {
3823
+ const isSel = i === payment.selected;
3824
+ return (_jsxs("text", { children: [_jsx("span", { style: { fg: isSel ? "#22c55e" : t.textMuted }, children: isSel ? "> " : " " }), _jsx("span", { style: { fg: isSel ? t.text : t.textMuted }, children: isSel ? _jsx("b", { children: label }) : label })] }, label));
3825
+ }) }), _jsxs("box", { flexDirection: "row", gap: 3, marginTop: 1, flexShrink: 0, children: [_jsxs("text", { children: [_jsx("span", { style: { fg: t.text }, children: "↑↓" }), _jsx("span", { style: { fg: t.textMuted }, children: " select" })] }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.text }, children: "enter" }), _jsx("span", { style: { fg: t.textMuted }, children: " confirm" })] }), _jsxs("text", { children: [_jsx("span", { style: { fg: t.text }, children: "esc" }), _jsx("span", { style: { fg: t.textMuted }, children: " reject" })] })] })] }));
3826
+ }
3827
+ function WalletPickerModal({ t, settings, walletInfo, focusIndex, width, height, }) {
3828
+ const panelHeight = Math.min(WALLET_ROWS.length + 6, Math.floor(height * 0.6));
3829
+ const top = bottomAlignedModalTop(height, panelHeight);
3830
+ const overlayBg = "#000000cc";
3831
+ return (_jsx("box", { position: "absolute", left: 0, top: 0, width: width, height: height, alignItems: "center", paddingTop: top, backgroundColor: overlayBg, children: _jsxs("box", { width: Math.min(64, width - 6), height: panelHeight, backgroundColor: t.backgroundPanel, paddingTop: 1, paddingBottom: 1, flexDirection: "column", children: [_jsxs("box", { flexShrink: 0, flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, children: [_jsx("text", { fg: t.primary, children: _jsx("b", { children: "Wallet & Payments" }) }), _jsx("text", { fg: t.textMuted, children: "esc" })] }), _jsx("scrollbox", { flexGrow: 1, minHeight: 0, children: WALLET_ROWS.map((row, idx) => {
3832
+ const focused = idx === focusIndex;
3833
+ const display = row.getDisplay(settings, walletInfo);
3834
+ return (_jsx("box", { backgroundColor: focused ? t.selectedBg : undefined, paddingLeft: 2, paddingRight: 2, width: "100%", children: _jsxs("box", { width: "100%", flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: focused ? t.selected : t.text, children: row.label }), row.type === "toggle" ? (_jsxs("text", { fg: focused ? t.primary : t.textMuted, children: ["< ", display, " >"] })) : (_jsx("text", { fg: focused ? t.primary : t.textMuted, children: display }))] }) }, row.key));
3835
+ }) }), _jsx("box", { flexShrink: 0, paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: _jsx("text", { fg: t.textMuted, children: "arrows navigate left/right toggle esc close" }) })] }) }));
3836
+ }
3837
+ /* ── Helpers ──────────────────────────────────────────────────── */
3838
+ function isEscapeKey(key) {
3839
+ return (key.name === "escape" ||
3840
+ key.code === "Escape" ||
3841
+ key.baseCode === 27 ||
3842
+ key.sequence === "\u001b" ||
3843
+ key.raw === "\u001b");
3844
+ }
3845
+ function toolArgs(tc) {
3846
+ if (!tc)
3847
+ return "";
3848
+ try {
3849
+ const a = JSON.parse(tc.function.arguments);
3850
+ if (tc.function.name === "bash")
3851
+ return (a.command || "").replace(/\n/g, " ").trim();
3852
+ if (tc.function.name === "read_file" || tc.function.name === "write_file" || tc.function.name === "edit_file")
3853
+ return a.file_path || a.path || "";
3854
+ if (tc.function.name === "grep") {
3855
+ const path = a.path ? ` in ${a.path}` : "";
3856
+ return `"${a.pattern || ""}"${path}`;
3857
+ }
3858
+ if (tc.function.name === "generate_image" || tc.function.name === "generate_video")
3859
+ return a.prompt || "";
3860
+ if (tc.function.name === "task")
3861
+ return a.description || "";
3862
+ if (tc.function.name === "lsp")
3863
+ return `${a.operation || "query"} ${a.filePath || ""}`.trim();
3864
+ if (tc.function.name === "delegate")
3865
+ return a.description || "";
3866
+ if (tc.function.name === "delegation_read")
3867
+ return a.id || "";
3868
+ if (tc.function.name === "process_logs" || tc.function.name === "process_stop")
3869
+ return a.id != null ? String(a.id) : "";
3870
+ return a.query || "";
3871
+ }
3872
+ catch {
3873
+ return "";
3874
+ }
3875
+ }
3876
+ function tryParseArg(tc, key) {
3877
+ if (!tc)
3878
+ return "";
3879
+ try {
3880
+ return JSON.parse(tc.function.arguments)[key] || "";
3881
+ }
3882
+ catch {
3883
+ return "";
3884
+ }
3885
+ }
3886
+ function toolLabel(tc) {
3887
+ const args = toolArgs(tc);
3888
+ if (tc.function.name === "bash") {
3889
+ try {
3890
+ const parsed = JSON.parse(tc.function.arguments);
3891
+ if (parsed.background)
3892
+ return `Background: ${trunc(args || "Starting process...", 70)}`;
3893
+ }
3894
+ catch {
3895
+ /* */
3896
+ }
3897
+ return trunc(args || "Running command...", 80);
3898
+ }
3899
+ if (tc.function.name === "read_file")
3900
+ return `Read ${trunc(args, 60)}`;
3901
+ if (tc.function.name === "write_file")
3902
+ return `Write ${trunc(args, 60)}`;
3903
+ if (tc.function.name === "edit_file")
3904
+ return `Edit ${trunc(args, 60)}`;
3905
+ if (tc.function.name === "grep")
3906
+ return `Grep ${trunc(args, 60)}`;
3907
+ if (tc.function.name === "search_web")
3908
+ return `Web Search "${trunc(args, 60)}"`;
3909
+ if (tc.function.name === "search_x")
3910
+ return `X Search "${trunc(args, 60)}"`;
3911
+ if (tc.function.name === "generate_image")
3912
+ return `Generate image "${trunc(args, 60)}"`;
3913
+ if (tc.function.name === "generate_video")
3914
+ return `Generate video "${trunc(args, 60)}"`;
3915
+ if (tc.function.name === "task")
3916
+ return `Task ${trunc(args, 60)}`;
3917
+ if (tc.function.name === "delegate")
3918
+ return `Background ${trunc(args, 60)}`;
3919
+ if (tc.function.name === "delegation_read")
3920
+ return `Read delegation ${trunc(args, 60)}`;
3921
+ if (tc.function.name === "delegation_list")
3922
+ return "List delegations";
3923
+ if (tc.function.name === "process_logs")
3924
+ return `Logs for process ${args}`;
3925
+ if (tc.function.name === "process_stop")
3926
+ return `Stop process ${args}`;
3927
+ if (tc.function.name === "process_list")
3928
+ return "List processes";
3929
+ if (tc.function.name === "generate_plan")
3930
+ return "Generating plan...";
3931
+ return trunc(`${tc.function.name} ${args}`, 80);
3932
+ }
3933
+ function sanitizeContent(raw) {
3934
+ let s = raw.replace(/^[\s\n]*assistant:\s*/gi, "");
3935
+ s = s.replace(/\{"success"\s*:\s*(true|false)\s*,\s*"output"\s*:\s*"[\s\S]*$/m, "");
3936
+ return s.trim();
3937
+ }
3938
+ function shouldOpenApiKeyModalForKey(key) {
3939
+ if (key.ctrl || key.meta)
3940
+ return false;
3941
+ if (key.name === "return" || key.name === "backspace")
3942
+ return true;
3943
+ return !!(key.sequence && key.sequence.length === 1);
3944
+ }
3945
+ function compactTaskLabel(label) {
3946
+ const words = label.trim().split(/\s+/).filter(Boolean);
3947
+ if (words.length <= 3)
3948
+ return label.trim() || "Working";
3949
+ return `${words.slice(0, 3).join(" ")}...`;
3950
+ }
3951
+ function trunc(s, n) {
3952
+ return s.length <= n ? s : `${s.slice(0, n)}…`;
3953
+ }
3954
+ function truncateLine(s, n) {
3955
+ return trunc(s.replace(/\s+/g, " ").trim(), n);
3956
+ }
3957
+ //# sourceMappingURL=app.js.map