@adecore/agents-react 0.0.1 → 0.17.0-beta.1

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 (513) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +53 -0
  3. package/dist/agents/AccountDot.d.ts +4 -0
  4. package/dist/agents/AccountDot.js +8 -0
  5. package/dist/agents/AgentIcon.d.ts +6 -0
  6. package/dist/agents/AgentIcon.js +20 -0
  7. package/dist/agents/ModelOptionControl.d.ts +15 -0
  8. package/dist/agents/ModelOptionControl.js +33 -0
  9. package/dist/agents/ProviderLogo.d.ts +8 -0
  10. package/dist/agents/ProviderLogo.js +8 -0
  11. package/dist/agents/account-limits.d.ts +7 -0
  12. package/dist/agents/account-limits.js +40 -0
  13. package/dist/agents/accounts.d.ts +22 -0
  14. package/dist/agents/accounts.js +113 -0
  15. package/dist/agents/model-name.d.ts +12 -0
  16. package/dist/agents/model-name.js +56 -0
  17. package/dist/agents/model-options.d.ts +5 -0
  18. package/dist/agents/model-options.js +15 -0
  19. package/dist/agents/provider-paths.d.ts +2 -0
  20. package/dist/agents/provider-paths.js +8 -0
  21. package/dist/agents/status-look.d.ts +13 -0
  22. package/dist/agents/status-look.js +24 -0
  23. package/dist/chat/account-choice.d.ts +11 -0
  24. package/dist/chat/account-choice.js +30 -0
  25. package/dist/chat/actions.d.ts +6 -0
  26. package/dist/chat/actions.js +68 -0
  27. package/dist/chat/activity.d.ts +5 -0
  28. package/dist/chat/activity.js +6 -0
  29. package/dist/chat/attachments.d.ts +23 -0
  30. package/dist/chat/attachments.js +86 -0
  31. package/dist/chat/bookmarks.d.ts +14 -0
  32. package/dist/chat/bookmarks.js +73 -0
  33. package/dist/chat/chat-client.d.ts +55 -0
  34. package/dist/chat/chat-client.js +279 -0
  35. package/dist/chat/chat-references.d.ts +5 -0
  36. package/dist/chat/chat-references.js +5 -0
  37. package/dist/chat/drafts.d.ts +29 -0
  38. package/dist/chat/drafts.js +116 -0
  39. package/dist/chat/forks.d.ts +2 -0
  40. package/dist/chat/forks.js +28 -0
  41. package/dist/chat/guards.d.ts +12 -0
  42. package/dist/chat/guards.js +69 -0
  43. package/dist/chat/logic/ansi.d.ts +13 -0
  44. package/dist/chat/logic/ansi.js +185 -0
  45. package/dist/chat/logic/answers.d.ts +1 -0
  46. package/dist/chat/logic/answers.js +8 -0
  47. package/dist/chat/logic/bookmarks.d.ts +5 -0
  48. package/dist/chat/logic/bookmarks.js +33 -0
  49. package/dist/chat/logic/context-usage.d.ts +12 -0
  50. package/dist/chat/logic/context-usage.js +21 -0
  51. package/dist/chat/logic/fork.d.ts +45 -0
  52. package/dist/chat/logic/fork.js +148 -0
  53. package/dist/chat/logic/handback.d.ts +2 -0
  54. package/dist/chat/logic/handback.js +8 -0
  55. package/dist/chat/logic/json.d.ts +1 -0
  56. package/dist/chat/logic/json.js +4 -0
  57. package/dist/chat/logic/limit.d.ts +7 -0
  58. package/dist/chat/logic/limit.js +34 -0
  59. package/dist/chat/logic/resume-compaction.d.ts +14 -0
  60. package/dist/chat/logic/resume-compaction.js +47 -0
  61. package/dist/chat/logic/scrubber.d.ts +50 -0
  62. package/dist/chat/logic/scrubber.js +220 -0
  63. package/dist/chat/logic/thread-cards.d.ts +3 -0
  64. package/dist/chat/logic/thread-cards.js +59 -0
  65. package/dist/chat/logic/timeline-copy.d.ts +4 -0
  66. package/dist/chat/logic/timeline-copy.js +59 -0
  67. package/dist/chat/logic/timeline-target.d.ts +12 -0
  68. package/dist/chat/logic/timeline-target.js +34 -0
  69. package/dist/chat/logic/timeline.d.ts +115 -0
  70. package/dist/chat/logic/timeline.js +343 -0
  71. package/dist/chat/logic/tool-catalog.d.ts +10 -0
  72. package/dist/chat/logic/tool-catalog.js +44 -0
  73. package/dist/chat/logic/tools.d.ts +15 -0
  74. package/dist/chat/logic/tools.js +82 -0
  75. package/dist/chat/logic/welcome.d.ts +2 -0
  76. package/dist/chat/logic/welcome.js +10 -0
  77. package/dist/chat/logic/workflow.d.ts +14 -0
  78. package/dist/chat/logic/workflow.js +47 -0
  79. package/dist/chat/mentions.d.ts +65 -0
  80. package/dist/chat/mentions.js +184 -0
  81. package/dist/chat/persisted-json.d.ts +5 -0
  82. package/dist/chat/persisted-json.js +58 -0
  83. package/dist/chat/preferences.d.ts +27 -0
  84. package/dist/chat/preferences.js +139 -0
  85. package/dist/chat/quote.d.ts +2 -0
  86. package/dist/chat/quote.js +25 -0
  87. package/dist/chat/recent-messages.d.ts +17 -0
  88. package/dist/chat/recent-messages.js +36 -0
  89. package/dist/chat/resume-compaction-dismissals.d.ts +3 -0
  90. package/dist/chat/resume-compaction-dismissals.js +14 -0
  91. package/dist/chat/runtime-modes.d.ts +4 -0
  92. package/dist/chat/runtime-modes.js +15 -0
  93. package/dist/chat/stash.d.ts +24 -0
  94. package/dist/chat/stash.js +91 -0
  95. package/dist/chat/subagent-conversation.d.ts +41 -0
  96. package/dist/chat/subagent-conversation.js +167 -0
  97. package/dist/chat/subagent-list.d.ts +19 -0
  98. package/dist/chat/subagent-list.js +106 -0
  99. package/dist/chat/subagent-support.d.ts +6 -0
  100. package/dist/chat/subagent-support.js +14 -0
  101. package/dist/chat/subagent-view.d.ts +35 -0
  102. package/dist/chat/subagent-view.js +96 -0
  103. package/dist/chat/thumbnails.d.ts +10 -0
  104. package/dist/chat/thumbnails.js +89 -0
  105. package/dist/chat/timeline-flash.d.ts +12 -0
  106. package/dist/chat/timeline-flash.js +16 -0
  107. package/dist/chat/timeline-scroll.d.ts +27 -0
  108. package/dist/chat/timeline-scroll.js +152 -0
  109. package/dist/chat/ui/AccountPill.d.ts +3 -0
  110. package/dist/chat/ui/AccountPill.js +23 -0
  111. package/dist/chat/ui/AnsiOutput.d.ts +5 -0
  112. package/dist/chat/ui/AnsiOutput.js +9 -0
  113. package/dist/chat/ui/BookmarkMarker.d.ts +8 -0
  114. package/dist/chat/ui/BookmarkMarker.js +57 -0
  115. package/dist/chat/ui/BookmarkSubmenu.d.ts +3 -0
  116. package/dist/chat/ui/BookmarkSubmenu.js +33 -0
  117. package/dist/chat/ui/ChatActivity.d.ts +7 -0
  118. package/dist/chat/ui/ChatActivity.js +84 -0
  119. package/dist/chat/ui/ChatReferenceChip.d.ts +4 -0
  120. package/dist/chat/ui/ChatReferenceChip.js +16 -0
  121. package/dist/chat/ui/CodeBlock.d.ts +4 -0
  122. package/dist/chat/ui/CodeBlock.js +132 -0
  123. package/dist/chat/ui/Composer.d.ts +15 -0
  124. package/dist/chat/ui/Composer.js +856 -0
  125. package/dist/chat/ui/ComposerInput.d.ts +29 -0
  126. package/dist/chat/ui/ComposerInput.js +127 -0
  127. package/dist/chat/ui/DiffPool.d.ts +5 -0
  128. package/dist/chat/ui/DiffPool.js +26 -0
  129. package/dist/chat/ui/EditDiff.d.ts +4 -0
  130. package/dist/chat/ui/EditDiff.js +21 -0
  131. package/dist/chat/ui/FadingWords.d.ts +3 -0
  132. package/dist/chat/ui/FadingWords.js +5 -0
  133. package/dist/chat/ui/ImageView.d.ts +6 -0
  134. package/dist/chat/ui/ImageView.js +81 -0
  135. package/dist/chat/ui/LimitState.d.ts +8 -0
  136. package/dist/chat/ui/LimitState.js +112 -0
  137. package/dist/chat/ui/Markdown.d.ts +18 -0
  138. package/dist/chat/ui/Markdown.js +159 -0
  139. package/dist/chat/ui/MessageActions.d.ts +9 -0
  140. package/dist/chat/ui/MessageActions.js +53 -0
  141. package/dist/chat/ui/Pickers.d.ts +21 -0
  142. package/dist/chat/ui/Pickers.js +111 -0
  143. package/dist/chat/ui/PromptComposer.d.ts +13 -0
  144. package/dist/chat/ui/PromptComposer.js +56 -0
  145. package/dist/chat/ui/QuoteButton.d.ts +7 -0
  146. package/dist/chat/ui/QuoteButton.js +74 -0
  147. package/dist/chat/ui/ResumeCompactionDock.d.ts +5 -0
  148. package/dist/chat/ui/ResumeCompactionDock.js +14 -0
  149. package/dist/chat/ui/RunSettings.d.ts +28 -0
  150. package/dist/chat/ui/RunSettings.js +153 -0
  151. package/dist/chat/ui/Scrubber.d.ts +17 -0
  152. package/dist/chat/ui/Scrubber.js +118 -0
  153. package/dist/chat/ui/SubagentControls.d.ts +11 -0
  154. package/dist/chat/ui/SubagentControls.js +36 -0
  155. package/dist/chat/ui/SubagentInfo.d.ts +4 -0
  156. package/dist/chat/ui/SubagentInfo.js +34 -0
  157. package/dist/chat/ui/SubagentStopButton.d.ts +7 -0
  158. package/dist/chat/ui/SubagentStopButton.js +34 -0
  159. package/dist/chat/ui/SubagentTimeline.d.ts +4 -0
  160. package/dist/chat/ui/SubagentTimeline.js +107 -0
  161. package/dist/chat/ui/Timeline.d.ts +6 -0
  162. package/dist/chat/ui/Timeline.js +424 -0
  163. package/dist/chat/ui/TimelineMenu.d.ts +12 -0
  164. package/dist/chat/ui/TimelineMenu.js +54 -0
  165. package/dist/chat/ui/UnifiedDiff.d.ts +11 -0
  166. package/dist/chat/ui/UnifiedDiff.js +66 -0
  167. package/dist/chat/ui/UploadThumb.d.ts +4 -0
  168. package/dist/chat/ui/UploadThumb.js +11 -0
  169. package/dist/chat/ui/Welcome.d.ts +4 -0
  170. package/dist/chat/ui/Welcome.js +16 -0
  171. package/dist/chat/ui/chips.d.ts +4 -0
  172. package/dist/chat/ui/chips.js +11 -0
  173. package/dist/chat/ui/code-lines.d.ts +18 -0
  174. package/dist/chat/ui/code-lines.js +46 -0
  175. package/dist/chat/ui/code-streaming.d.ts +1 -0
  176. package/dist/chat/ui/code-streaming.js +3 -0
  177. package/dist/chat/ui/code-theme.d.ts +3 -0
  178. package/dist/chat/ui/code-theme.js +12 -0
  179. package/dist/chat/ui/composer/chips.d.ts +6 -0
  180. package/dist/chat/ui/composer/chips.js +31 -0
  181. package/dist/chat/ui/composer/editor.d.ts +11 -0
  182. package/dist/chat/ui/composer/editor.js +98 -0
  183. package/dist/chat/ui/composer/insert.d.ts +2 -0
  184. package/dist/chat/ui/composer/insert.js +19 -0
  185. package/dist/chat/ui/composer/keys.d.ts +30 -0
  186. package/dist/chat/ui/composer/keys.js +100 -0
  187. package/dist/chat/ui/diff-theme.d.ts +2 -0
  188. package/dist/chat/ui/diff-theme.js +20 -0
  189. package/dist/chat/ui/file-links.d.ts +5 -0
  190. package/dist/chat/ui/file-links.js +20 -0
  191. package/dist/chat/ui/find-reveal.d.ts +7 -0
  192. package/dist/chat/ui/find-reveal.js +13 -0
  193. package/dist/chat/ui/full-diff.d.ts +6 -0
  194. package/dist/chat/ui/full-diff.js +19 -0
  195. package/dist/chat/ui/icons.d.ts +4 -0
  196. package/dist/chat/ui/icons.js +20 -0
  197. package/dist/chat/ui/markdown-blocks.d.ts +6 -0
  198. package/dist/chat/ui/markdown-blocks.js +103 -0
  199. package/dist/chat/ui/quote-selection.d.ts +3 -0
  200. package/dist/chat/ui/quote-selection.js +46 -0
  201. package/dist/chat/ui/rehype-chips.d.ts +23 -0
  202. package/dist/chat/ui/rehype-chips.js +32 -0
  203. package/dist/chat/ui/rehype-fade.d.ts +23 -0
  204. package/dist/chat/ui/rehype-fade.js +36 -0
  205. package/dist/chat/ui/remark-html-as-text.d.ts +8 -0
  206. package/dist/chat/ui/remark-html-as-text.js +26 -0
  207. package/dist/chat/ui/reply-context.d.ts +5 -0
  208. package/dist/chat/ui/reply-context.js +2 -0
  209. package/dist/chat/ui/reveal.d.ts +13 -0
  210. package/dist/chat/ui/reveal.js +114 -0
  211. package/dist/chat/ui/rows/ForksRow.d.ts +4 -0
  212. package/dist/chat/ui/rows/ForksRow.js +38 -0
  213. package/dist/chat/ui/rows/MessageRows.d.ts +40 -0
  214. package/dist/chat/ui/rows/MessageRows.js +204 -0
  215. package/dist/chat/ui/rows/Rows.d.ts +12 -0
  216. package/dist/chat/ui/rows/Rows.js +61 -0
  217. package/dist/chat/ui/rows/SubagentRow.d.ts +17 -0
  218. package/dist/chat/ui/rows/SubagentRow.js +101 -0
  219. package/dist/chat/ui/rows/TaskRow.d.ts +8 -0
  220. package/dist/chat/ui/rows/TaskRow.js +63 -0
  221. package/dist/chat/ui/rows/WorkRows.d.ts +47 -0
  222. package/dist/chat/ui/rows/WorkRows.js +168 -0
  223. package/dist/chat/ui/rows/WorkflowRow.d.ts +8 -0
  224. package/dist/chat/ui/rows/WorkflowRow.js +48 -0
  225. package/dist/chat/ui/rows/row-rhythm.d.ts +6 -0
  226. package/dist/chat/ui/rows/row-rhythm.js +34 -0
  227. package/dist/chat/ui/useToggleSet.d.ts +6 -0
  228. package/dist/chat/ui/useToggleSet.js +25 -0
  229. package/dist/host.d.ts +195 -0
  230. package/dist/host.js +55 -0
  231. package/dist/lazy.d.ts +6 -0
  232. package/dist/lazy.js +57 -0
  233. package/dist/locales/en/agent-chat.json +446 -0
  234. package/dist/locales/en/agent-prompts.json +42 -0
  235. package/dist/locales/en/agent-providers.json +131 -0
  236. package/dist/locales/en/agent-usage.json +131 -0
  237. package/dist/locales/nl/agent-chat.json +446 -0
  238. package/dist/locales/nl/agent-prompts.json +42 -0
  239. package/dist/locales/nl/agent-providers.json +131 -0
  240. package/dist/locales/nl/agent-usage.json +131 -0
  241. package/dist/locales.d.ts +5 -0
  242. package/dist/locales.js +20 -0
  243. package/dist/mounted-registry.d.ts +7 -0
  244. package/dist/mounted-registry.js +39 -0
  245. package/dist/port-transport.d.ts +6 -0
  246. package/dist/port-transport.js +129 -0
  247. package/dist/prompts/logic/focus.d.ts +2 -0
  248. package/dist/prompts/logic/focus.js +23 -0
  249. package/dist/prompts/logic/keys.d.ts +42 -0
  250. package/dist/prompts/logic/keys.js +94 -0
  251. package/dist/prompts/logic/prompts.d.ts +34 -0
  252. package/dist/prompts/logic/prompts.fixtures.d.ts +5 -0
  253. package/dist/prompts/logic/prompts.fixtures.js +79 -0
  254. package/dist/prompts/logic/prompts.js +32 -0
  255. package/dist/prompts/logic/subjects.d.ts +50 -0
  256. package/dist/prompts/logic/subjects.js +63 -0
  257. package/dist/prompts/logic/useFocusAfterAnswer.d.ts +6 -0
  258. package/dist/prompts/logic/useFocusAfterAnswer.js +28 -0
  259. package/dist/prompts/logic/usePromptSession.d.ts +24 -0
  260. package/dist/prompts/logic/usePromptSession.js +87 -0
  261. package/dist/prompts/ui/ApprovalBody.d.ts +13 -0
  262. package/dist/prompts/ui/ApprovalBody.js +34 -0
  263. package/dist/prompts/ui/PromptActions.d.ts +27 -0
  264. package/dist/prompts/ui/PromptActions.js +18 -0
  265. package/dist/prompts/ui/PromptCard.d.ts +16 -0
  266. package/dist/prompts/ui/PromptCard.js +50 -0
  267. package/dist/prompts/ui/PromptView.d.ts +2 -0
  268. package/dist/prompts/ui/PromptView.js +47 -0
  269. package/dist/prompts/ui/QuestionBody.d.ts +9 -0
  270. package/dist/prompts/ui/QuestionBody.js +87 -0
  271. package/dist/providers/AccountColors.d.ts +5 -0
  272. package/dist/providers/AccountColors.js +8 -0
  273. package/dist/providers/AccountDetail.d.ts +12 -0
  274. package/dist/providers/AccountDetail.js +69 -0
  275. package/dist/providers/AccountVariables.d.ts +8 -0
  276. package/dist/providers/AccountVariables.js +43 -0
  277. package/dist/providers/AddAccountForm.d.ts +10 -0
  278. package/dist/providers/AddAccountForm.js +54 -0
  279. package/dist/providers/CliDetail.d.ts +12 -0
  280. package/dist/providers/CliDetail.js +54 -0
  281. package/dist/providers/ProvidersPane.d.ts +9 -0
  282. package/dist/providers/ProvidersPane.js +86 -0
  283. package/dist/providers/account-actions.d.ts +7 -0
  284. package/dist/providers/account-actions.js +57 -0
  285. package/dist/providers/parts.d.ts +9 -0
  286. package/dist/providers/parts.js +18 -0
  287. package/dist/providers/provider-abilities.d.ts +2 -0
  288. package/dist/providers/provider-abilities.js +7 -0
  289. package/dist/providers/variables.d.ts +13 -0
  290. package/dist/providers/variables.js +63 -0
  291. package/dist/scope.d.ts +11 -0
  292. package/dist/scope.js +10 -0
  293. package/dist/settings/sections.d.ts +13 -0
  294. package/dist/settings/sections.js +32 -0
  295. package/dist/state/chats.d.ts +44 -0
  296. package/dist/state/chats.js +199 -0
  297. package/dist/state/provider-accounts.d.ts +17 -0
  298. package/dist/state/provider-accounts.js +67 -0
  299. package/dist/state/providers.d.ts +17 -0
  300. package/dist/state/providers.js +33 -0
  301. package/dist/state/usage.d.ts +44 -0
  302. package/dist/state/usage.js +121 -0
  303. package/dist/storage.d.ts +20 -0
  304. package/dist/storage.js +89 -0
  305. package/dist/theme.css +196 -0
  306. package/dist/transport.d.ts +24 -0
  307. package/dist/transport.js +18 -0
  308. package/dist/ui/PlainTextarea.d.ts +2 -0
  309. package/dist/ui/PlainTextarea.js +5 -0
  310. package/dist/usage/LimitsList.d.ts +18 -0
  311. package/dist/usage/LimitsList.js +73 -0
  312. package/dist/usage/UsageBreakdown.d.ts +9 -0
  313. package/dist/usage/UsageBreakdown.js +67 -0
  314. package/dist/usage/UsageChart.d.ts +10 -0
  315. package/dist/usage/UsageChart.js +47 -0
  316. package/dist/usage/UsageDialog.d.ts +6 -0
  317. package/dist/usage/UsageDialog.js +12 -0
  318. package/dist/usage/UsageLimits.d.ts +1 -0
  319. package/dist/usage/UsageLimits.js +57 -0
  320. package/dist/usage/UsageLimitsCard.d.ts +4 -0
  321. package/dist/usage/UsageLimitsCard.js +26 -0
  322. package/dist/usage/UsagePage.d.ts +7 -0
  323. package/dist/usage/UsagePage.js +154 -0
  324. package/dist/usage/UsagePane.d.ts +3 -0
  325. package/dist/usage/UsagePane.js +21 -0
  326. package/dist/usage/UsageSummary.d.ts +12 -0
  327. package/dist/usage/UsageSummary.js +15 -0
  328. package/dist/usage/UsageTiles.d.ts +7 -0
  329. package/dist/usage/UsageTiles.js +19 -0
  330. package/dist/usage/format.d.ts +13 -0
  331. package/dist/usage/format.js +55 -0
  332. package/dist/usage/limit-groups.d.ts +22 -0
  333. package/dist/usage/limit-groups.js +86 -0
  334. package/dist/usage/limits.d.ts +4 -0
  335. package/dist/usage/limits.js +58 -0
  336. package/dist/usage/money.d.ts +1 -0
  337. package/dist/usage/money.js +13 -0
  338. package/dist/usage/summary.d.ts +34 -0
  339. package/dist/usage/summary.js +94 -0
  340. package/package.json +895 -3
  341. package/src/agents/AccountDot.tsx +14 -0
  342. package/src/agents/AgentIcon.tsx +26 -0
  343. package/src/agents/ModelOptionControl.tsx +68 -0
  344. package/src/agents/ProviderLogo.tsx +20 -0
  345. package/src/agents/account-limits.ts +61 -0
  346. package/src/agents/accounts.ts +137 -0
  347. package/src/agents/model-name.ts +70 -0
  348. package/src/agents/model-options.ts +21 -0
  349. package/src/agents/provider-paths.ts +10 -0
  350. package/src/agents/status-look.ts +43 -0
  351. package/src/chat/account-choice.ts +44 -0
  352. package/src/chat/actions.ts +72 -0
  353. package/src/chat/activity.ts +8 -0
  354. package/src/chat/attachments.ts +118 -0
  355. package/src/chat/bookmarks.ts +87 -0
  356. package/src/chat/chat-client.ts +345 -0
  357. package/src/chat/chat-references.ts +11 -0
  358. package/src/chat/drafts.ts +165 -0
  359. package/src/chat/forks.ts +33 -0
  360. package/src/chat/guards.ts +84 -0
  361. package/src/chat/logic/ansi.ts +206 -0
  362. package/src/chat/logic/answers.ts +8 -0
  363. package/src/chat/logic/bookmarks.ts +42 -0
  364. package/src/chat/logic/context-usage.ts +36 -0
  365. package/src/chat/logic/fork.ts +200 -0
  366. package/src/chat/logic/handback.ts +10 -0
  367. package/src/chat/logic/json.ts +4 -0
  368. package/src/chat/logic/limit.ts +46 -0
  369. package/src/chat/logic/resume-compaction.ts +71 -0
  370. package/src/chat/logic/scrubber.ts +289 -0
  371. package/src/chat/logic/thread-cards.ts +64 -0
  372. package/src/chat/logic/timeline-copy.ts +65 -0
  373. package/src/chat/logic/timeline-target.ts +52 -0
  374. package/src/chat/logic/timeline.ts +419 -0
  375. package/src/chat/logic/tool-catalog.ts +62 -0
  376. package/src/chat/logic/tools.ts +102 -0
  377. package/src/chat/logic/welcome.ts +13 -0
  378. package/src/chat/logic/workflow.ts +66 -0
  379. package/src/chat/mentions.ts +232 -0
  380. package/src/chat/persisted-json.ts +67 -0
  381. package/src/chat/preferences.ts +179 -0
  382. package/src/chat/quote.ts +26 -0
  383. package/src/chat/recent-messages.ts +59 -0
  384. package/src/chat/resume-compaction-dismissals.ts +17 -0
  385. package/src/chat/runtime-modes.ts +18 -0
  386. package/src/chat/stash.ts +122 -0
  387. package/src/chat/subagent-conversation.ts +203 -0
  388. package/src/chat/subagent-list.ts +141 -0
  389. package/src/chat/subagent-support.ts +21 -0
  390. package/src/chat/subagent-view.ts +137 -0
  391. package/src/chat/thumbnails.ts +108 -0
  392. package/src/chat/timeline-flash.ts +33 -0
  393. package/src/chat/timeline-scroll.ts +199 -0
  394. package/src/chat/ui/AccountPill.tsx +31 -0
  395. package/src/chat/ui/AnsiOutput.tsx +18 -0
  396. package/src/chat/ui/BookmarkMarker.tsx +108 -0
  397. package/src/chat/ui/BookmarkSubmenu.tsx +53 -0
  398. package/src/chat/ui/ChatActivity.tsx +198 -0
  399. package/src/chat/ui/ChatReferenceChip.tsx +28 -0
  400. package/src/chat/ui/CodeBlock.tsx +175 -0
  401. package/src/chat/ui/Composer.tsx +1326 -0
  402. package/src/chat/ui/ComposerInput.tsx +187 -0
  403. package/src/chat/ui/DiffPool.tsx +36 -0
  404. package/src/chat/ui/EditDiff.tsx +30 -0
  405. package/src/chat/ui/FadingWords.tsx +18 -0
  406. package/src/chat/ui/ImageView.tsx +159 -0
  407. package/src/chat/ui/LimitState.tsx +165 -0
  408. package/src/chat/ui/Markdown.tsx +258 -0
  409. package/src/chat/ui/MessageActions.tsx +88 -0
  410. package/src/chat/ui/Pickers.tsx +272 -0
  411. package/src/chat/ui/PromptComposer.tsx +119 -0
  412. package/src/chat/ui/QuoteButton.tsx +103 -0
  413. package/src/chat/ui/ResumeCompactionDock.tsx +28 -0
  414. package/src/chat/ui/RunSettings.tsx +546 -0
  415. package/src/chat/ui/Scrubber.tsx +265 -0
  416. package/src/chat/ui/SubagentControls.tsx +66 -0
  417. package/src/chat/ui/SubagentInfo.tsx +67 -0
  418. package/src/chat/ui/SubagentStopButton.tsx +45 -0
  419. package/src/chat/ui/SubagentTimeline.tsx +170 -0
  420. package/src/chat/ui/Timeline.tsx +628 -0
  421. package/src/chat/ui/TimelineMenu.tsx +124 -0
  422. package/src/chat/ui/UnifiedDiff.tsx +99 -0
  423. package/src/chat/ui/UploadThumb.tsx +13 -0
  424. package/src/chat/ui/Welcome.tsx +22 -0
  425. package/src/chat/ui/chips.ts +15 -0
  426. package/src/chat/ui/code-lines.ts +60 -0
  427. package/src/chat/ui/code-streaming.ts +4 -0
  428. package/src/chat/ui/code-theme.ts +15 -0
  429. package/src/chat/ui/composer/chips.ts +43 -0
  430. package/src/chat/ui/composer/editor.ts +118 -0
  431. package/src/chat/ui/composer/insert.ts +22 -0
  432. package/src/chat/ui/composer/keys.ts +144 -0
  433. package/src/chat/ui/diff-theme.ts +23 -0
  434. package/src/chat/ui/file-links.ts +24 -0
  435. package/src/chat/ui/find-reveal.ts +23 -0
  436. package/src/chat/ui/full-diff.ts +25 -0
  437. package/src/chat/ui/icons.tsx +24 -0
  438. package/src/chat/ui/markdown-blocks.ts +123 -0
  439. package/src/chat/ui/quote-selection.ts +53 -0
  440. package/src/chat/ui/rehype-chips.ts +62 -0
  441. package/src/chat/ui/rehype-fade.ts +64 -0
  442. package/src/chat/ui/remark-html-as-text.ts +36 -0
  443. package/src/chat/ui/reply-context.ts +4 -0
  444. package/src/chat/ui/reveal.ts +135 -0
  445. package/src/chat/ui/rows/ForksRow.tsx +79 -0
  446. package/src/chat/ui/rows/MessageRows.tsx +415 -0
  447. package/src/chat/ui/rows/Rows.tsx +81 -0
  448. package/src/chat/ui/rows/SubagentRow.tsx +209 -0
  449. package/src/chat/ui/rows/TaskRow.tsx +91 -0
  450. package/src/chat/ui/rows/WorkRows.tsx +445 -0
  451. package/src/chat/ui/rows/WorkflowRow.tsx +120 -0
  452. package/src/chat/ui/rows/row-rhythm.ts +37 -0
  453. package/src/chat/ui/useToggleSet.ts +39 -0
  454. package/src/host.ts +324 -0
  455. package/src/lazy.tsx +64 -0
  456. package/src/locales/en/agent-chat.json +446 -0
  457. package/src/locales/en/agent-prompts.json +42 -0
  458. package/src/locales/en/agent-providers.json +131 -0
  459. package/src/locales/en/agent-usage.json +131 -0
  460. package/src/locales/nl/agent-chat.json +446 -0
  461. package/src/locales/nl/agent-prompts.json +42 -0
  462. package/src/locales/nl/agent-providers.json +131 -0
  463. package/src/locales/nl/agent-usage.json +131 -0
  464. package/src/locales.ts +33 -0
  465. package/src/mounted-registry.ts +45 -0
  466. package/src/port-transport.ts +146 -0
  467. package/src/prompts/logic/focus.ts +25 -0
  468. package/src/prompts/logic/keys.ts +116 -0
  469. package/src/prompts/logic/prompts.fixtures.ts +84 -0
  470. package/src/prompts/logic/prompts.ts +58 -0
  471. package/src/prompts/logic/subjects.ts +122 -0
  472. package/src/prompts/logic/useFocusAfterAnswer.ts +38 -0
  473. package/src/prompts/logic/usePromptSession.ts +114 -0
  474. package/src/prompts/ui/ApprovalBody.tsx +109 -0
  475. package/src/prompts/ui/PromptActions.tsx +109 -0
  476. package/src/prompts/ui/PromptCard.tsx +101 -0
  477. package/src/prompts/ui/PromptView.tsx +111 -0
  478. package/src/prompts/ui/QuestionBody.tsx +174 -0
  479. package/src/providers/AccountColors.tsx +8 -0
  480. package/src/providers/AccountDetail.tsx +209 -0
  481. package/src/providers/AccountVariables.tsx +126 -0
  482. package/src/providers/AddAccountForm.tsx +121 -0
  483. package/src/providers/CliDetail.tsx +157 -0
  484. package/src/providers/ProvidersPane.tsx +236 -0
  485. package/src/providers/account-actions.ts +67 -0
  486. package/src/providers/parts.tsx +25 -0
  487. package/src/providers/provider-abilities.ts +9 -0
  488. package/src/providers/variables.ts +87 -0
  489. package/src/scope.ts +30 -0
  490. package/src/settings/sections.ts +47 -0
  491. package/src/state/chats.ts +279 -0
  492. package/src/state/provider-accounts.ts +87 -0
  493. package/src/state/providers.ts +50 -0
  494. package/src/state/usage.ts +196 -0
  495. package/src/storage.ts +113 -0
  496. package/src/theme.css +196 -0
  497. package/src/transport.ts +50 -0
  498. package/src/ui/PlainTextarea.tsx +6 -0
  499. package/src/usage/LimitsList.tsx +232 -0
  500. package/src/usage/UsageBreakdown.tsx +210 -0
  501. package/src/usage/UsageChart.tsx +113 -0
  502. package/src/usage/UsageDialog.tsx +20 -0
  503. package/src/usage/UsageLimits.tsx +145 -0
  504. package/src/usage/UsageLimitsCard.tsx +36 -0
  505. package/src/usage/UsagePage.tsx +276 -0
  506. package/src/usage/UsagePane.tsx +52 -0
  507. package/src/usage/UsageSummary.tsx +50 -0
  508. package/src/usage/UsageTiles.tsx +34 -0
  509. package/src/usage/format.ts +71 -0
  510. package/src/usage/limit-groups.ts +119 -0
  511. package/src/usage/limits.ts +71 -0
  512. package/src/usage/money.ts +14 -0
  513. package/src/usage/summary.ts +137 -0
