@adecore/agents-react 0.23.1 → 0.24.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 (344) hide show
  1. package/README.md +14 -11
  2. package/dist/agents/AccountDot.js +3 -1
  3. package/dist/agents/accounts.d.ts +3 -2
  4. package/dist/agents/accounts.js +5 -6
  5. package/dist/chat/actions.d.ts +2 -2
  6. package/dist/chat/actions.js +8 -5
  7. package/dist/chat/bookmarks.d.ts +5 -3
  8. package/dist/chat/bookmarks.js +13 -14
  9. package/dist/chat/chat-client.d.ts +1 -0
  10. package/dist/chat/chat-client.js +7 -0
  11. package/dist/chat/composer-send.d.ts +3 -0
  12. package/dist/chat/composer-send.js +22 -0
  13. package/dist/chat/forks.js +22 -5
  14. package/dist/chat/index.d.ts +31 -0
  15. package/dist/chat/index.js +28 -0
  16. package/dist/chat/logic/embedded-code.d.ts +8 -0
  17. package/dist/chat/logic/embedded-code.js +371 -0
  18. package/dist/chat/logic/fork.js +1 -0
  19. package/dist/chat/logic/goal.d.ts +6 -0
  20. package/dist/chat/logic/goal.js +26 -0
  21. package/dist/chat/logic/scrubber.d.ts +1 -1
  22. package/dist/chat/logic/scrubber.js +6 -5
  23. package/dist/chat/logic/thought.d.ts +3 -0
  24. package/dist/chat/logic/thought.js +35 -0
  25. package/dist/chat/logic/thread-cards.js +2 -0
  26. package/dist/chat/logic/timeline.d.ts +5 -0
  27. package/dist/chat/logic/timeline.js +22 -1
  28. package/dist/chat/logic/tool-catalog.d.ts +1 -0
  29. package/dist/chat/logic/tool-catalog.js +3 -1
  30. package/dist/chat/logic/tools.d.ts +14 -0
  31. package/dist/chat/logic/tools.js +57 -0
  32. package/dist/chat/preferences.d.ts +6 -0
  33. package/dist/chat/preferences.js +19 -3
  34. package/dist/chat/reading-width.d.ts +9 -0
  35. package/dist/chat/reading-width.js +27 -0
  36. package/dist/chat/send-key.d.ts +10 -0
  37. package/dist/chat/send-key.js +24 -0
  38. package/dist/chat/ui/BookmarkMarker.js +4 -2
  39. package/dist/chat/ui/BookmarkSubmenu.js +4 -3
  40. package/dist/chat/ui/ChatActivity.js +14 -10
  41. package/dist/chat/ui/ChatReferenceChip.js +3 -4
  42. package/dist/chat/ui/CodeBlock.d.ts +2 -1
  43. package/dist/chat/ui/CodeBlock.js +14 -96
  44. package/dist/chat/ui/Composer.d.ts +1 -1
  45. package/dist/chat/ui/Composer.js +130 -907
  46. package/dist/chat/ui/ComposerInput.js +5 -4
  47. package/dist/chat/ui/EditDiff.js +3 -2
  48. package/dist/chat/ui/GoalDock.d.ts +9 -0
  49. package/dist/chat/ui/GoalDock.js +63 -0
  50. package/dist/chat/ui/LimitState.js +5 -3
  51. package/dist/chat/ui/Markdown.d.ts +2 -1
  52. package/dist/chat/ui/Markdown.js +70 -31
  53. package/dist/chat/ui/MermaidBlock.d.ts +3 -0
  54. package/dist/chat/ui/MermaidBlock.js +73 -0
  55. package/dist/chat/ui/MessageActions.js +12 -7
  56. package/dist/chat/ui/Pickers.js +6 -5
  57. package/dist/chat/ui/PromptComposer.js +4 -3
  58. package/dist/chat/ui/RowTime.d.ts +3 -0
  59. package/dist/chat/ui/RowTime.js +12 -0
  60. package/dist/chat/ui/RunSettings.js +5 -4
  61. package/dist/chat/ui/ShellCommand.d.ts +3 -0
  62. package/dist/chat/ui/ShellCommand.js +110 -0
  63. package/dist/chat/ui/SubagentInfo.js +4 -3
  64. package/dist/chat/ui/SubagentStopButton.js +4 -3
  65. package/dist/chat/ui/SubagentTimeline.js +4 -1
  66. package/dist/chat/ui/Timeline.js +70 -40
  67. package/dist/chat/ui/TimelineMenu.d.ts +2 -1
  68. package/dist/chat/ui/TimelineMenu.js +18 -8
  69. package/dist/chat/ui/UnifiedDiff.js +3 -2
  70. package/dist/chat/ui/VisualFrame.js +6 -7
  71. package/dist/chat/ui/code-theme.d.ts +2 -1
  72. package/dist/chat/ui/code-theme.js +4 -4
  73. package/dist/chat/ui/composer/ComposerControls.d.ts +27 -0
  74. package/dist/chat/ui/composer/ComposerControls.js +34 -0
  75. package/dist/chat/ui/composer/ComposerMenu.d.ts +4 -0
  76. package/dist/chat/ui/composer/ComposerMenu.js +46 -0
  77. package/dist/chat/ui/composer/DraftParts.d.ts +18 -0
  78. package/dist/chat/ui/composer/DraftParts.js +25 -0
  79. package/dist/chat/ui/composer/QueuedMessages.d.ts +10 -0
  80. package/dist/chat/ui/composer/QueuedMessages.js +20 -0
  81. package/dist/chat/ui/composer/key-handler.d.ts +20 -0
  82. package/dist/chat/ui/composer/key-handler.js +150 -0
  83. package/dist/chat/ui/composer/keys.d.ts +7 -2
  84. package/dist/chat/ui/composer/keys.js +19 -9
  85. package/dist/chat/ui/composer/placeholder.d.ts +9 -0
  86. package/dist/chat/ui/composer/placeholder.js +38 -0
  87. package/dist/chat/ui/composer/surface.d.ts +1 -0
  88. package/dist/chat/ui/composer/surface.js +44 -0
  89. package/dist/chat/ui/composer/use-chat-controls.d.ts +28 -0
  90. package/dist/chat/ui/composer/use-chat-controls.js +112 -0
  91. package/dist/chat/ui/composer/use-compaction-offer.d.ts +2 -0
  92. package/dist/chat/ui/composer/use-compaction-offer.js +23 -0
  93. package/dist/chat/ui/composer/use-composer-draft.d.ts +14 -0
  94. package/dist/chat/ui/composer/use-composer-draft.js +39 -0
  95. package/dist/chat/ui/composer/use-composer-menus.d.ts +52 -0
  96. package/dist/chat/ui/composer/use-composer-menus.js +232 -0
  97. package/dist/chat/ui/composer/use-composer-submit.d.ts +32 -0
  98. package/dist/chat/ui/composer/use-composer-submit.js +113 -0
  99. package/dist/chat/ui/composer/use-draft-files.d.ts +27 -0
  100. package/dist/chat/ui/composer/use-draft-files.js +111 -0
  101. package/dist/chat/ui/composer/use-notice.d.ts +6 -0
  102. package/dist/chat/ui/composer/use-notice.js +18 -0
  103. package/dist/chat/ui/composer/use-pending-requests.d.ts +2 -0
  104. package/dist/chat/ui/composer/use-pending-requests.js +22 -0
  105. package/dist/chat/ui/composer/use-prompt-history.d.ts +8 -0
  106. package/dist/chat/ui/composer/use-prompt-history.js +57 -0
  107. package/dist/chat/ui/composer/use-prompt-hold.d.ts +6 -0
  108. package/dist/chat/ui/composer/use-prompt-hold.js +48 -0
  109. package/dist/chat/ui/composer/use-take-back.d.ts +13 -0
  110. package/dist/chat/ui/composer/use-take-back.js +82 -0
  111. package/dist/chat/ui/diff/EditorEditDiff.js +4 -3
  112. package/dist/chat/ui/diff/LegacyDiffPool.js +3 -2
  113. package/dist/chat/ui/diff/LegacyEditDiff.js +3 -2
  114. package/dist/chat/ui/diff/LegacyUnifiedDiff.js +3 -2
  115. package/dist/chat/ui/diff/syntax-runtime.d.ts +2 -1
  116. package/dist/chat/ui/diff/syntax-runtime.js +9 -5
  117. package/dist/chat/ui/diff-syntax.d.ts +2 -2
  118. package/dist/chat/ui/diff-syntax.js +1 -2
  119. package/dist/chat/ui/diff-theme.js +10 -9
  120. package/dist/chat/ui/file-links.d.ts +1 -1
  121. package/dist/chat/ui/file-links.js +7 -5
  122. package/dist/chat/ui/highlighter.d.ts +27 -0
  123. package/dist/chat/ui/highlighter.js +151 -0
  124. package/dist/chat/ui/index.d.ts +67 -0
  125. package/dist/chat/ui/index.js +64 -0
  126. package/dist/chat/ui/intelligent-ui/UiReply.js +3 -2
  127. package/dist/chat/ui/intelligent-ui/renderers/content.js +3 -2
  128. package/dist/chat/ui/intelligent-ui/use-ui-links.js +2 -2
  129. package/dist/chat/ui/intelligent-ui/use-ui-queries.js +4 -3
  130. package/dist/chat/ui/markdown-blocks.d.ts +8 -0
  131. package/dist/chat/ui/markdown-blocks.js +260 -12
  132. package/dist/chat/ui/mermaid.d.ts +23 -0
  133. package/dist/chat/ui/mermaid.js +93 -0
  134. package/dist/chat/ui/rows/ForksRow.js +5 -3
  135. package/dist/chat/ui/rows/GeneratedImageRow.js +6 -4
  136. package/dist/chat/ui/rows/MessageRows.d.ts +4 -0
  137. package/dist/chat/ui/rows/MessageRows.js +36 -13
  138. package/dist/chat/ui/rows/Rows.js +3 -1
  139. package/dist/chat/ui/rows/SubagentRow.js +3 -2
  140. package/dist/chat/ui/rows/VisualRow.js +3 -2
  141. package/dist/chat/ui/rows/WorkRows.d.ts +6 -3
  142. package/dist/chat/ui/rows/WorkRows.js +57 -20
  143. package/dist/chat/ui/rows/WorkflowRow.js +3 -2
  144. package/dist/chat/ui/timestamp-format.d.ts +1 -0
  145. package/dist/chat/ui/timestamp-format.js +2 -0
  146. package/dist/chat/ui/use-diff-source-tokens.js +6 -3
  147. package/dist/chat/ui/visual-theme.js +3 -2
  148. package/dist/host.d.ts +9 -6
  149. package/dist/host.js +19 -8
  150. package/dist/index.d.ts +8 -0
  151. package/dist/index.js +9 -0
  152. package/dist/locales/en/agent-chat.d.ts +658 -0
  153. package/dist/locales/en/agent-chat.js +657 -0
  154. package/dist/locales/en/agent-prompts.d.ts +44 -0
  155. package/dist/locales/en/agent-prompts.js +43 -0
  156. package/dist/locales/en/agent-providers.d.ts +132 -0
  157. package/dist/locales/en/agent-providers.js +131 -0
  158. package/dist/locales/en/agent-usage.d.ts +132 -0
  159. package/dist/locales/en/agent-usage.js +131 -0
  160. package/dist/locales/nl/agent-chat.d.ts +658 -0
  161. package/dist/locales/nl/agent-chat.js +657 -0
  162. package/dist/locales/nl/agent-prompts.d.ts +44 -0
  163. package/dist/locales/nl/agent-prompts.js +43 -0
  164. package/dist/locales/nl/agent-providers.d.ts +132 -0
  165. package/dist/locales/nl/agent-providers.js +131 -0
  166. package/dist/locales/nl/agent-usage.d.ts +132 -0
  167. package/dist/locales/nl/agent-usage.js +131 -0
  168. package/dist/locales.js +9 -8
  169. package/dist/prompts/index.d.ts +11 -0
  170. package/dist/prompts/index.js +12 -0
  171. package/dist/prompts/logic/subjects.d.ts +2 -1
  172. package/dist/prompts/logic/subjects.js +6 -4
  173. package/dist/prompts/ui/ApprovalBody.js +6 -4
  174. package/dist/prompts/ui/PromptCard.js +5 -4
  175. package/dist/prompts/ui/PromptView.js +3 -2
  176. package/dist/prompts/ui/QuestionBody.js +3 -2
  177. package/dist/providers/AccountColors.js +2 -2
  178. package/dist/providers/AccountDetail.js +7 -6
  179. package/dist/providers/AccountVariables.js +3 -1
  180. package/dist/providers/AddAccountForm.js +3 -2
  181. package/dist/providers/ProvidersPane.js +4 -3
  182. package/dist/providers/account-actions.d.ts +4 -3
  183. package/dist/providers/account-actions.js +6 -7
  184. package/dist/providers/index.d.ts +16 -0
  185. package/dist/providers/index.js +17 -0
  186. package/dist/providers/provider-abilities.d.ts +2 -1
  187. package/dist/providers/provider-abilities.js +4 -0
  188. package/dist/state/chats.js +4 -2
  189. package/dist/state/index.d.ts +4 -0
  190. package/dist/state/index.js +5 -0
  191. package/dist/testing/index.d.ts +1 -0
  192. package/dist/testing/index.js +2 -0
  193. package/dist/{prompts/logic/prompts.fixtures.d.ts → testing/prompts.d.ts} +1 -1
  194. package/dist/theme.css +13 -3
  195. package/dist/usage/UsageBreakdown.js +3 -2
  196. package/dist/usage/UsageLimits.js +4 -3
  197. package/dist/usage/UsagePage.js +3 -2
  198. package/dist/usage/index.d.ts +15 -0
  199. package/dist/usage/index.js +15 -0
  200. package/package.json +53 -1031
  201. package/src/agents/AccountDot.tsx +3 -1
  202. package/src/agents/accounts.ts +6 -6
  203. package/src/chat/actions.ts +8 -5
  204. package/src/chat/bookmarks.ts +23 -14
  205. package/src/chat/chat-client.ts +8 -0
  206. package/src/chat/composer-send.ts +27 -0
  207. package/src/chat/forks.ts +27 -6
  208. package/src/chat/index.ts +98 -0
  209. package/src/chat/logic/embedded-code.ts +409 -0
  210. package/src/chat/logic/fork.ts +1 -0
  211. package/src/chat/logic/goal.ts +33 -0
  212. package/src/chat/logic/scrubber.ts +6 -5
  213. package/src/chat/logic/thought.ts +40 -0
  214. package/src/chat/logic/thread-cards.ts +2 -0
  215. package/src/chat/logic/timeline.ts +25 -1
  216. package/src/chat/logic/tool-catalog.ts +5 -1
  217. package/src/chat/logic/tools.ts +76 -0
  218. package/src/chat/preferences.ts +26 -3
  219. package/src/chat/reading-width.ts +39 -0
  220. package/src/chat/send-key.ts +38 -0
  221. package/src/chat/ui/BookmarkMarker.tsx +4 -2
  222. package/src/chat/ui/BookmarkSubmenu.tsx +4 -3
  223. package/src/chat/ui/ChatActivity.tsx +14 -10
  224. package/src/chat/ui/ChatReferenceChip.tsx +3 -4
  225. package/src/chat/ui/CodeBlock.tsx +33 -124
  226. package/src/chat/ui/Composer.tsx +212 -1358
  227. package/src/chat/ui/ComposerInput.tsx +5 -4
  228. package/src/chat/ui/EditDiff.tsx +3 -2
  229. package/src/chat/ui/GoalDock.tsx +104 -0
  230. package/src/chat/ui/LimitState.tsx +5 -3
  231. package/src/chat/ui/Markdown.tsx +113 -39
  232. package/src/chat/ui/MermaidBlock.tsx +119 -0
  233. package/src/chat/ui/MessageActions.tsx +13 -8
  234. package/src/chat/ui/Pickers.tsx +8 -7
  235. package/src/chat/ui/PromptComposer.tsx +4 -3
  236. package/src/chat/ui/RowTime.tsx +21 -0
  237. package/src/chat/ui/RunSettings.tsx +9 -4
  238. package/src/chat/ui/ShellCommand.tsx +134 -0
  239. package/src/chat/ui/SubagentInfo.tsx +4 -3
  240. package/src/chat/ui/SubagentStopButton.tsx +4 -3
  241. package/src/chat/ui/SubagentTimeline.tsx +5 -1
  242. package/src/chat/ui/Timeline.tsx +167 -105
  243. package/src/chat/ui/TimelineMenu.tsx +42 -7
  244. package/src/chat/ui/UnifiedDiff.tsx +3 -2
  245. package/src/chat/ui/VisualFrame.tsx +6 -7
  246. package/src/chat/ui/code-theme.ts +4 -4
  247. package/src/chat/ui/composer/ComposerControls.tsx +147 -0
  248. package/src/chat/ui/composer/ComposerMenu.tsx +138 -0
  249. package/src/chat/ui/composer/DraftParts.tsx +86 -0
  250. package/src/chat/ui/composer/QueuedMessages.tsx +64 -0
  251. package/src/chat/ui/composer/key-handler.ts +174 -0
  252. package/src/chat/ui/composer/keys.ts +32 -10
  253. package/src/chat/ui/composer/placeholder.ts +48 -0
  254. package/src/chat/ui/composer/surface.ts +45 -0
  255. package/src/chat/ui/composer/use-chat-controls.ts +134 -0
  256. package/src/chat/ui/composer/use-compaction-offer.ts +25 -0
  257. package/src/chat/ui/composer/use-composer-draft.ts +71 -0
  258. package/src/chat/ui/composer/use-composer-menus.ts +289 -0
  259. package/src/chat/ui/composer/use-composer-submit.ts +163 -0
  260. package/src/chat/ui/composer/use-draft-files.ts +135 -0
  261. package/src/chat/ui/composer/use-notice.ts +28 -0
  262. package/src/chat/ui/composer/use-pending-requests.ts +23 -0
  263. package/src/chat/ui/composer/use-prompt-history.ts +76 -0
  264. package/src/chat/ui/composer/use-prompt-hold.ts +58 -0
  265. package/src/chat/ui/composer/use-take-back.ts +91 -0
  266. package/src/chat/ui/diff/DiffFallbackBoundary.tsx +2 -2
  267. package/src/chat/ui/diff/EditorEditDiff.tsx +4 -3
  268. package/src/chat/ui/diff/LegacyDiffPool.tsx +3 -2
  269. package/src/chat/ui/diff/LegacyEditDiff.tsx +3 -2
  270. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +3 -2
  271. package/src/chat/ui/diff/syntax-runtime.ts +10 -5
  272. package/src/chat/ui/diff-syntax.ts +2 -2
  273. package/src/chat/ui/diff-theme.ts +10 -9
  274. package/src/chat/ui/file-links.ts +7 -5
  275. package/src/chat/ui/highlighter.ts +185 -0
  276. package/src/chat/ui/index.ts +109 -0
  277. package/src/chat/ui/intelligent-ui/UiReply.tsx +5 -4
  278. package/src/chat/ui/intelligent-ui/renderers/content.tsx +3 -2
  279. package/src/chat/ui/intelligent-ui/use-ui-links.ts +2 -2
  280. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +4 -3
  281. package/src/chat/ui/markdown-blocks.ts +300 -12
  282. package/src/chat/ui/mermaid.ts +118 -0
  283. package/src/chat/ui/rows/ForksRow.tsx +5 -3
  284. package/src/chat/ui/rows/GeneratedImageRow.tsx +6 -4
  285. package/src/chat/ui/rows/MessageRows.tsx +73 -18
  286. package/src/chat/ui/rows/Rows.tsx +14 -1
  287. package/src/chat/ui/rows/SubagentRow.tsx +3 -2
  288. package/src/chat/ui/rows/VisualRow.tsx +3 -2
  289. package/src/chat/ui/rows/WorkRows.tsx +148 -47
  290. package/src/chat/ui/rows/WorkflowRow.tsx +3 -2
  291. package/src/chat/ui/timestamp-format.ts +2 -0
  292. package/src/chat/ui/use-diff-source-tokens.ts +10 -4
  293. package/src/chat/ui/visual-theme.ts +3 -2
  294. package/src/host.ts +31 -15
  295. package/src/index.ts +49 -0
  296. package/src/locales/en/agent-chat.ts +657 -0
  297. package/src/locales/en/agent-prompts.ts +43 -0
  298. package/src/locales/en/agent-providers.ts +131 -0
  299. package/src/locales/en/agent-usage.ts +131 -0
  300. package/src/locales/nl/agent-chat.ts +657 -0
  301. package/src/locales/nl/agent-prompts.ts +43 -0
  302. package/src/locales/nl/agent-providers.ts +131 -0
  303. package/src/locales/nl/agent-usage.ts +132 -0
  304. package/src/locales.ts +9 -8
  305. package/src/prompts/index.ts +35 -0
  306. package/src/prompts/logic/subjects.ts +12 -4
  307. package/src/prompts/ui/ApprovalBody.tsx +6 -4
  308. package/src/prompts/ui/PromptCard.tsx +11 -4
  309. package/src/prompts/ui/PromptView.tsx +3 -2
  310. package/src/prompts/ui/QuestionBody.tsx +3 -2
  311. package/src/providers/AccountColors.tsx +2 -2
  312. package/src/providers/AccountDetail.tsx +13 -7
  313. package/src/providers/AccountVariables.tsx +3 -1
  314. package/src/providers/AddAccountForm.tsx +3 -2
  315. package/src/providers/ProvidersPane.tsx +4 -3
  316. package/src/providers/account-actions.ts +7 -7
  317. package/src/providers/index.ts +17 -0
  318. package/src/providers/provider-abilities.ts +6 -1
  319. package/src/state/chats.ts +4 -2
  320. package/src/state/index.ts +39 -0
  321. package/src/testing/index.ts +2 -0
  322. package/src/{prompts/logic/prompts.fixtures.ts → testing/prompts.ts} +1 -1
  323. package/src/theme.css +13 -3
  324. package/src/usage/UsageBreakdown.tsx +3 -2
  325. package/src/usage/UsageLimits.tsx +4 -3
  326. package/src/usage/UsagePage.tsx +3 -2
  327. package/src/usage/index.ts +16 -0
  328. package/dist/locales/en/agent-chat.json +0 -597
  329. package/dist/locales/en/agent-prompts.json +0 -43
  330. package/dist/locales/en/agent-providers.json +0 -131
  331. package/dist/locales/en/agent-usage.json +0 -131
  332. package/dist/locales/nl/agent-chat.json +0 -597
  333. package/dist/locales/nl/agent-prompts.json +0 -43
  334. package/dist/locales/nl/agent-providers.json +0 -131
  335. package/dist/locales/nl/agent-usage.json +0 -131
  336. package/src/locales/en/agent-chat.json +0 -597
  337. package/src/locales/en/agent-prompts.json +0 -43
  338. package/src/locales/en/agent-providers.json +0 -131
  339. package/src/locales/en/agent-usage.json +0 -131
  340. package/src/locales/nl/agent-chat.json +0 -597
  341. package/src/locales/nl/agent-prompts.json +0 -43
  342. package/src/locales/nl/agent-providers.json +0 -131
  343. package/src/locales/nl/agent-usage.json +0 -131
  344. /package/dist/{prompts/logic/prompts.fixtures.js → testing/prompts.js} +0 -0
@@ -1,22 +1,38 @@
1
- import { Suspense, useEffect, useState, type Dispatch, type ReactNode, type SetStateAction } from 'react';
1
+ import { Fragment, Suspense, useEffect, useState, type Dispatch, type ReactNode, type SetStateAction } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import i18next from 'i18next';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { ChevronRight, FileDiff, X } from 'lucide-react';
6
6
  import type { ChatCheckpointDiff, ChatCheckpointFile, ChatFileChange, ChatToolItem, ChatTurnItem } from '@adecore/agent-contracts';
7
7
  import type { BaseMoved } from '../../logic/timeline';
8
- import { fileChanges, liveOutput, readImagePath, toolStartedAt, toolSummary, unifiedChanges, type FileChange } from '../../logic/tools';
8
+ import {
9
+ exitCodeOf,
10
+ fileChanges,
11
+ hasOutput,
12
+ hasToolBody,
13
+ liveOutput,
14
+ readImagePath,
15
+ shellCommand,
16
+ toolCallLines,
17
+ toolStartedAt,
18
+ toolSummary,
19
+ toolSummaryIsPath,
20
+ unifiedChanges,
21
+ type FileChange
22
+ } from '../../logic/tools';
9
23
  import { useOpenForFind } from '../find-reveal';
10
- import { chatHost } from '../../../host';
24
+ import { useChatHost } from '../../../host';
11
25
  import { lazyNamed } from '../../../lazy';