@@ -0,0 +1,265 @@
1
+ import { Fragment, memo, useEffect, useMemo, useState, type ComponentProps, type MouseEvent as ReactMouseEvent } from 'react';
2
+ import clsx from 'clsx';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Bookmark, Copy, GitFork, type LucideIcon } from 'lucide-react';
5
+ import { layoutTicks, messageAt, slotInView, slotOf, tickWidth, TICK_HEIGHT_PX, type ScrubberTick } from '../logic/scrubber';
6
+ import { BookmarkMenuItems } from './TimelineMenu';
7
+ import { formatMoment } from '@adecore/ui/format';
8
+ import { cameThroughPortal, ContextMenu, copyText, Icon, Menu, PreviewCard } from '@adecore/ui';
9
+
10
+ const PREVIEW_CHARS = 280;
11
+
12
+ const FOUND_WIDTH_PX = 4;
13
+
14
+ function timeOf(createdAt: number): string {
15
+ return formatMoment(createdAt);
16
+ }
17
+
18
+ /* What the card may do to the chat the strip belongs to; absent, the card only offers what needs no chat. */
19
+ export interface CardChat {
20
+ chatId: string;
21
+ canFork(turnId: string): boolean;
22
+ fork(turnId: string): void;
23
+ }
24
+
25
+ /* What the card offers for a message. An action a tick cannot take is not drawn at all, not even disabled. */
26
+ type CardActionId = 'copy-message' | 'fork';
27
+
28
+ interface CardAction {
29
+ /* The key of its label in the `chat` namespace; the card reads the words where it draws them. */
30
+ label: string;
31
+ icon: LucideIcon;
32
+ offers(tick: ScrubberTick, chat: CardChat | null): boolean;
33
+ run(tick: ScrubberTick, chat: CardChat | null): void;
34
+ }
35
+
36
+ const CARD_ACTIONS: Record<CardActionId, CardAction> = {
37
+ 'copy-message': {
38
+ label: 'timeline.menu.copyMessage',
39
+ icon: Copy,
40
+ // A wake carries the machine's label, not anything a person wrote.
41
+ offers: (tick) => tick.kind === 'person' && tick.text !== '',
42
+ run: (tick) => copyText(tick.text)
43
+ },
44
+ fork: {
45
+ label: 'timeline.menu.forkFromHere',
46
+ icon: GitFork,
47
+ offers: (tick, chat) => chat !== null && tick.turnId !== null && chat.canFork(tick.turnId),
48
+ run: (tick, chat) => {
49
+ if (chat !== null && tick.turnId !== null) {
50
+ chat.fork(tick.turnId);
51
+ }
52
+ }
53
+ }
54
+ };
55
+
56
+ interface ScrubberProps {
57
+ ticks: readonly ScrubberTick[];
58
+ /* The indexes in `ticks` of the first and last message on screen; two numbers, so the memo holds while scrolling within them. */
59
+ firstInView: number | null;
60
+ lastInView: number | null;
61
+ onPick(index: number): void;
62
+ chat?: CardChat | null;
63
+ /* Per tick, whether a find hit falls in its stretch of the thread; empty while nothing is searched. */
64
+ found?: readonly boolean[];
65
+ /* The tick of the hit the find bar is on. */
66
+ foundCurrent?: number | null;
67
+ }
68
+
69
+ const NOTHING_FOUND: readonly boolean[] = [];
70
+
71
+ /*
72
+ * The strip at the left edge of a chat view: a tick per message of the person, per wake by tasks and
73
+ * per other message with a bookmark, the ones on screen bright and the rest dimmed, a bookmark in the accent, growing only under the pointer. A single hover card follows the pointer along the strip, anchored at the tick
74
+ * it points at, so a thousand ticks are a thousand spans and not a thousand popups. While a find is
75
+ * open, a short mark at the strip's right edge stands beside every tick with a hit under it.
76
+ */
77
+ export const Scrubber = memo(function Scrubber({
78
+ ticks,
79
+ firstInView,
80
+ lastInView,
81
+ onPick,
82
+ chat = null,
83
+ found = NOTHING_FOUND,
84
+ foundCurrent = null
85
+ }: ScrubberProps) {
86
+ const { t } = useTranslation('agent-chat');
87
+ // State rather than a ref. The card's anchor is built during render and has to change when the strip does.
88
+ const [strip, setStrip] = useState<HTMLDivElement | null>(null);
89
+ const [height, setHeight] = useState(0);
90
+ // The message the card is about; it stays while the card closes, so the card never empties as it fades.
91
+ const [hovered, setHovered] = useState<number | null>(null);
92
+ // Apart from `hovered`, which outlives the pointer for the card's sake. The ticks shrink back the moment it leaves.
93
+ const [pointing, setPointing] = useState(false);
94
+ const [cardOpen, setCardOpen] = useState(false);
95
+ // The message a right-click was on, kept apart from `hovered`, which moves on with the pointer while the menu is up.
96
+ const [menuIndex, setMenuIndex] = useState<number | null>(null);
97
+ const [menuOpen, setMenuOpen] = useState(false);
98
+
99
+ useEffect(() => {
100
+ if (strip === null || typeof ResizeObserver === 'undefined') {
101
+ return;
102
+ }
103
+ const observer = new ResizeObserver(() => setHeight(strip.offsetHeight));
104
+ observer.observe(strip);
105
+ return () => observer.disconnect();
106
+ }, [strip]);
107
+
108
+ const kinds = useMemo(() => ticks.map((tick) => tick.kind), [ticks]);
109
+ const marked = useMemo(() => ticks.map((tick) => tick.bookmark !== null), [ticks]);
110
+ const layout = useMemo(() => layoutTicks(kinds, height, marked, found), [kinds, height, marked, found]);
111
+ const currentSlot = foundCurrent === null ? null : slotOf(layout, foundCurrent);
112
+ const inView = firstInView === null || lastInView === null ? null : { first: firstInView, last: lastInView };
113
+ const hoveredTick = hovered === null ? null : (ticks[hovered] ?? null);
114
+ const hoveredSlot = hovered === null ? null : slotOf(layout, hovered);
115
+ const anchorY = hoveredSlot === null ? null : layout.slots[hoveredSlot]!.y;
116
+
117
+ const anchor = useMemo(() => {
118
+ const element = strip;
119
+ if (anchorY === null || element === null) {
120
+ return undefined;
121
+ }
122
+ return {
123
+ contextElement: element,
124
+ getBoundingClientRect: () => {
125
+ const rect = element.getBoundingClientRect();
126
+ return DOMRect.fromRect({ x: rect.left, y: rect.top + anchorY + TICK_HEIGHT_PX / 2, width: rect.width, height: 0 });
127
+ }
128
+ };
129
+ }, [anchorY, strip]);
130
+
131
+ const pointAt = (e: ReactMouseEvent<HTMLDivElement>): number | null => messageAt(layout, e.clientY - e.currentTarget.getBoundingClientRect().top);
132
+
133
+ const actionsFor = (tick: ScrubberTick | null): CardAction[] =>
134
+ tick === null ? [] : Object.values(CARD_ACTIONS).filter((action) => action.offers(tick, chat));
135
+ const actions = actionsFor(hoveredTick);
136
+ /* The message a bookmark goes on: the one it is on already, or the person's own. A wake is the machine's and holds none. */
137
+ const markableOf = (tick: ScrubberTick | null): string | null =>
138
+ tick === null || chat === null ? null : (tick.bookmark?.itemId ?? (tick.kind === 'person' ? tick.id : null));
139
+ const menuTick = menuIndex === null ? null : (ticks[menuIndex] ?? null);
140
+ const menuMarkable = markableOf(menuTick);
141
+
142
+ const onContextMenu: NonNullable<ComponentProps<typeof ContextMenu.Trigger>['onContextMenu']> = (e) => {
143
+ // The hover card is a child here as well, and a click in it is about no tick.
144
+ const index = strip === null || cameThroughPortal(e) ? null : messageAt(layout, e.clientY - strip.getBoundingClientRect().top);
145
+ const tick = index === null ? null : (ticks[index] ?? null);
146
+ if (tick === null || (actionsFor(tick).length === 0 && markableOf(tick) === null)) {
147
+ e.preventBaseUIHandler();
148
+ return;
149
+ }
150
+ setMenuIndex(index);
151
+ };
152
+
153
+ return (
154
+ <ContextMenu.Root onOpenChange={setMenuOpen}>
155
+ <ContextMenu.Trigger className="h-full w-full" onContextMenu={onContextMenu}>
156
+ <PreviewCard.Root open={cardOpen && !menuOpen} onOpenChange={setCardOpen}>
157
+ <PreviewCard.Trigger
158
+ delay={150}
159
+ closeDelay={150}
160
+ render={
161
+ <div
162
+ ref={setStrip}
163
+ role="navigation"
164
+ aria-label={t('scrubber.strip')}
165
+ className="relative h-full w-full cursor-pointer select-none"
166
+ onPointerMove={(e) => {
167
+ setPointing(true);
168
+ const index = pointAt(e);
169
+ if (index !== hovered) {
170
+ setHovered(index);
171
+ }
172
+ }}
173
+ onPointerLeave={() => setPointing(false)}
174
+ onClick={(e) => {
175
+ const index = pointAt(e);
176
+ if (index !== null) {
177
+ onPick(index);
178
+ }
179
+ }}
180
+ />
181
+ }
182
+ >
183
+ {layout.slots.map((slot, index) => {
184
+ const distance = pointing && hoveredSlot !== null ? Math.abs(index - hoveredSlot) : null;
185
+ // A bookmark is a place to find again from anywhere, so it never dims with the rest.
186
+ const bright = slot.marked || slotInView(slot, inView) || distance === 0;
187
+ return (
188
+ <Fragment key={slot.first}>
189
+ <span
190
+ aria-hidden
191
+ className={clsx(
192
+ 'absolute left-1 rounded-full transition-[width,opacity] duration-100 ease-out motion-reduce:transition-none',
193
+ slot.marked ? 'bg-accent' : slot.kind === 'wake' ? 'bg-text-muted' : 'bg-text',
194
+ bright ? 'opacity-100' : 'opacity-30'
195
+ )}
196
+ style={{ top: slot.y, height: TICK_HEIGHT_PX, width: tickWidth(slot.kind, distance) }}
197
+ />
198
+ {slot.found && (
199
+ <span
200
+ aria-hidden
201
+ className={clsx(
202
+ 'absolute right-0 rounded-full bg-find-current',
203
+ index === currentSlot ? 'opacity-100' : 'opacity-50'
204
+ )}
205
+ style={{ top: slot.y, height: TICK_HEIGHT_PX, width: FOUND_WIDTH_PX }}
206
+ />
207
+ )}
208
+ </Fragment>
209
+ );
210
+ })}
211
+ </PreviewCard.Trigger>
212
+ <PreviewCard.Popup anchor={anchor} side="right" align="center" sideOffset={4} className="w-72">
213
+ {hoveredTick !== null && (
214
+ <>
215
+ <div className="px-2.5 pt-1.5 pb-2">
216
+ <div className="flex items-baseline justify-between gap-3 text-xs text-text-faint">
217
+ {hoveredTick.bookmark === null ? (
218
+ <span>{hoveredTick.kind === 'person' ? t('rows.user.heading') : t('scrubber.tasks')}</span>
219
+ ) : (
220
+ <span className="flex min-w-0 items-center gap-1 self-center text-text-muted">
221
+ <Icon icon={Bookmark} size={12} className="shrink-0 text-accent" />
222
+ <span className="truncate">{hoveredTick.bookmark.name ?? t('bookmarks.unnamed')}</span>
223
+ </span>
224
+ )}
225
+ <time className="tabular-nums">{timeOf(hoveredTick.createdAt)}</time>
226
+ </div>
227
+ <p className="mt-1 line-clamp-4 text-sm break-words whitespace-pre-line text-text">
228
+ {hoveredTick.text.slice(0, PREVIEW_CHARS) || t('pickers.stash.noText')}
229
+ </p>
230
+ </div>
231
+ {actions.length > 0 && (
232
+ <>
233
+ <Menu.Separator />
234
+ {actions.map((action) => (
235
+ <button
236
+ key={action.label}
237
+ type="button"
238
+ className="menu-item w-full hover:bg-surface-hover"
239
+ onClick={() => action.run(hoveredTick, chat)}
240
+ >
241
+ <Icon icon={action.icon} size={14} /> {t(action.label)}
242
+ </button>
243
+ ))}
244
+ </>
245
+ )}
246
+ </>
247
+ )}
248
+ </PreviewCard.Popup>
249
+ </PreviewCard.Root>
250
+ </ContextMenu.Trigger>
251
+ <ContextMenu.Popup>
252
+ {menuTick !== null &&
253
+ actionsFor(menuTick).map((action) => (
254
+ <ContextMenu.Item key={action.label} onClick={() => action.run(menuTick, chat)}>
255
+ <Icon icon={action.icon} size={14} /> {t(action.label)}
256
+ </ContextMenu.Item>
257
+ ))}
258
+ {menuTick !== null && actionsFor(menuTick).length > 0 && menuMarkable !== null && <ContextMenu.Separator />}
259
+ {chat !== null && menuIndex !== null && menuMarkable !== null && (
260
+ <BookmarkMenuItems chatId={chat.chatId} itemId={menuMarkable} onName={() => onPick(menuIndex)} />
261
+ )}
262
+ </ContextMenu.Popup>
263
+ </ContextMenu.Root>
264
+ );
265
+ });
@@ -0,0 +1,66 @@
1
+ import { Fragment, type ReactNode } from 'react';
2
+ import clsx from 'clsx';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { ChevronRight, X } from 'lucide-react';
5
+ import { breadcrumbOf, MAIN_AGENT, trailTo, useOpenableSubagents, useSubagentTrail } from '../subagent-view';
6
+ import { SubagentStopButton } from './SubagentStopButton';
7
+ import { Icon, IconButton } from '@adecore/ui';
8
+
9
+ const CRUMB_LINK = 'truncate text-text-muted hover:text-text';
10
+
11
+ /*
12
+ * The chat's own title where the bar draws it. While a sub-agent stands in the chat's place it is the first crumb of the way down, so pressing it goes back to the main agent.
13
+ */
14
+ export function SubagentTitleCrumb({ chatId, className, children }: { chatId: string; className?: string; children: ReactNode }) {
15
+ const { trail, show } = useSubagentTrail(chatId);
16
+ if (trail.length === 0) {
17
+ return <>{children}</>;
18
+ }
19
+ return (
20
+ <button type="button" className={clsx('flex min-w-0 items-center', className)} onClick={() => show(MAIN_AGENT)}>
21
+ {children}
22
+ </button>
23
+ );
24
+ }
25
+
26
+ /*
27
+ * The way down after the chat's title, each step a way back up, and a close that goes straight back
28
+ * to the main agent. A bar that does not draw the title itself passes it, and it opens the way.
29
+ * Nothing while the main agent itself is on screen.
30
+ */
31
+ export function SubagentBreadcrumb({ chatId, title, className }: { chatId: string; title?: string; className?: string }) {
32
+ const { t } = useTranslation('agent-chat');
33
+ const { trail, show } = useSubagentTrail(chatId);
34
+ const subagents = useOpenableSubagents(chatId);
35
+ if (trail.length === 0) {
36
+ return null;
37
+ }
38
+ const steps = breadcrumbOf(trail);
39
+ // Only a row of the chat's own thread can be stopped; a grandchild belongs to the sub-agent that opened it.
40
+ const shown = trail.length === 1 ? subagents.find((item) => item.toolUseId === trail[0]!.toolUseId) : undefined;
41
+ return (
42
+ <nav aria-label={t('subagents.title')} className={clsx('flex min-w-0 items-center gap-1 text-xs', className)}>
43
+ {title !== undefined && (
44
+ <button type="button" className={clsx(CRUMB_LINK, 'min-w-0 shrink')} onClick={() => show(MAIN_AGENT)}>
45
+ {title}
46
+ </button>
47
+ )}
48
+ {steps.map((step) => (
49
+ <Fragment key={step.depth}>
50
+ <Icon icon={ChevronRight} size={12} className="shrink-0 text-text-faint" />
51
+ {step.current ? (
52
+ <span aria-current="page" className="min-w-0 truncate text-text">
53
+ {step.label}
54
+ </span>
55
+ ) : (
56
+ <button type="button" className={clsx(CRUMB_LINK, 'min-w-0 shrink')} onClick={() => show(trailTo(trail, step.depth))}>
57
+ {step.label}
58
+ </button>
59
+ )}
60
+ </Fragment>
61
+ ))}
62
+ {shown !== undefined && <SubagentStopButton chatId={chatId} item={shown} size="sm" />}
63
+ <IconButton icon={X} size="sm" label={t('subagents.backToMain')} onClick={() => show(MAIN_AGENT)} />
64
+ </nav>
65
+ );
66
+ }
@@ -0,0 +1,67 @@
1
+ import { Fragment, useState } from 'react';
2
+ import clsx from 'clsx';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { ChevronDown } from 'lucide-react';
5
+ import { entryTimeOf, statusWordOf, subagentFacts, taskIdOf } from '../subagent-list';
6
+ import { useSubagentItem } from '../subagent-view';
7
+ import { StatusIcon } from './ChatActivity';
8
+ import { Markdown } from './Markdown';
9
+ import { Icon, useNow } from '@adecore/ui';
10
+ import { useModelName } from '../../agents/model-name';
11
+ import { useChatRow } from '../../state/chats';
12
+ import { chatHost } from '../../host';
13
+ import { useChatScope } from '../../scope';
14
+
15
+ /*
16
+ * What a sub-agent is, over its conversation: its state and time, the model it runs on and what it
17
+ * spent, and the task it was given behind a fold. Nothing for a conversation whose row the thread lost.
18
+ */
19
+ export function SubagentInfo({ chatId, toolUseId }: { chatId: string; toolUseId: string }) {
20
+ const { t } = useTranslation('agent-chat');
21
+ const { id } = useChatScope();
22
+ const item = useSubagentItem(chatId, toolUseId);
23
+ const provider = useChatRow(chatId, (row) => row?.info.provider);
24
+ const task = chatHost().tasks.useTask(id, item === null ? null : taskIdOf(item));
25
+ const modelName = useModelName(provider, item?.model ?? '');
26
+ const now = useNow(1000, item?.status === 'running');
27
+ const [open, setOpen] = useState(false);
28
+ if (item === null) {
29
+ return null;
30
+ }
31
+ const word = statusWordOf(item, task);
32
+ const facts = subagentFacts(item, item.model === undefined ? null : modelName);
33
+ return (
34
+ <section aria-label={t('subagents.info.label')} className="shrink-0 border-b border-border px-4 py-2">
35
+ <div className="chat-column-content flex flex-col gap-2">
36
+ <div className="flex min-w-0 items-center gap-2 text-xs text-text-faint">
37
+ <StatusIcon word={word} />
38
+ <span className="sr-only">{t(`common.status.${word}`)}</span>
39
+ <span className="shrink-0 text-text-muted tabular-nums">{entryTimeOf(item, task, now)}</span>
40
+ {facts.map((fact) => (
41
+ <Fragment key={fact}>
42
+ <span aria-hidden="true">·</span>
43
+ <span className="min-w-0 truncate">{fact}</span>
44
+ </Fragment>
45
+ ))}
46
+ <span className="grow" />
47
+ {item.prompt && (
48
+ <button
49
+ type="button"
50
+ className="flex shrink-0 items-center gap-1.5 text-text-muted hover:text-text"
51
+ aria-expanded={open}
52
+ onClick={() => setOpen((current) => !current)}
53
+ >
54
+ <Icon icon={ChevronDown} size={12} className={clsx('transition-transform', open && 'rotate-180')} />
55
+ {open ? t('subagents.info.hideTask') : t('subagents.info.showTask')}
56
+ </button>
57
+ )}
58
+ </div>
59
+ {open && item.prompt && (
60
+ <div className="max-h-60 overflow-auto rounded-md border border-border bg-surface-raised px-3 py-2 text-xs select-text">
61
+ <Markdown text={item.prompt} />
62
+ </div>
63
+ )}
64
+ </div>
65
+ </section>
66
+ );
67
+ }
@@ -0,0 +1,45 @@
1
+ import { useTranslation } from 'react-i18next';
2
+ import { Square } from 'lucide-react';
3
+ import type { ChatSubagentItem } from '@adecore/agent-contracts';
4
+ import { chatHost } from '../../host';
5
+ import { useChatScope } from '../../scope';
6
+ import { useChatRow } from '../../state/chats';
7
+ import { useChatActions } from '../actions';
8
+ import { stopLabel, stopOf, subagentTitle } from '../subagent-list';
9
+ import { IconButton } from '@adecore/ui';
10
+
11
+ /* Stops one active sub-agent of a chat, or says nothing when stopping it is not on offer. */
12
+ export function SubagentStopButton({
13
+ chatId,
14
+ item,
15
+ size = 'xs',
16
+ className
17
+ }: {
18
+ chatId: string;
19
+ item: ChatSubagentItem;
20
+ size?: 'sm' | 'xs';
21
+ className?: string;
22
+ }) {
23
+ const { t } = useTranslation('agent-chat');
24
+ const { id } = useChatScope();
25
+ const actions = useChatActions();
26
+ const turnRunning = useChatRow(chatId, (row) => (row?.info.activeTurnId ?? null) !== null);
27
+ const stop = stopOf(item, turnRunning);
28
+ if (stop === null) {
29
+ return null;
30
+ }
31
+ const send = (): void => {
32
+ void actions.stopSubagent(chatId, item.toolUseId).catch((e: unknown) => {
33
+ const description = e instanceof Error ? e.message : t('subagents.noAnswer');
34
+ chatHost().notify({ kind: 'error', title: t('subagents.stopFailed'), description });
35
+ });
36
+ };
37
+ const run = (): void => {
38
+ if (stop === 'task' && item.childId !== undefined) {
39
+ chatHost().confirm.stopTask(id, item.childId, subagentTitle(item), send);
40
+ return;
41
+ }
42
+ send();
43
+ };
44
+ return <IconButton icon={Square} size={size} label={stopLabel(stop)} className={className} onClick={run} />;
45
+ }
@@ -0,0 +1,170 @@
1
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Bot, LoaderCircle } from 'lucide-react';
4
+ import { deriveTimelineRows } from '../logic/timeline';
5
+ import { INITIAL_CONVERSATION, SubagentConversation, type SubagentConversationState } from '../subagent-conversation';
6
+ import { useSubagentSupport } from '../subagent-support';
7
+ import { firstRowInView, restoreAnchor, wantsEarlier, type ReadingAnchor } from '../timeline-scroll';
8
+ import { EMPTY_TARGET, readTimelineTarget, type TimelineTarget } from '../logic/timeline-target';
9
+ import { openBelow, useSubagentTrail, type SubagentStep } from '../subagent-view';
10
+ import { Row } from './rows/Rows';
11
+ import { ReplyContext } from './reply-context';
12
+ import { SubagentInfo } from './SubagentInfo';
13
+ import { TimelineMenuPopup } from './TimelineMenu';
14
+ import { FOLLOW_THRESHOLD_PX, rowRhythm } from './rows/row-rhythm';
15
+ import { useToggleSet } from './useToggleSet';
16
+ import { FileLinkContext } from './file-links';
17
+ import { useChatRow } from '../../state/chats';
18
+ import { useChatScope } from '../../scope';
19
+ import { EmptyState, ContextMenu, Icon } from '@adecore/ui';
20
+
21
+ const NO_TURNS = new Set<string>();
22
+
23
+ /*
24
+ * A sub-agent's whole conversation, read back from the host and grown while it is on screen. It
25
+ * stands in the chat's own place, read-only, and a sub-agent it opened goes one level further down.
26
+ */
27
+ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUseId: string }) {
28
+ const { t } = useTranslation('agent-chat');
29
+ const scope = useChatScope();
30
+ const { trail, show } = useSubagentTrail(chatId);
31
+ const [state, setState] = useState<SubagentConversationState>(INITIAL_CONVERSATION);
32
+ const controller = useRef<SubagentConversation | null>(null);
33
+ const scrollRef = useRef<HTMLDivElement>(null);
34
+ const threadRef = useRef<HTMLDivElement>(null);
35
+ const [target, setTarget] = useState<TimelineTarget>(EMPTY_TARGET);
36
+ const followRef = useRef(true);
37
+ const stopFollowing = useCallback(() => {
38
+ followRef.current = false;
39
+ }, []);
40
+ const groups = useToggleSet(stopFollowing);
41
+ const subagents = useToggleSet(stopFollowing);
42
+ // The row being read when an earlier page was asked for, so it stays where it was once that page is in.
43
+ const anchorRef = useRef<ReadingAnchor | null>(null);
44
+ const headRef = useRef<string | undefined>(undefined);
45
+ const cwd = useChatRow(chatId, (row) => row?.info.cwd ?? null);
46
+
47
+ useEffect(() => {
48
+ const conversation = new SubagentConversation(scope.transport, chatId, toolUseId, setState);
49
+ controller.current = conversation;
50
+ void conversation.start();
51
+ return () => {
52
+ conversation.dispose();
53
+ controller.current = null;
54
+ };
55
+ }, [scope, chatId, toolUseId]);
56
+
57
+ useEffect(() => {
58
+ if (state.unsupported) {
59
+ useSubagentSupport.getState().markUnsupported(scope.id);
60
+ }
61
+ }, [state.unsupported, scope]);
62
+
63
+ const rows = useMemo(
64
+ () => deriveTimelineRows(state.items, { expandedGroups: groups.ids, expandedTurns: NO_TURNS, expandedSubagents: subagents.ids, activeTurnId: null }),
65
+ [state.items, groups.ids, subagents.ids]
66
+ );
67
+
68
+ useLayoutEffect(() => {
69
+ const element = scrollRef.current;
70
+ if (element === null) {
71
+ return;
72
+ }
73
+ const head = state.items[0]?.id;
74
+ const moved = head !== headRef.current;
75
+ headRef.current = head;
76
+ if (moved && anchorRef.current !== null && !followRef.current) {
77
+ restoreAnchor(element, anchorRef.current);
78
+ } else if (followRef.current) {
79
+ element.scrollTop = element.scrollHeight;
80
+ }
81
+ if (moved) {
82
+ anchorRef.current = null;
83
+ }
84
+ }, [rows, state.live, state.items]);
85
+
86
+ const loadEarlier = useCallback(() => {
87
+ const element = scrollRef.current;
88
+ if (element === null || state.cursor === null || !wantsEarlier(element)) {
89
+ return;
90
+ }
91
+ // Taken on every call, since the reader may scroll on while a page is on its way.
92
+ anchorRef.current = firstRowInView(element);
93
+ void controller.current?.loadEarlier();
94
+ }, [state.cursor]);
95
+
96
+ // Close to the top, the page before goes in; again after each page that left too little above the reader.
97
+ // A page that failed changes no row, so it is asked for again only on the next scroll.
98
+ useEffect(() => {
99
+ if (state.status === 'ready') {
100
+ loadEarlier();
101
+ }
102
+ }, [rows, state.status, loadEarlier]);
103
+
104
+ const openChild = (step: SubagentStep): void => {
105
+ show(openBelow(trail, step));
106
+ };
107
+
108
+ return (
109
+ <div className="flex h-full min-h-0 flex-col">
110
+ <SubagentInfo chatId={chatId} toolUseId={toolUseId} />
111
+ {state.status === 'loading' && <div className="chat-column-content px-4 pt-4 text-xs text-text-faint">{t('subagents.loading')}</div>}
112
+ {state.status === 'failed' && (
113
+ <EmptyState icon={Bot}>{state.unsupported ? t('subagents.unsupported') : (state.error ?? t('subagents.unreadable'))}</EmptyState>
114
+ )}
115
+ {state.status === 'ready' && (
116
+ <FileLinkContext.Provider value={state.context?.cwd ?? cwd}>
117
+ <ReplyContext.Provider value={state.context ?? { provider: state.source === 'codex-thread' ? 'codex' : 'claude' }}>
118
+ <div
119
+ ref={scrollRef}
120
+ className="chat-thread relative min-h-0 grow overflow-auto px-4 pt-4 pb-3"
121
+ // The thread keeps its reader in place itself when a page goes in above.
122
+ style={{ overflowAnchor: 'none' }}
123
+ onScroll={(event) => {
124
+ const element = event.currentTarget;
125
+ followRef.current = element.scrollHeight - element.scrollTop - element.clientHeight < FOLLOW_THRESHOLD_PX;
126
+ loadEarlier();
127
+ }}
128
+ >
129
+ {/* The same menu the chat's own thread has; this transcript is read-only, so the chat
130
+ it belongs to is not passed on and the fork item stays out. */}
131
+ <ContextMenu.Root>
132
+ <ContextMenu.Trigger
133
+ ref={threadRef}
134
+ className="chat-column-content"
135
+ onContextMenu={(event) => setTarget(readTimelineTarget(event.target as HTMLElement, threadRef.current, rows))}
136
+ >
137
+ {state.loadingEarlier && (
138
+ <div className="pointer-events-none sticky top-0 z-10 flex h-0 justify-center" role="status">
139
+ <Icon icon={LoaderCircle} size={16} className="animate-spin text-text-faint" />
140
+ <span className="sr-only">{t('timeline.loadingEarlier')}</span>
141
+ </div>
142
+ )}
143
+ {rows.length === 0 && !state.live && <EmptyState icon={Bot}>{t('rows.subagent.nothingYet')}</EmptyState>}
144
+ {rows.map((row, index) => {
145
+ const previous = index > 0 ? rows[index - 1]! : null;
146
+ return (
147
+ <div key={row.id} data-item-id={row.id} className={rowRhythm(row, previous)}>
148
+ <Row
149
+ row={row}
150
+ chatId={chatId}
151
+ toggleGroup={groups.toggle}
152
+ toggleTurn={() => undefined}
153
+ toggleSubagent={subagents.toggle}
154
+ openSubagent={() => undefined}
155
+ openConversation={openChild}
156
+ />
157
+ </div>
158
+ );
159
+ })}
160
+ {state.live && <div className="chat-live-text pt-1 text-xs">{t('subagents.working')}</div>}
161
+ </ContextMenu.Trigger>
162
+ <TimelineMenuPopup target={target} thread={threadRef} />
163
+ </ContextMenu.Root>
164
+ </div>
165
+ </ReplyContext.Provider>
166
+ </FileLinkContext.Provider>
167
+ )}
168
+ </div>
169
+ );
170
+ }