12
26
  import { useChatActions } from '../../actions';
13
27
  import { useChatScope } from '../../../scope';
14
28
  import { useCurrentItem } from '../../../state/chats';
15
29
  import { useTimelineFlash } from '../../timeline-flash';
16
30
  import { formatClockDuration, formatElapsedShort } from '@adecore/ui/format';
17
- import { Icon, Spinner, useTickingText } from '@adecore/ui';
31
+ import { Icon, MiddleTruncate, Spinner, useTickingText } from '@adecore/ui';
18
32
  import { ROW_GUTTER, toolIcon } from '../icons';
19
33
  import { AnsiOutput } from '../AnsiOutput';
34
+ import { RowTime } from '../RowTime';
35
+ import { ShellCommand } from '../ShellCommand';
20
36
  import { DiffStack, ReviewStackView } from '../diff/DiffStack';
21
37
  import { reviewEntries, type ReviewEntry } from '../diff/review-plan';
22
38
  import { useReviewStack } from '../diff/use-review-stack';
@@ -26,54 +42,79 @@ const UnifiedDiff = lazyNamed(() => import('../UnifiedDiff'), 'default');
26
42
 
27
43
  const OUTPUT_LIMIT = 4000;
28
44
 
45
+ /* The line that says how much of a text past the limit is left out; empty for a text within it. */
46
+ function clipNotice(text: string): string {
47
+ return text.length > OUTPUT_LIMIT ? `\n${i18next.t('agent-chat:work.moreCharacters', { count: text.length - OUTPUT_LIMIT })}` : '';
48
+ }
49
+
29
50
  function clip(text: string): string {
30
- return text.length > OUTPUT_LIMIT
31
- ? `${text.slice(0, OUTPUT_LIMIT)}\n${i18next.t('agent-chat:work.moreCharacters', { count: text.length - OUTPUT_LIMIT })}`
32
- : text;
51
+ return text.slice(0, OUTPUT_LIMIT) + clipNotice(text);
33
52
  }
34
53
 
54
+ /*
55
+ * The line of a call, a subagent or a group. Without `onToggle` it has nothing to open: no chevron, and
56
+ * not a button. `time` shows at the right edge while the line is hovered or focused.
57
+ */
35
58
  export function ToggleLine({
36
59
  icon,
37
60
  label,
38
61
  detail,
39
- open,
62
+ command,
63
+ path,
64
+ open = false,
40
65
  onToggle,
41
66
  failed,
42
67
  live,
43
68
  trailing,
69
+ time,
44
70
  className
45
71
  }: {
46
72
  icon: ReactNode;
47
73
  label: string;
48
74
  detail?: string;
49
- open: boolean;
50
- onToggle(): void;
75
+ /* Draws `detail` as a shell command, in the colors of the code theme. */
76
+ command?: boolean;
77
+ /* `detail` is the path of a file, cut in the middle so its name stays in sight. */
78
+ path?: boolean;
79
+ open?: boolean;
80
+ onToggle?(): void;
51
81
  failed?: boolean;
52
82
  live?: boolean;
53
83
  trailing?: ReactNode;
84
+ time?: number;
54
85
  className?: string;
55
86
  }) {
56
- return (
57
- <button
58
- className={clsx(
59
- // Fix the width so long commands reach the truncation inside the negative margins.
60
- '-mx-1 mb-0.5 flex h-7 w-[calc(100%+8px)] items-center gap-2 rounded-md px-1 text-left text-xs text-text-muted hover:bg-surface-hover',
61
- failed && 'text-status-error',
62
- className
63
- )}
64
- onClick={onToggle}
65
- >
87
+ const classes = clsx(
88
+ // Fix the width so long commands reach the truncation inside the negative margins.
89
+ 'group/line -mx-1 mb-0.5 flex h-7 w-[calc(100%+8px)] items-center gap-2 rounded-md px-1 text-left text-xs text-text-muted',
90
+ onToggle !== undefined && 'hover:bg-surface-hover',
91
+ failed && 'text-status-error',
92
+ className
93
+ );
94
+ const content = (
95
+ <>
66
96
  <span className={clsx(ROW_GUTTER, live && 'text-accent')}>{icon}</span>
67
97
  <span className={clsx('shrink-0', live && 'chat-live-text')}>{label}</span>
68
98
  {detail && (
69
99
  <span data-find-field="summary" className="min-w-0 truncate font-mono text-text-faint">
70
- {detail}
100
+ {command ? <ShellCommand command={detail} /> : path ? <MiddleTruncate text={detail} /> : detail}
71
101
  </span>
72
102
  )}
73
103
  <span className="grow" />
74
104
  {trailing}
105
+ {time !== undefined && <RowTime at={time} />}
75
106
  {failed && <Icon icon={X} size={12} className="shrink-0" />}
76
- <Icon icon={ChevronRight} size={12} className={clsx('shrink-0 text-text-faint transition-transform', open && 'rotate-90')} />
107
+ {onToggle !== undefined && (
108
+ <Icon icon={ChevronRight} size={12} className={clsx('shrink-0 text-text-faint transition-transform', open && 'rotate-90')} />
109
+ )}
110
+ </>
111
+ );
112
+ if (onToggle === undefined) {
113
+ return <div className={classes}>{content}</div>;
114
+ }
115
+ return (
116
+ <button className={classes} onClick={onToggle}>
117
+ {content}
77
118
  </button>
78
119
  );
79
120
  }
@@ -106,7 +147,7 @@ function ChangedFileLine({
106
147
  onClick={onToggle}
107
148
  >
108
149
  <Icon icon={ChevronRight} size={12} className={clsx('shrink-0 text-text-faint transition-transform', open && 'rotate-90')} />
109
- <span className="min-w-0 truncate font-mono text-text">{path}</span>
150
+ <MiddleTruncate text={path} className="font-mono text-text" />
110
151
  <span className="grow" />
111
152
  {trailing}
112
153
  </button>
@@ -115,19 +156,12 @@ function ChangedFileLine({
115
156
  );
116
157
  }
117
158
 
118
- function ToolBody({ tool }: { tool: ChatToolItem }) {
119
- const patches = unifiedChanges(tool);
120
- const changes = patches.length > 0 ? [] : fileChanges(tool.name, tool.input);
159
+ /* A diff keeps its card, with what the call answered under it. */
160
+ function DiffBody({ tool, entries, patched }: { tool: ChatToolItem; entries: readonly ReviewEntry[]; patched: boolean }) {
121
161
  return (
122
162
  <div className="mt-1.5 mb-2 ml-6 overflow-hidden rounded-md border border-border bg-surface-raised">
123
- {patches.length + changes.length > 0 ? (
124
- <DiffStack entries={reviewEntries(patches, changes)} />
125
- ) : (
126
- <pre className="max-h-64 overflow-auto px-3 py-2 font-mono text-code text-text-muted select-text">
127
- {clip(JSON.stringify(tool.input, null, 2) ?? '')}
128
- </pre>
129
- )}
130
- {tool.output !== null && tool.output !== '' && patches.length === 0 && (
163
+ <DiffStack entries={entries} />
164
+ {hasOutput(tool.output) && !patched && (
131
165
  <pre
132
166
  data-find-field="output"
133
167
  className={clsx(
@@ -142,9 +176,64 @@ function ToolBody({ tool }: { tool: ChatToolItem }) {
142
176
  );
143
177
  }
144
178
 
179
+ /* What a call answered, muted under the call; a failure in the error color, or its exit code when the tool names one. */
180
+ function ToolOutput({ tool }: { tool: ChatToolItem }) {
181
+ const { t } = useTranslation('agent-chat');
182
+ if (!hasOutput(tool.output)) {
183
+ return null;
184
+ }
185
+ const failed = tool.state === 'error';
186
+ const { code, rest } = failed ? exitCodeOf(tool.output) : { code: null, rest: tool.output };
187
+ return (
188
+ <>
189
+ {hasOutput(rest) && (
190
+ <pre
191
+ data-find-field="output"
192
+ className={clsx('max-h-64 overflow-auto whitespace-pre-wrap', failed && code === null ? 'text-status-error' : 'text-text-muted')}
193
+ >
194
+ <AnsiOutput text={rest} limit={OUTPUT_LIMIT} />
195
+ </pre>
196
+ )}
197
+ {code !== null && <div className="text-status-error">{t('work.exitCode', { code: String(code) })}</div>}
198
+ </>
199
+ );
200
+ }
201
+
202
+ /* An opened call without a card: what it was asked in the foreground, what it answered muted below. */
203
+ function ToolBody({ tool }: { tool: ChatToolItem }) {
204
+ const patches = unifiedChanges(tool);
205
+ const changes = patches.length > 0 ? [] : fileChanges(tool.name, tool.input);
206
+ if (patches.length + changes.length > 0) {
207
+ return <DiffBody tool={tool} entries={reviewEntries(patches, changes)} patched={patches.length > 0} />;
208
+ }
209
+ const lines = toolCallLines(tool.name, tool.input);
210
+ return (
211
+ <div className="mt-0.5 mb-2 ml-6 flex flex-col gap-1.5 font-mono text-code select-text">
212
+ {lines.command !== null && (
213
+ <pre className="max-h-64 overflow-auto whitespace-pre-wrap wrap-anywhere text-text">
214
+ <ShellCommand command={lines.command.slice(0, OUTPUT_LIMIT)} />
215
+ {clipNotice(lines.command)}
216
+ </pre>
217
+ )}
218
+ {lines.args.length > 0 && (
219
+ <dl className="grid max-h-64 grid-cols-[auto_minmax(0,1fr)] gap-x-3 overflow-auto">
220
+ {lines.args.map(([key, value]) => (
221
+ <Fragment key={key}>
222
+ <dt className="text-text-faint">{key}</dt>
223
+ <dd className="whitespace-pre-wrap wrap-anywhere text-text">{clip(value)}</dd>
224
+ </Fragment>
225
+ ))}
226
+ </dl>
227
+ )}
228
+ {lines.text !== null && <pre className="max-h-64 overflow-auto whitespace-pre-wrap wrap-anywhere text-text">{clip(lines.text)}</pre>}
229
+ <ToolOutput tool={tool} />
230
+ </div>
231
+ );
232
+ }
233
+
145
234
  /*
146
- * One settled tool call: a line, and its input and output behind it. An image the call looked at is
147
- * drawn under the line. `icon`, `label` and `failed` let a row that knows the call better name it.
235
+ * One settled tool call: a line, and what it was asked and answered behind it. An image the call
236
+ * looked at is drawn under the line. `icon`, `label` and `failed` let a row that knows the call better name it.
148
237
  */
149
238
  export function WorkRow({
150
239
  tool,
@@ -161,22 +250,28 @@ export function WorkRow({
161
250
  label?: string;
162
251
  failed?: boolean;
163
252
  }) {
253
+ const host = useChatHost();
164
254
  const [open, setOpen] = useState(false);
165
255
  const image = tool.state === 'done' ? readImagePath(tool.name, tool.input) : null;
166
- const { ReadImage } = chatHost();
256
+ const { ReadImage } = host;
167
257
  useOpenForFind(tool.id, 'output', setOpen);
258
+ const summary = detail ?? toolSummary(tool.name, tool.input);
259
+ const opens = hasToolBody(tool);
168
260
  return (
169
261
  <div data-find-item={tool.id} className={nested ? 'ml-6' : undefined}>
170
262
  <ToggleLine
171
263
  icon={icon ?? toolIcon(tool.name)}
172
264
  label={label ?? tool.name}
173
- detail={detail ?? toolSummary(tool.name, tool.input)}
174
- open={open}
175
- onToggle={() => setOpen((o) => !o)}
265
+ detail={summary}
266
+ command={detail === undefined && summary === shellCommand(tool.name, tool.input)}
267
+ path={detail === undefined && toolSummaryIsPath(tool.name, tool.input)}
268
+ open={open && opens}
269
+ onToggle={opens ? () => setOpen((o) => !o) : undefined}
176
270
  failed={failed ?? tool.state === 'error'}
271
+ time={tool.createdAt}
177
272
  />
178
273
  {image !== null && ReadImage !== null && <ReadImage path={image} />}
179
- {open && <ToolBody tool={tool} />}
274
+ {open && opens && <ToolBody tool={tool} />}
180
275
  </div>
181
276
  );
182
277
  }
@@ -193,18 +288,22 @@ export function WorkLiveRow({ chatId, tool: derived }: { chatId: string; tool: C
193
288
  const [open, setOpen] = useState(false);
194
289
  const tool = useCurrentItem(chatId, derived);
195
290
  const tail = liveOutput(tool);
291
+ const summary = toolSummary(tool.name, tool.input);
292
+ const opens = hasToolBody(tool);
196
293
  return (
197
294
  <div>
198
295
  <ToggleLine
199
296
  icon={toolIcon(tool.name)}
200
297
  label={tool.name}
201
- detail={toolSummary(tool.name, tool.input) || tool.progress?.description || ''}
202
- open={open}
203
- onToggle={() => setOpen((o) => !o)}
298
+ detail={summary || tool.progress?.description || ''}
299
+ command={summary !== '' && summary === shellCommand(tool.name, tool.input)}
300
+ path={summary !== '' && toolSummaryIsPath(tool.name, tool.input)}
301
+ open={open && opens}
302
+ onToggle={opens ? () => setOpen((o) => !o) : undefined}
204
303
  trailing={<RunningFor startedAt={toolStartedAt(tool)} />}
205
304
  live
206
305
  />
207
- {open && <ToolBody tool={tool} />}
306
+ {open && opens && <ToolBody tool={tool} />}
208
307
  {tail !== null && !open && (
209
308
  <pre className="mt-1.5 mb-2 ml-6 max-h-48 overflow-auto rounded-md border border-border bg-surface-raised px-3 py-2 font-mono text-code whitespace-pre-wrap text-chat-output select-text">
210
309
  <AnsiOutput text={tail} tailLines={12} />
@@ -219,7 +318,7 @@ export function WorkGroupRow({ tools, summary, expanded, onToggle }: { tools: Ch
219
318
  const failed = tools.some((tool) => tool.state === 'error');
220
319
  return (
221
320
  <div>
222
- <ToggleLine icon={toolIcon(tools[0]!.name)} label={summary} open={expanded} onToggle={onToggle} failed={failed} />
321
+ <ToggleLine icon={toolIcon(tools[0]!.name)} label={summary} open={expanded} onToggle={onToggle} failed={failed} time={tools[0]!.createdAt} />
223
322
  </div>
224
323
  );
225
324
  }
@@ -244,7 +343,7 @@ export function TurnFoldRow({
244
343
  return (
245
344
  <button
246
345
  className={clsx(
247
- 'group -mx-1 mb-2 flex h-7 w-[calc(100%+8px)] min-w-0 items-center gap-2 rounded-md px-1 text-left text-xs text-text-muted hover:bg-surface-hover',
346
+ 'group group/line -mx-1 mb-2 flex h-7 w-[calc(100%+8px)] min-w-0 items-center gap-2 rounded-md px-1 text-left text-xs text-text-muted hover:bg-surface-hover',
248
347
  turn.state === 'error' && 'text-status-error'
249
348
  )}
250
349
  aria-expanded={expanded}
@@ -255,6 +354,8 @@ export function TurnFoldRow({
255
354
  </span>
256
355
  <span className="shrink-0 group-hover:text-text">{label}</span>
257
356
  {work.length > 0 && <span className="min-w-0 truncate text-text-faint">{work.map((part) => ` · ${part}`).join('')}</span>}
357
+ <span className="grow" />
358
+ <RowTime at={turn.createdAt} />
258
359
  </button>
259
360
  );
260
361
  }
@@ -9,7 +9,7 @@ import { RunningFor, ToggleLine, WorkLiveRow, WorkRow } from './WorkRows';
9
9
  import { useChatScope } from '../../../scope';
10
10
  import { Icon, IconButton } from '@adecore/ui';
11
11
  import { useChatActions } from '../../actions';
12
- import { chatHost } from '../../../host';
12
+ import { useChatHost } from '../../../host';
13
13
 
14
14
  function AgentLine({ view, onOpen }: { view: WorkflowAgentView; onOpen?(step: SubagentStep): void }) {
15
15
  const { agent, state } = view;
@@ -75,6 +75,7 @@ export function WorkflowRow({
75
75
  chatId?: string;
76
76
  onOpenAgent?(step: SubagentStep): void;
77
77
  }) {
78
+ const host = useChatHost();
78
79
  const { t } = useTranslation('agent-chat');
79
80
  const { id } = useChatScope();
80
81
  // A host that does not answer `chat.subagent` has no conversation to open for anyone.
@@ -86,7 +87,7 @@ export function WorkflowRow({
86
87
  const stop = (): void => {
87
88
  if (chatId && workflow.taskId) {
88
89
  void actions.stopTask(chatId, workflow.taskId).catch((error: unknown) => {
89
- chatHost().notify({
90
+ host.notify({
90
91
  kind: 'error',
91
92
  title: t('activity.stopFailed'),
92
93
  description: error instanceof Error ? error.message : t('subagents.noAnswer')
@@ -0,0 +1,2 @@
1
+ /* The full date and time a row's time names on its tooltip. */
2
+ export const TIMESTAMP_FORMAT: Intl.DateTimeFormatOptions = { dateStyle: 'full', timeStyle: 'medium' };
@@ -1,15 +1,16 @@
1
1
  import { useLayoutEffect, useMemo } from 'react';
2
2
  import { diffSyntaxDocument, type DiffSourceTokens } from '@adecore/editor-react';
3
3
  import type { DiffDocument } from '@adecore/merge';
4
- import { diffSourceTokens, type DiffSourceTokenSession, type DiffSyntaxRuntime } from './diff-syntax';
4
+ import { useChatHost } from '../../host';
5
+ import { diffSourceTokens, diffSyntaxRuntime, type DiffSourceTokenSession, type DiffSyntaxRuntime } from './diff-syntax';
5
6
 
6
- function createSourceBinding(document: DiffDocument, language: string, theme: string, runtime?: DiffSyntaxRuntime) {
7
+ function createSourceBinding(document: DiffDocument, language: string, theme: string, runtime: () => DiffSyntaxRuntime) {
7
8
  const mapping = diffSyntaxDocument(document, language, theme);
8
9
  const listeners = new Set<() => void>();
9
10
  let source: DiffSourceTokenSession | undefined;
10
11
  const current = (): DiffSourceTokenSession => {
11
12
  if (source === undefined) {
12
- source = diffSourceTokens(document, mapping, runtime);
13
+ source = diffSourceTokens(document, mapping, runtime());
13
14
  source.subscribe(() => {
14
15
  for (const listener of [...listeners]) {
15
16
  listener();
@@ -36,8 +37,13 @@ function createSourceBinding(document: DiffDocument, language: string, theme: st
36
37
  };
37
38
  }
38
39
 
40
+ /* Without a `runtime`, the shared one of the host's `code`. */
39
41
  export function useDiffSourceTokens(document: DiffDocument, language: string, theme: string, runtime?: DiffSyntaxRuntime): DiffSourceTokens {
40
- const binding = useMemo(() => createSourceBinding(document, language, theme, runtime), [document, language, theme, runtime]);
42
+ const { code } = useChatHost();
43
+ const binding = useMemo(
44
+ () => createSourceBinding(document, language, theme, () => runtime ?? diffSyntaxRuntime(code)),
45
+ [document, language, theme, runtime, code]
46
+ );
41
47
  useLayoutEffect(() => () => binding.release(), [binding]);
42
48
  return binding.tokens;
43
49
  }
@@ -1,6 +1,6 @@
1
1
  import { useLayoutEffect, useState, type RefObject } from 'react';
2
2
  import { cleanVisualVariables, type VisualAppearance, type VisualTheme, type VisualThemeVariable } from '@adecore/agent-contracts/visual';
3
- import { chatHost } from '../../host';
3
+ import { useChatHost } from '../../host';
4
4
 
5
5
  /* The tokens of the app's theme where a frame stands; each answers '' for a token the theme lacks. */
6
6
  export interface VisualTokens {
@@ -167,7 +167,8 @@ const THEME_ATTRIBUTES = ['class', 'style', 'data-theme'];
167
167
  * accent or a font a person picks reaches every frame as well.
168
168
  */
169
169
  export function useVisualTheme(ref: RefObject<HTMLElement | null>): VisualTheme | null {
170
- const mode = chatHost().code.useMode();
170
+ const host = useChatHost();
171
+ const mode = host.code.useMode();
171
172
  const [theme, setTheme] = useState<VisualTheme | null>(null);
172
173
  useLayoutEffect(() => {
173
174
  const element = ref.current;
package/src/host.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ComponentProps, ComponentType, ReactNode, RefObject } from 'react';
1
+ import { createContext, createElement, useContext, useState, type ComponentProps, type ComponentType, type ReactNode, type RefObject } from 'react';
2
2
  import type { Extension } from '@codemirror/state';
3
3
  import type { EditorView } from '@codemirror/view';
4
4
  import type { Highlighter, ThemeRegistration } from 'shiki';
@@ -7,6 +7,7 @@ import type {
7
7
  AgentKind,
8
8
  ChatCheckpointDiff,
9
9
  ChatConfigurePayload,
10
+ ChatGoalAction,
10
11
  ChatItem,
11
12
  ChatSubagentItem,
12
13
  ChatUiChoicePayload,
@@ -21,7 +22,6 @@ import type { FindReveal } from './chat/ui/find-reveal';
21
22
  import type { PromptAction } from './prompts/logic/prompts';
22
23
  import type { HostPrompt, PromptViewProps } from './prompts/logic/subjects';
23
24
  import { PlainTextarea } from './ui/PlainTextarea';
24
- import { configureChatStorage, type ChatStorageOptions } from './storage';
25
25
 
26
26
  /* A color an account may wear, by the name the host stores it under. */
27
27
  export interface AccentChoice {
@@ -58,6 +58,8 @@ export interface ChatActions {
58
58
  turnDiff(chatId: string, turnId: string): Promise<ChatCheckpointDiff | null>;
59
59
  stopSubagent(chatId: string, toolUseId: string): Promise<void>;
60
60
  stopTask(chatId: string, taskId: string): Promise<void>;
61
+ /* Pauses, resumes or clears the chat's goal; absent where the host offers none of it, which leaves the goal row without its buttons. */
62
+ goal?(chatId: string, action: ChatGoalAction): Promise<void>;
61
63
  approve(chatId: string, requestId: string, decision: 'allow' | 'allow-always' | 'deny', message?: string): Promise<void>;
62
64
  answer(chatId: string, requestId: string, answers: Record<string, string>): Promise<void>;
63
65
  dismiss(chatId: string, itemId: string): Promise<void>;
@@ -194,13 +196,11 @@ export interface ReplyAuthor {
194
196
  }
195
197
 
196
198
  /*
197
- * What the app around the chat decides and the chat cannot. Set once, before the first render, with
198
- * `setChatHost`; whatever an app leaves out keeps the default here, which is the chat without that
199
- * part. The same in every scope, so a function that is about one host of chats is told its scope id.
200
- * The hooks are called as hooks: an app hands the same function for the life of the page.
199
+ * What the app around the chat decides and the chat cannot, handed to the views by a `ChatHostProvider`.
200
+ * Whatever an app leaves out keeps the default here, which is the chat without that part. One host may
201
+ * serve several scopes, so a function that is about one host of chats is told its scope id.
201
202
  */
202
203
  export interface ChatHost {
203
- storage?: ChatStorageOptions;
204
204
  intelligentUi?: {
205
205
  subscribe?(scopeId: string, chatId: string, changed: () => void): () => void;
206
206
  link?(scopeId: string, payload: ChatUiLinkPayload): Promise<ChatUiLinkReading>;
@@ -249,7 +249,7 @@ export interface ChatHost {
249
249
  syntaxWorker?: () => Worker;
250
250
  /* Starts legacy highlighting with its first diff; an enclosing DiffPool takes precedence. */
251
251
  legacyDiffWorker?: () => Worker;
252
- /* Borrow the page highlighter for fallback; the host retains ownership. */
252
+ /* Borrow the page highlighter for code blocks and the diff fallback; the host retains ownership. */
253
253
  highlighter?: () => Promise<Highlighter>;
254
254
  /* Opt in to bounded editor reviews; failures show a status, omitted keeps the legacy renderer. */
255
255
  editor?: () => Promise<EditorEngine>;
@@ -310,6 +310,11 @@ export interface ChatHost {
310
310
  };
311
311
  /* Offers a fork of the chat after one of its turns; null offers none. */
312
312
  fork: ((chatId: string, turnId: string) => void) | null;
313
+ /*
314
+ * Opens a new chat beside this one on its CLI, model and settings, with the focus in its composer,
315
+ * once Mod+Alt+Enter sent a message from it; null leaves that key sending the other way, like Alt+Enter.
316
+ */
317
+ openNewChat: ((scopeId: string, chatId: string) => void) | null;
313
318
  openSettings(section: 'providers' | 'agents'): void;
314
319
  /* Whether the host lets a chat go on by itself once a limit it stopped on lifts. */
315
320
  useResumeAtReset(scopeId: string): boolean;
@@ -364,6 +369,7 @@ const DEFAULT_HOST: ChatHost = {
364
369
  tasks: { useTasks: () => undefined, useTask: () => null },
365
370
  confirm: { stopSubagents: (_scopeId, _chatId, run) => run(), stopTask: (_scopeId, _childId, _title, run) => run() },
366
371
  fork: null,
372
+ openNewChat: null,
367
373
  openSettings: () => undefined,
368
374
  useResumeAtReset: () => true,
369
375
  useComposerPlaceholder: () => null,
@@ -374,15 +380,25 @@ const DEFAULT_HOST: ChatHost = {
374
380
  visuals: null
375
381
  };
376
382
 
377
- let host: ChatHost = DEFAULT_HOST;
383
+ const ChatHostContext = createContext<ChatHost>(DEFAULT_HOST);
378
384
 
379
- export function setChatHost(patch: Partial<ChatHost>): void {
380
- if (patch.storage !== undefined) {
381
- configureChatStorage(patch.storage);
385
+ /*
386
+ * Hands the views under it a host: `host` over the one of a provider above it, member by member, and
387
+ * over the defaults at the top. A part of an app that loads later and adds hooks renders a provider of
388
+ * its own around what it draws, so every view under it calls the same hooks from its first render on.
389
+ * The host is read once, as the provider mounts, since a hook that changed under a mounted view would
390
+ * break the order of its hooks: a provider that gets another `host` throws, and a new `key` remounts it.
391
+ */
392
+ export function ChatHostProvider({ host, children }: { host: Partial<ChatHost>; children?: ReactNode }) {
393
+ const parent = useContext(ChatHostContext);
394
+ const [pinned] = useState(() => ({ host, value: { ...parent, ...host } }));
395
+ if (pinned.host !== host) {
396
+ throw new Error('A ChatHostProvider reads its host once: hand it the same object on every render, or a new key to mount it with another');
382
397
  }
383
- host = { ...host, ...patch };
398
+ return createElement(ChatHostContext.Provider, { value: pinned.value }, children);
384
399
  }
385
400
 
386
- export function chatHost(): ChatHost {
387
- return host;
401
+ /* The host of the nearest `ChatHostProvider`, or the defaults outside one. */
402
+ export function useChatHost(): ChatHost {
403
+ return useContext(ChatHostContext);
388
404
  }
package/src/index.ts ADDED
@@ -0,0 +1,49 @@
1
+ // Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
2
+ export {
3
+ type ChatActions,
4
+ type FileRef,
5
+ type ShellCodeBlockContext,
6
+ type ChatPlace,
7
+ type SubagentTask,
8
+ type TimelineFindOptions,
9
+ type TimelineFind,
10
+ type ComposerDictationProps,
11
+ type ComposerSlotProps,
12
+ type SubagentSlotProps,
13
+ type ThreadCard,
14
+ type VisualHost,
15
+ type ReplyAuthor,
16
+ type ChatHost,
17
+ ChatHostProvider,
18
+ useChatHost,
19
+ type AccentChoice,
20
+ type ChatToast,
21
+ type TextareaProps,
22
+ type ResourceUrl
23
+ } from './host';
24
+ export { type ChatScope, ChatScopeContext, useChatScope } from './scope';
25
+ export {
26
+ type ChatRequestMap,
27
+ type ChatEventMap,
28
+ type ChatTransportStatus,
29
+ type ChatTransport,
30
+ ChatTransportError,
31
+ errorCode,
32
+ isConnectionError
33
+ } from './transport';
34
+ export { portTransport, type PortTransport } from './port-transport';
35
+ export {
36
+ onChatStorageConfiguration,
37
+ configureChatStorage,
38
+ chatStorageKey,
39
+ chatStorageLegacyKeys,
40
+ chatStorageAdapter,
41
+ hydrateChatStorage,
42
+ createChatStore,
43
+ type ChatStorageRecord,
44
+ type ChatStorageAdapter,
45
+ type ChatStorageOptions
46
+ } from './storage';
47
+ export { setLazyPrefetch, onLazyOpenError } from './lazy';
48
+ export { type MountedEntry, MountedRegistry } from './mounted-registry';
49
+ export { type AgentsSettingsSection, PROVIDERS_SECTION, USAGE_SECTION, settingsSection } from './settings/sections';