@adecore/agents-react 0.38.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/dist/agents/AgentModelLabel.d.ts +10 -0
  2. package/dist/agents/AgentModelLabel.js +98 -0
  3. package/dist/agents/model-name.js +2 -1
  4. package/dist/agents/provider-paths.d.ts +2 -1
  5. package/dist/agents/provider-paths.js +7 -3
  6. package/dist/chat/account-choice.js +57 -28
  7. package/dist/chat/bookmarks.js +2 -1
  8. package/dist/chat/drafts.d.ts +0 -1
  9. package/dist/chat/drafts.js +9 -28
  10. package/dist/chat/logic/picker-entries.js +2 -8
  11. package/dist/chat/logic/resume-compaction.js +2 -5
  12. package/dist/chat/logic/timeline-copy.js +2 -8
  13. package/dist/chat/logic/timeline.js +9 -10
  14. package/dist/chat/logic/workflow.js +2 -4
  15. package/dist/chat/subagent-conversation.js +2 -1
  16. package/dist/chat/ui/Composer.js +385 -360
  17. package/dist/chat/ui/GoalDock.js +2 -1
  18. package/dist/chat/ui/LimitState.js +4 -6
  19. package/dist/chat/ui/Pickers.d.ts +2 -1
  20. package/dist/chat/ui/Pickers.js +126 -123
  21. package/dist/chat/ui/PromptEditor.d.ts +19 -0
  22. package/dist/chat/ui/PromptEditor.js +115 -0
  23. package/dist/chat/ui/Timeline.d.ts +2 -1
  24. package/dist/chat/ui/Timeline.js +13 -1
  25. package/dist/chat/ui/composer/ComposerControls.d.ts +3 -0
  26. package/dist/chat/ui/composer/ComposerControls.js +235 -38
  27. package/dist/chat/ui/composer/key-handler.d.ts +1 -1
  28. package/dist/chat/ui/composer/key-handler.js +3 -11
  29. package/dist/chat/ui/composer/prompt-picks.d.ts +4 -0
  30. package/dist/chat/ui/composer/prompt-picks.js +14 -0
  31. package/dist/chat/ui/composer/use-composer-menus.d.ts +4 -49
  32. package/dist/chat/ui/composer/use-composer-menus.js +65 -519
  33. package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
  34. package/dist/chat/ui/composer/use-notice.js +2 -1
  35. package/dist/chat/ui/composer/use-prompt-history.js +1 -1
  36. package/dist/chat/ui/composer/use-prompt-menus.d.ts +73 -0
  37. package/dist/chat/ui/composer/use-prompt-menus.js +499 -0
  38. package/dist/chat/ui/composer/use-take-back.js +2 -2
  39. package/dist/chat/ui/diff/ApprovalReview.js +1 -1
  40. package/dist/chat/ui/highlighter.d.ts +0 -6
  41. package/dist/chat/ui/highlighter.js +0 -12
  42. package/dist/chat/ui/icons.d.ts +0 -1
  43. package/dist/chat/ui/icons.js +0 -3
  44. package/dist/chat/ui/index.d.ts +3 -0
  45. package/dist/chat/ui/index.js +2 -0
  46. package/dist/chat/ui/rows/MessageRows.js +2 -5
  47. package/dist/chat/ui/rows/row-rhythm.js +2 -5
  48. package/dist/chat/ui/theme-watch.js +3 -7
  49. package/dist/host-hooks.d.ts +2 -1
  50. package/dist/host-hooks.js +6 -0
  51. package/dist/host.d.ts +17 -1
  52. package/dist/index.d.ts +1 -1
  53. package/dist/locales/en/agent-chat.d.ts +1 -0
  54. package/dist/locales/en/agent-chat.js +1 -0
  55. package/dist/locales/nl/agent-chat.d.ts +1 -0
  56. package/dist/locales/nl/agent-chat.js +1 -0
  57. package/dist/prompts/logic/usePromptSession.js +2 -1
  58. package/dist/providers/AddAccountForm.js +2 -1
  59. package/dist/providers/CliDetail.js +104 -130
  60. package/dist/providers/account-actions.js +6 -9
  61. package/dist/providers/index.d.ts +1 -0
  62. package/dist/providers/index.js +1 -0
  63. package/dist/state/chats.d.ts +1 -0
  64. package/dist/state/chats.js +6 -11
  65. package/dist/state/provider-accounts.d.ts +1 -1
  66. package/dist/state/provider-accounts.js +2 -8
  67. package/dist/theme.css +3 -0
  68. package/dist/transport.d.ts +2 -0
  69. package/dist/transport.js +14 -0
  70. package/dist/usage/ChartLogo.d.ts +5 -0
  71. package/dist/usage/ChartLogo.js +48 -0
  72. package/dist/usage/LimitsList.js +6 -16
  73. package/dist/usage/UsageBreakdown.js +17 -40
  74. package/dist/usage/UsageLimits.js +183 -211
  75. package/dist/usage/UsagePage.js +3 -17
  76. package/dist/usage/UsageSummary.js +4 -9
  77. package/dist/usage/limits.js +16 -31
  78. package/dist/usage/summary.d.ts +1 -1
  79. package/dist/usage/summary.js +3 -5
  80. package/package.json +26 -26
  81. package/src/agents/AccountDot.tsx +1 -0
  82. package/src/agents/AgentIcon.tsx +2 -0
  83. package/src/agents/AgentModelLabel.tsx +38 -0
  84. package/src/agents/ModelOptionControl.tsx +8 -0
  85. package/src/agents/account-limits.ts +3 -0
  86. package/src/agents/accounts.ts +12 -0
  87. package/src/agents/model-name.ts +13 -1
  88. package/src/agents/model-options.ts +2 -0
  89. package/src/agents/model-requirement.ts +4 -0
  90. package/src/agents/option-words.ts +2 -0
  91. package/src/agents/provider-paths.ts +8 -3
  92. package/src/agents/status-look.ts +1 -0
  93. package/src/chat/account-choice.ts +25 -19
  94. package/src/chat/actions.ts +5 -0
  95. package/src/chat/attachments.ts +8 -0
  96. package/src/chat/bookmarks.ts +13 -1
  97. package/src/chat/chat-client.ts +63 -0
  98. package/src/chat/chat-references.ts +3 -0
  99. package/src/chat/composer-effort.ts +5 -0
  100. package/src/chat/composer-send.ts +4 -0
  101. package/src/chat/drafts.ts +21 -17
  102. package/src/chat/forks.ts +4 -0
  103. package/src/chat/guards.ts +2 -0
  104. package/src/chat/index.ts +7 -0
  105. package/src/chat/logic/ansi.ts +47 -0
  106. package/src/chat/logic/bookmarks.ts +7 -0
  107. package/src/chat/logic/command-looks.ts +29 -0
  108. package/src/chat/logic/context-usage.ts +3 -0
  109. package/src/chat/logic/continue-after-stop.ts +5 -0
  110. package/src/chat/logic/effort.ts +4 -0
  111. package/src/chat/logic/embedded-code.ts +63 -0
  112. package/src/chat/logic/fork.ts +22 -0
  113. package/src/chat/logic/generated-image.ts +7 -0
  114. package/src/chat/logic/goal.ts +7 -0
  115. package/src/chat/logic/handback.ts +2 -0
  116. package/src/chat/logic/limit.ts +8 -0
  117. package/src/chat/logic/picker-entries.ts +16 -4
  118. package/src/chat/logic/resume-compaction.ts +22 -5
  119. package/src/chat/logic/scrubber.ts +38 -5
  120. package/src/chat/logic/thought.ts +5 -0
  121. package/src/chat/logic/thread-cards.ts +6 -0
  122. package/src/chat/logic/timeline-copy.ts +4 -9
  123. package/src/chat/logic/timeline-target.ts +15 -0
  124. package/src/chat/logic/timeline.ts +113 -10
  125. package/src/chat/logic/tools.ts +30 -0
  126. package/src/chat/logic/visual-height.ts +5 -0
  127. package/src/chat/logic/visual-viewport.ts +1 -0
  128. package/src/chat/logic/welcome.ts +3 -0
  129. package/src/chat/logic/workflow.ts +26 -7
  130. package/src/chat/mentions.ts +56 -0
  131. package/src/chat/pending-prompts.ts +16 -0
  132. package/src/chat/persisted-json.ts +15 -0
  133. package/src/chat/preferences.ts +16 -0
  134. package/src/chat/prompt-hold.ts +2 -0
  135. package/src/chat/quote.ts +7 -0
  136. package/src/chat/reading-width.ts +1 -0
  137. package/src/chat/recent-messages.ts +5 -0
  138. package/src/chat/resume-compaction-dismissals.ts +1 -0
  139. package/src/chat/stash.ts +12 -0
  140. package/src/chat/subagent-conversation.ts +24 -4
  141. package/src/chat/subagent-list.ts +14 -0
  142. package/src/chat/subagent-support.ts +1 -0
  143. package/src/chat/subagent-view.ts +9 -0
  144. package/src/chat/thumbnails.ts +14 -0
  145. package/src/chat/timeline-flash.ts +2 -0
  146. package/src/chat/timeline-scroll.ts +23 -0
  147. package/src/chat/ui/AccountPill.tsx +4 -0
  148. package/src/chat/ui/AnsiOutput.tsx +1 -0
  149. package/src/chat/ui/BookmarkMarker.tsx +5 -0
  150. package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
  151. package/src/chat/ui/ChatActivity.tsx +13 -0
  152. package/src/chat/ui/CodeBlock.tsx +8 -0
  153. package/src/chat/ui/Composer.tsx +27 -3
  154. package/src/chat/ui/ComposerInput.tsx +22 -0
  155. package/src/chat/ui/DiffPool.tsx +3 -0
  156. package/src/chat/ui/EditDiff.tsx +5 -0
  157. package/src/chat/ui/GoalDock.tsx +13 -1
  158. package/src/chat/ui/ImageView.tsx +11 -0
  159. package/src/chat/ui/LimitState.tsx +23 -6
  160. package/src/chat/ui/Markdown.tsx +41 -0
  161. package/src/chat/ui/MermaidBlock.tsx +16 -0
  162. package/src/chat/ui/MessageActions.tsx +8 -0
  163. package/src/chat/ui/ModelMarks.tsx +1 -0
  164. package/src/chat/ui/Pickers.tsx +30 -1
  165. package/src/chat/ui/PromptComposer.tsx +2 -0
  166. package/src/chat/ui/PromptEditor.tsx +136 -0
  167. package/src/chat/ui/QuoteButton.tsx +12 -0
  168. package/src/chat/ui/ReferenceChip.tsx +3 -0
  169. package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
  170. package/src/chat/ui/RowTime.tsx +1 -0
  171. package/src/chat/ui/RunSettings.tsx +38 -0
  172. package/src/chat/ui/Scrubber.tsx +14 -0
  173. package/src/chat/ui/SessionLine.tsx +1 -0
  174. package/src/chat/ui/ShellCommand.tsx +32 -0
  175. package/src/chat/ui/SubagentControls.tsx +7 -0
  176. package/src/chat/ui/SubagentInfo.tsx +1 -0
  177. package/src/chat/ui/SubagentStopButton.tsx +6 -0
  178. package/src/chat/ui/SubagentTimeline.tsx +10 -0
  179. package/src/chat/ui/Timeline.tsx +141 -1
  180. package/src/chat/ui/TimelineMenu.tsx +5 -0
  181. package/src/chat/ui/UnifiedDiff.tsx +6 -0
  182. package/src/chat/ui/UploadThumb.tsx +2 -0
  183. package/src/chat/ui/VisualDialogs.tsx +3 -0
  184. package/src/chat/ui/VisualFrame.tsx +15 -0
  185. package/src/chat/ui/Welcome.tsx +1 -0
  186. package/src/chat/ui/chip-clipboard.ts +20 -0
  187. package/src/chat/ui/code-lines.ts +14 -0
  188. package/src/chat/ui/code-theme.ts +1 -0
  189. package/src/chat/ui/composer/ComposerControls.tsx +70 -15
  190. package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
  191. package/src/chat/ui/composer/DraftParts.tsx +2 -0
  192. package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
  193. package/src/chat/ui/composer/chip-paste.ts +16 -0
  194. package/src/chat/ui/composer/chips.ts +6 -0
  195. package/src/chat/ui/composer/editor.ts +9 -0
  196. package/src/chat/ui/composer/insert.ts +2 -0
  197. package/src/chat/ui/composer/key-handler.ts +42 -9
  198. package/src/chat/ui/composer/keys.ts +23 -0
  199. package/src/chat/ui/composer/placeholder.ts +6 -0
  200. package/src/chat/ui/composer/prompt-picks.ts +18 -0
  201. package/src/chat/ui/composer/surface.ts +5 -0
  202. package/src/chat/ui/composer/use-chat-controls.ts +13 -0
  203. package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
  204. package/src/chat/ui/composer/use-composer-menus.ts +20 -275
  205. package/src/chat/ui/composer/use-composer-submit.ts +27 -0
  206. package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
  207. package/src/chat/ui/composer/use-draft-files.ts +24 -0
  208. package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
  209. package/src/chat/ui/composer/use-notice.ts +4 -1
  210. package/src/chat/ui/composer/use-pending-requests.ts +3 -0
  211. package/src/chat/ui/composer/use-prompt-history.ts +20 -11
  212. package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
  213. package/src/chat/ui/composer/use-prompt-menus.ts +361 -0
  214. package/src/chat/ui/composer/use-take-back.ts +20 -2
  215. package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
  216. package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
  217. package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
  218. package/src/chat/ui/diff/DiffStack.tsx +2 -0
  219. package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
  220. package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
  221. package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
  222. package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
  223. package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
  224. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
  225. package/src/chat/ui/diff/RawDiff.tsx +1 -0
  226. package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
  227. package/src/chat/ui/diff/diff-source-budget.ts +8 -0
  228. package/src/chat/ui/diff/editor-edit-model.ts +4 -0
  229. package/src/chat/ui/diff/review-plan.ts +9 -0
  230. package/src/chat/ui/diff/review-settle.ts +1 -0
  231. package/src/chat/ui/diff/syntax-runtime.ts +52 -0
  232. package/src/chat/ui/diff/use-review-stack.ts +9 -0
  233. package/src/chat/ui/diff-syntax.ts +12 -0
  234. package/src/chat/ui/diff-theme.ts +2 -0
  235. package/src/chat/ui/file-links.ts +2 -0
  236. package/src/chat/ui/find-reveal.ts +1 -0
  237. package/src/chat/ui/full-diff.ts +1 -0
  238. package/src/chat/ui/highlighter.ts +29 -15
  239. package/src/chat/ui/icons.tsx +2 -4
  240. package/src/chat/ui/index.ts +7 -0
  241. package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
  242. package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
  243. package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
  244. package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
  245. package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
  246. package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
  247. package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
  248. package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
  249. package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
  250. package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
  251. package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
  252. package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
  253. package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
  254. package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
  255. package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
  256. package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
  257. package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
  258. package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
  259. package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
  260. package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
  261. package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
  262. package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
  263. package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
  264. package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
  265. package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
  266. package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
  267. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
  268. package/src/chat/ui/markdown-blocks.ts +64 -0
  269. package/src/chat/ui/mermaid.ts +4 -0
  270. package/src/chat/ui/quote-selection.ts +10 -0
  271. package/src/chat/ui/rehype-chips.ts +5 -0
  272. package/src/chat/ui/rehype-fade.ts +2 -0
  273. package/src/chat/ui/remark-html-as-text.ts +5 -0
  274. package/src/chat/ui/reveal.ts +18 -0
  275. package/src/chat/ui/rows/ForksRow.tsx +5 -0
  276. package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
  277. package/src/chat/ui/rows/MessageRows.tsx +29 -5
  278. package/src/chat/ui/rows/RowContainer.tsx +1 -0
  279. package/src/chat/ui/rows/Rows.tsx +2 -0
  280. package/src/chat/ui/rows/SubagentRow.tsx +13 -0
  281. package/src/chat/ui/rows/TaskRow.tsx +7 -0
  282. package/src/chat/ui/rows/VisualRow.tsx +4 -0
  283. package/src/chat/ui/rows/WorkRows.tsx +46 -0
  284. package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
  285. package/src/chat/ui/rows/row-rhythm.ts +7 -5
  286. package/src/chat/ui/theme-watch.ts +8 -7
  287. package/src/chat/ui/thread-references.ts +7 -0
  288. package/src/chat/ui/use-diff-source-tokens.ts +8 -0
  289. package/src/chat/ui/useToggleSet.ts +3 -0
  290. package/src/chat/ui/visual-theme.ts +30 -0
  291. package/src/chat/visual-bridge.ts +23 -0
  292. package/src/chat/visual-viewport.ts +12 -0
  293. package/src/host-hooks.ts +15 -5
  294. package/src/host.ts +29 -1
  295. package/src/index.ts +5 -0
  296. package/src/lazy.tsx +9 -0
  297. package/src/locales/en/agent-chat.ts +1 -0
  298. package/src/locales/nl/agent-chat.ts +1 -0
  299. package/src/mounted-registry.ts +4 -0
  300. package/src/port-transport.ts +32 -0
  301. package/src/prompts/index.ts +2 -0
  302. package/src/prompts/logic/focus.ts +5 -0
  303. package/src/prompts/logic/keys.ts +12 -0
  304. package/src/prompts/logic/prompts.ts +8 -0
  305. package/src/prompts/logic/subjects.ts +8 -0
  306. package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
  307. package/src/prompts/logic/usePromptSession.ts +13 -1
  308. package/src/prompts/ui/ApprovalBody.tsx +2 -0
  309. package/src/prompts/ui/PromptActions.tsx +2 -0
  310. package/src/prompts/ui/PromptCard.tsx +6 -0
  311. package/src/prompts/ui/PromptView.tsx +6 -0
  312. package/src/prompts/ui/QuestionBody.tsx +11 -0
  313. package/src/providers/AccountColors.tsx +1 -0
  314. package/src/providers/AccountDetail.tsx +7 -0
  315. package/src/providers/AccountVariables.tsx +2 -0
  316. package/src/providers/AddAccountForm.tsx +5 -1
  317. package/src/providers/CliDetail.tsx +6 -5
  318. package/src/providers/InstallPage.tsx +11 -0
  319. package/src/providers/ProviderList.tsx +3 -0
  320. package/src/providers/ProvidersPane.tsx +18 -0
  321. package/src/providers/account-actions.ts +10 -8
  322. package/src/providers/index.ts +1 -0
  323. package/src/providers/install-steps.ts +3 -0
  324. package/src/providers/pane-target.ts +5 -0
  325. package/src/providers/provider-abilities.ts +1 -0
  326. package/src/providers/provider-groups.ts +6 -0
  327. package/src/providers/variables.ts +12 -0
  328. package/src/scope.ts +2 -0
  329. package/src/state/chats.ts +61 -14
  330. package/src/state/index.ts +2 -0
  331. package/src/state/provider-accounts.ts +7 -9
  332. package/src/state/providers.ts +1 -0
  333. package/src/state/usage.ts +6 -0
  334. package/src/storage.ts +18 -0
  335. package/src/testing/prompts.ts +5 -0
  336. package/src/theme.css +3 -0
  337. package/src/transport.ts +18 -0
  338. package/src/usage/ChartLogo.tsx +12 -0
  339. package/src/usage/LimitsList.tsx +7 -8
  340. package/src/usage/UsageBreakdown.tsx +11 -4
  341. package/src/usage/UsageChart.tsx +5 -0
  342. package/src/usage/UsageLimits.tsx +10 -7
  343. package/src/usage/UsageLimitsCard.tsx +2 -0
  344. package/src/usage/UsagePage.tsx +16 -19
  345. package/src/usage/UsageSummary.tsx +4 -5
  346. package/src/usage/UsageTiles.tsx +2 -0
  347. package/src/usage/format.ts +7 -0
  348. package/src/usage/limit-groups.ts +14 -0
  349. package/src/usage/limits.ts +11 -13
  350. package/src/usage/money.ts +1 -0
  351. package/src/usage/summary.ts +15 -7
@@ -39,6 +39,7 @@ const TIME = 'shrink-0 text-xs text-text-faint tabular-nums';
39
39
 
40
40
  function Elapsed({ startedAt }: { startedAt: number }) {
41
41
  const ref = useTickingText(() => formatElapsedShort(Date.now() - startedAt));
42
+
42
43
  return <span ref={ref} className={TIME} />;
43
44
  }
44
45
 
@@ -49,6 +50,7 @@ function AgentLine({ view, provider, onOpen }: { view: WorkflowAgentView; provid
49
50
  const agentId = agent.agentId;
50
51
  // One still waiting has no transcript yet.
51
52
  const open = onOpen !== undefined && agentId !== null ? () => onOpen({ toolUseId: workflowAgentRef(agentId), description: agent.label }) : undefined;
53
+
52
54
  return (
53
55
  <button
54
56
  className="-mx-1 mb-0.5 flex w-[calc(100%+8px)] items-start gap-2 rounded-md px-1 py-1 text-left enabled:hover:bg-surface-hover"
@@ -87,6 +89,7 @@ function AgentLine({ view, provider, onOpen }: { view: WorkflowAgentView; provid
87
89
  function PhaseBlock({ phase, provider, onOpen }: { phase: WorkflowPhaseView; provider: AgentKind | undefined; onOpen?(step: SubagentStep): void }) {
88
90
  const { t } = useTranslation('agent-chat');
89
91
  const settled = phase.agents.filter((view) => view.state === 'done' || view.state === 'failed').length;
92
+
90
93
  return (
91
94
  <div className="mt-3">
92
95
  {phase.title !== null && (
@@ -106,11 +109,13 @@ function PhaseBlock({ phase, provider, onOpen }: { phase: WorkflowPhaseView; pro
106
109
 
107
110
  function Counts({ counts }: { counts: WorkflowCounts }) {
108
111
  const { t } = useTranslation('agent-chat');
112
+
109
113
  const parts = [
110
114
  { key: 'working', count: counts.running, tone: 'text-status-running' },
111
115
  { key: 'finished', count: counts.done, tone: 'text-status-idle' },
112
116
  { key: 'failedCount', count: counts.failed, tone: 'text-status-error' }
113
117
  ].filter((part) => part.count > 0);
118
+
114
119
  return (
115
120
  <span className="shrink-0 truncate text-xs text-text-faint tabular-nums">
116
121
  {t('rows.workflow.agents', { count: counts.total })}
@@ -150,6 +155,7 @@ export function WorkflowRow({
150
155
  const opens = hasToolBody(tool);
151
156
  const onOpen = refused ? undefined : onOpenAgent;
152
157
  const actions = useChatActions();
158
+
153
159
  const stop = (): void => {
154
160
  if (chatId && workflow.taskId) {
155
161
  void actions.stopTask(chatId, workflow.taskId).catch((error: unknown) => {
@@ -161,7 +167,9 @@ export function WorkflowRow({
161
167
  });
162
168
  }
163
169
  };
170
+
164
171
  const failed = tool.state === 'error';
172
+
165
173
  const header = (
166
174
  <>
167
175
  <span className={clsx(ROW_GUTTER, running && 'text-accent')}>
@@ -173,11 +181,13 @@ export function WorkflowRow({
173
181
  {opens && <Icon icon={ChevronRight} size={12} className={clsx('shrink-0 text-text-faint transition-transform', open && 'rotate-90')} />}
174
182
  </>
175
183
  );
184
+
176
185
  const headerClasses = clsx(
177
186
  '-mx-1 flex h-7 w-[calc(100%+8px)] min-w-0 items-center gap-2 rounded-md px-1 text-left text-xs',
178
187
  failed ? 'text-status-error' : 'text-text-muted',
179
188
  opens && 'hover:bg-surface-hover'
180
189
  );
190
+
181
191
  return (
182
192
  <div data-find-item={tool.id} className="mb-1">
183
193
  <div className="flex items-center gap-1">
@@ -6,16 +6,14 @@ import { isBlock, type TimelineRow } from '../../logic/timeline';
6
6
  export const FOLLOW_THRESHOLD_PX = 40;
7
7
 
8
8
  /*
9
- * The gaps a row wears for where it sits: a question is followed by its answer gap, and a seam where
10
- * the list rhythm of tool lines meets the block rhythm of prose gets the block gap. A visual and an
11
- * app's card have no margin of their own like prose does, so the row under one takes the block gap
12
- * too. A question already carries the gap before it, and the row after one is the answer, so neither
13
- * takes a seam.
9
+ * A seam where tool lines meet prose gets the block gap, and so does the row under a visual or an app's
10
+ * card, which have no margin of their own. A question and the answer after it never take a seam.
14
11
  */
15
12
  export function rowRhythm(row: TimelineRow, previous: TimelineRow | null): string {
16
13
  const question = row.kind === 'user';
17
14
  const bare = previous?.kind === 'visual' || previous?.kind === 'app-card';
18
15
  const seam = !question && previous !== null && previous.kind !== 'user' && (isBlock(row) !== isBlock(previous) || bare);
16
+
19
17
  return clsx(question && 'pb-(--chat-answer-gap)', seam && 'pt-(--chat-block-gap)');
20
18
  }
21
19
 
@@ -28,15 +26,19 @@ export function replyHeader(row: TimelineRow, previous: TimelineRow | null): { a
28
26
  if (row.kind === 'user' || row.kind === 'turn-start') {
29
27
  return null;
30
28
  }
29
+
31
30
  if (previous === null) {
32
31
  return { at: null };
33
32
  }
33
+
34
34
  if (previous.kind === 'user') {
35
35
  return { at: previous.item.createdAt };
36
36
  }
37
+
37
38
  if (previous.kind === 'turn-start') {
38
39
  return { at: previous.turn.createdAt };
39
40
  }
41
+
40
42
  return null;
41
43
  }
42
44
 
@@ -2,17 +2,14 @@
2
2
  const THEME_ATTRIBUTES = ['class', 'style', 'data-theme'];
3
3
 
4
4
  /*
5
- * Calls `changed` after the root of `owner` or its body changes a theme attribute, at most once a
6
- * frame, for whatever resolves the theme's tokens into colors of its own. A stylesheet that changes
7
- * changes no attribute, so an app that rewrites its palette that way touches the root's `style` as
8
- * well, the signal the terminal follows too. Returns what stops it.
9
- *
10
- * Any change of those attributes counts, also one that leaves every token as it was (a dialog that
11
- * locks the scroll writes the root's `style`), so a caller compares what it reads with what it drew.
5
+ * Calls `changed` at most once a frame after the root or body of `owner` changes a theme attribute. An app
6
+ * that rewrites its palette through a stylesheet touches the root's `style` as well, as the terminal expects.
7
+ * Any change counts, also one that leaves every token alone, so a caller compares what it reads with what it drew.
12
8
  */
13
9
  export function watchTheme(owner: Document, changed: () => void): () => void {
14
10
  const view = owner.defaultView ?? window;
15
11
  let frame = 0;
12
+
16
13
  const schedule = (): void => {
17
14
  if (frame === 0) {
18
15
  frame = view.requestAnimationFrame(() => {
@@ -21,14 +18,18 @@ export function watchTheme(owner: Document, changed: () => void): () => void {
21
18
  });
22
19
  }
23
20
  };
21
+
24
22
  const observer = new view.MutationObserver(schedule);
23
+
25
24
  for (const root of [owner.documentElement, owner.body]) {
26
25
  if (root !== null) {
27
26
  observer.observe(root, { attributes: true, attributeFilter: THEME_ATTRIBUTES });
28
27
  }
29
28
  }
29
+
30
30
  return () => {
31
31
  observer.disconnect();
32
+
32
33
  if (frame !== 0) {
33
34
  view.cancelAnimationFrame(frame);
34
35
  frame = 0;
@@ -16,11 +16,15 @@ export function threadReferencedKey(structure: Record<string, ChatItem> | undefi
16
16
  if (structure === undefined) {
17
17
  return '';
18
18
  }
19
+
19
20
  const cached = referencedOf.get(structure);
21
+
20
22
  if (cached !== undefined) {
21
23
  return cached;
22
24
  }
25
+
23
26
  const ids = new Set<string>();
27
+
24
28
  for (const item of Object.values(structure)) {
25
29
  if (item.kind === 'user') {
26
30
  for (const id of [...(item.chats ?? []), ...(item.references ?? [])]) {
@@ -28,8 +32,10 @@ export function threadReferencedKey(structure: Record<string, ChatItem> | undefi
28
32
  }
29
33
  }
30
34
  }
35
+
31
36
  const key = [...ids].join('\n');
32
37
  referencedOf.set(structure, key);
38
+
33
39
  return key;
34
40
  }
35
41
 
@@ -39,6 +45,7 @@ export function useReferenceLookup(chatId: string): ReferenceLookup {
39
45
  const { openReference } = useChatHost();
40
46
  const referables = useHostReferables(scopeId, chatId);
41
47
  const key = useChatRow(chatId, (chat) => threadReferencedKey(chat?.structure));
48
+
42
49
  return {
43
50
  items: referables.items,
44
51
  open: openReference ? (id: string) => openReference(scopeId, id) : null,
@@ -8,22 +8,27 @@ function createSourceBinding(document: DiffDocument, language: string, theme: st
8
8
  const mapping = diffSyntaxDocument(document, language, theme);
9
9
  const listeners = new Set<() => void>();
10
10
  let source: DiffSourceTokenSession | undefined;
11
+
11
12
  const current = (): DiffSourceTokenSession => {
12
13
  if (source === undefined) {
13
14
  source = diffSourceTokens(document, mapping, runtime());
15
+
14
16
  source.subscribe(() => {
15
17
  for (const listener of [...listeners]) {
16
18
  listener();
17
19
  }
18
20
  });
19
21
  }
22
+
20
23
  return source;
21
24
  };
25
+
22
26
  return {
23
27
  tokens: {
24
28
  get: (side, index) => (mapping.locate(side, index) === undefined ? null : current().get(side, index)),
25
29
  subscribe(listener) {
26
30
  listeners.add(listener);
31
+
27
32
  return () => {
28
33
  listeners.delete(listener);
29
34
  };
@@ -40,10 +45,13 @@ function createSourceBinding(document: DiffDocument, language: string, theme: st
40
45
  /* Without a `runtime`, the shared one of the host's `code`. */
41
46
  export function useDiffSourceTokens(document: DiffDocument, language: string, theme: string, runtime?: DiffSyntaxRuntime): DiffSourceTokens {
42
47
  const { code } = useChatHost();
48
+
43
49
  const binding = useMemo(
44
50
  () => createSourceBinding(document, language, theme, () => runtime ?? diffSyntaxRuntime(code)),
45
51
  [document, language, theme, runtime, code]
46
52
  );
53
+
47
54
  useLayoutEffect(() => () => binding.release(), [binding]);
55
+
48
56
  return binding.tokens;
49
57
  }
@@ -18,13 +18,16 @@ export function useToggleSet(onToggle?: () => void): ToggleSet {
18
18
  const toggle = (id: string): void => {
19
19
  setIds((current) => {
20
20
  const next = new Set(current);
21
+
21
22
  if (next.has(id)) {
22
23
  next.delete(id);
23
24
  } else {
24
25
  next.add(id);
25
26
  }
27
+
26
28
  return next;
27
29
  });
30
+
28
31
  onToggle?.();
29
32
  };
30
33
 
@@ -25,6 +25,7 @@ export function visualThemeOf(tokens: VisualTokens, appearance: VisualAppearance
25
25
  const raised = tokens.color('--surface-raised');
26
26
  const accent = tokens.color('--accent');
27
27
  const onAccent = tokens.color('--accent-text');
28
+
28
29
  const variables: Partial<Record<VisualThemeVariable, string>> = {
29
30
  '--background': background,
30
31
  '--foreground': text,
@@ -61,6 +62,7 @@ export function visualThemeOf(tokens: VisualTokens, appearance: VisualAppearance
61
62
  '--font-sans': tokens.value('--font-sans'),
62
63
  '--font-mono': tokens.value('--font-mono')
63
64
  };
65
+
64
66
  return { appearance, variables: cleanVisualVariables(variables) };
65
67
  }
66
68
 
@@ -75,10 +77,13 @@ interface Rgba {
75
77
 
76
78
  function rgbaOf(color: string): Rgba | null {
77
79
  const match = RGB.exec(color.trim());
80
+
78
81
  if (match === null) {
79
82
  return null;
80
83
  }
84
+
81
85
  const alpha = match[4] === undefined ? 1 : Number(match[4]) / (match[5] === '%' ? 100 : 1);
86
+
82
87
  return { red: Number(match[1]), green: Number(match[2]), blue: Number(match[3]), alpha };
83
88
  }
84
89
 
@@ -93,27 +98,36 @@ function isTransparent(color: string): boolean {
93
98
  */
94
99
  export function backgroundBehind(layers: readonly string[]): string | null {
95
100
  const translucent: Rgba[] = [];
101
+
96
102
  for (const layer of layers) {
97
103
  if (layer === '' || isTransparent(layer)) {
98
104
  continue;
99
105
  }
106
+
100
107
  const color = rgbaOf(layer);
108
+
101
109
  if (color !== null && color.alpha < 1) {
102
110
  translucent.push(color);
111
+
103
112
  continue;
104
113
  }
114
+
105
115
  if (color === null || translucent.length === 0) {
106
116
  // A color in a space other than sRGB stays as the engine wrote it, since there is no laying another over it here.
107
117
  return color === null ? layer : `rgb(${color.red}, ${color.green}, ${color.blue})`;
108
118
  }
119
+
109
120
  let { red, green, blue } = color;
121
+
110
122
  for (const over of translucent.reverse()) {
111
123
  red = over.red * over.alpha + red * (1 - over.alpha);
112
124
  green = over.green * over.alpha + green * (1 - over.alpha);
113
125
  blue = over.blue * over.alpha + blue * (1 - over.alpha);
114
126
  }
127
+
115
128
  return `rgb(${Math.round(red)}, ${Math.round(green)}, ${Math.round(blue)})`;
116
129
  }
130
+
117
131
  return null;
118
132
  }
119
133
 
@@ -132,27 +146,37 @@ function readTheme(element: HTMLElement, mode: VisualAppearance): VisualTheme {
132
146
  probe.style.position = 'absolute';
133
147
  probe.style.visibility = 'hidden';
134
148
  element.append(probe);
149
+
135
150
  try {
136
151
  const probed = view.getComputedStyle(probe);
152
+
137
153
  const declared = (token: string): string => style.getPropertyValue(token).trim();
154
+
138
155
  const computed = (property: 'color' | 'borderTopLeftRadius', token: string): string => {
139
156
  if (declared(token) === '') {
140
157
  return '';
141
158
  }
159
+
142
160
  probe.style[property] = `var(${token})`;
161
+
143
162
  return probed[property];
144
163
  };
164
+
145
165
  const tokens: VisualTokens = {
146
166
  color: (token) => computed('color', token),
147
167
  length: (token) => computed('borderTopLeftRadius', token),
148
168
  value: declared
149
169
  };
170
+
150
171
  const layers: string[] = [];
172
+
151
173
  for (let node: Element | null = element; node !== null; node = node.parentElement) {
152
174
  layers.push(view.getComputedStyle(node).backgroundColor);
153
175
  }
176
+
154
177
  const scheme = style.colorScheme.trim();
155
178
  const appearance = scheme === 'light' || scheme === 'dark' ? scheme : mode;
179
+
156
180
  return visualThemeOf(tokens, appearance, backgroundBehind(layers) ?? tokens.color('--surface'));
157
181
  } finally {
158
182
  probe.remove();
@@ -167,24 +191,30 @@ function readTheme(element: HTMLElement, mode: VisualAppearance): VisualTheme {
167
191
  export function useVisualTheme(ref: RefObject<HTMLElement | null>): VisualTheme | null {
168
192
  const mode = useHostCodeMode();
169
193
  const [theme, setTheme] = useState<VisualTheme | null>(null);
194
+
170
195
  useLayoutEffect(() => {
171
196
  const element = ref.current;
197
+
172
198
  if (element === null) {
173
199
  return;
174
200
  }
201
+
175
202
  const read = (): void => {
176
203
  const next = readTheme(element, mode);
177
204
  setTheme((current) => (current !== null && sameTheme(current, next) ? current : next));
178
205
  };
206
+
179
207
  read();
180
208
  const owner = element.ownerDocument;
181
209
  const stopWatching = watchTheme(owner, read);
182
210
  const scheme = owner.defaultView?.matchMedia?.('(prefers-color-scheme: dark)') ?? null;
183
211
  scheme?.addEventListener('change', read);
212
+
184
213
  return () => {
185
214
  stopWatching();
186
215
  scheme?.removeEventListener('change', read);
187
216
  };
188
217
  }, [ref, mode]);
218
+
189
219
  return theme;
190
220
  }
@@ -49,6 +49,7 @@ export interface VisualBridgeOptions {
49
49
 
50
50
  function timeout(ms: number, run: () => void): () => void {
51
51
  const timer = setTimeout(run, ms);
52
+
52
53
  return () => clearTimeout(timer);
53
54
  }
54
55
 
@@ -78,6 +79,7 @@ export class VisualBridge {
78
79
  constructor(options: VisualBridgeOptions) {
79
80
  this.options = options;
80
81
  this.theme = options.theme;
82
+
81
83
  options.page.then(
82
84
  (html) => {
83
85
  this.html = html;
@@ -94,10 +96,13 @@ export class VisualBridge {
94
96
  /* A `message` event of the window the frame is in. */
95
97
  receive(event: { source: unknown; data: unknown }): void {
96
98
  const frame = this.options.frame();
99
+
97
100
  if (this.disposed || frame === null || event.source !== frame) {
98
101
  return;
99
102
  }
103
+
100
104
  const message = parseVisualMessage(event.data);
105
+
101
106
  switch (message?.method) {
102
107
  case VISUAL_BRIDGE_METHODS.sandboxProxyReady:
103
108
  // A frame that loads again starts over from the sandbox host page.
@@ -108,40 +113,50 @@ export class VisualBridge {
108
113
  this.viewportReady = false;
109
114
  this.cancelSilence();
110
115
  this.sendPage();
116
+
111
117
  return;
112
118
  case VISUAL_BRIDGE_METHODS.sizeChanged:
113
119
  if (this.sent === null) {
114
120
  return;
115
121
  }
122
+
116
123
  if (!this.live) {
117
124
  this.live = true;
118
125
  this.options.onAppearance(this.sent.appearance);
119
126
  }
127
+
120
128
  this.options.onHeight(
121
129
  this.options.onViewportReady ? visualContentHeight(message.height) : clampVisualHeight(this.options.visual, message.height)
122
130
  );
131
+
123
132
  return;
124
133
  case VISUAL_BRIDGE_METHODS.viewportReady:
125
134
  if (this.sent === null) {
126
135
  return;
127
136
  }
137
+
128
138
  this.viewportReady = true;
129
139
  this.options.onViewportReady?.();
130
140
  this.setViewport(this.viewport);
141
+
131
142
  return;
132
143
  case VISUAL_BRIDGE_METHODS.scrollRequest:
133
144
  if (this.sent !== null && this.viewportReady && this.viewport !== null) {
134
145
  this.options.onScroll?.(message.request);
135
146
  }
147
+
136
148
  return;
137
149
  case VISUAL_BRIDGE_METHODS.openLink:
138
150
  if (this.sent === null) {
139
151
  return;
140
152
  }
153
+
141
154
  if (this.options.mayOpenLink()) {
142
155
  this.options.openLink(message.url);
143
156
  }
157
+
144
158
  frame.postMessage(visualOpenLinkResult(message.id), '*');
159
+
145
160
  return;
146
161
  default:
147
162
  return;
@@ -150,6 +165,7 @@ export class VisualBridge {
150
165
 
151
166
  setViewport(viewport: VisualViewport | null): void {
152
167
  this.viewport = viewport;
168
+
153
169
  if (!this.disposed && this.sent !== null && this.viewportReady) {
154
170
  this.options.frame()?.postMessage(visualViewportMessage(viewport), '*');
155
171
  }
@@ -159,10 +175,13 @@ export class VisualBridge {
159
175
  setTheme(theme: VisualTheme): void {
160
176
  this.theme = theme;
161
177
  const frame = this.options.frame();
178
+
162
179
  if (this.disposed || this.sent === null || frame === null) {
163
180
  return;
164
181
  }
182
+
165
183
  this.postTheme(frame);
184
+
166
185
  if (this.live) {
167
186
  this.options.onAppearance(theme.appearance);
168
187
  }
@@ -173,7 +192,9 @@ export class VisualBridge {
173
192
  if (this.heard || this.disposed) {
174
193
  return;
175
194
  }
195
+
176
196
  this.cancelSilence();
197
+
177
198
  this.cancelSilence = (this.options.after ?? timeout)(SILENCE_MS, () => {
178
199
  if (!this.heard && !this.disposed) {
179
200
  this.options.onFailure();
@@ -188,9 +209,11 @@ export class VisualBridge {
188
209
 
189
210
  private sendPage(): void {
190
211
  const frame = this.options.frame();
212
+
191
213
  if (this.disposed || !this.waiting || this.html === null || frame === null) {
192
214
  return;
193
215
  }
216
+
194
217
  this.waiting = false;
195
218
  // Any origin, since the frame's own is opaque; `receive` checks every answer's source instead.
196
219
  frame.postMessage(visualResourceReadyMessage(this.html), '*');
@@ -29,9 +29,11 @@ export class VisualViewportController {
29
29
  this.observer.observe(scroller);
30
30
  this.attributes = new MutationObserver(this.onScroll);
31
31
  const row = box.closest('[data-index]');
32
+
32
33
  if (row) {
33
34
  this.attributes.observe(row, { attributes: true, attributeFilter: ['style'] });
34
35
  }
36
+
35
37
  scroller.addEventListener('scroll', this.onScroll, { passive: true });
36
38
  this.refresh();
37
39
  }
@@ -40,11 +42,13 @@ export class VisualViewportController {
40
42
  if (this.disposed) {
41
43
  return;
42
44
  }
45
+
43
46
  const top = this.offsetInScroller(this.scroller.getBoundingClientRect(), this.box.getBoundingClientRect());
44
47
  const available = this.scroller.clientHeight;
45
48
  const geometry = visualViewportGeometry(this.box.offsetHeight, available, top);
46
49
  this.viewport.style.height = `${geometry.height}px`;
47
50
  const key = `${geometry.top}:${geometry.height}`;
51
+
48
52
  if (key !== this.last) {
49
53
  this.last = key;
50
54
  this.bridge.setViewport(geometry);
@@ -54,14 +58,18 @@ export class VisualViewportController {
54
58
  scroll(request: VisualScrollRequest): void {
55
59
  const outer = this.scroller.getBoundingClientRect();
56
60
  const box = this.box.getBoundingClientRect();
61
+
57
62
  if (this.disposed || box.bottom <= outer.top || box.top >= outer.bottom) {
58
63
  return;
59
64
  }
65
+
60
66
  if ('by' in request) {
61
67
  this.pendingDelta += request.by;
62
68
  this.schedule();
69
+
63
70
  return;
64
71
  }
72
+
65
73
  this.pendingDelta = 0;
66
74
  const start = this.scroller.scrollTop + this.offsetInScroller(outer, box);
67
75
  const top = 'to' in request ? start + request.to : request.edge === 'start' ? 0 : this.scroller.scrollHeight;
@@ -82,6 +90,7 @@ export class VisualViewportController {
82
90
  private offsetInScroller(outer: DOMRect, box: DOMRect): number {
83
91
  // A chat on a canvas can be scaled; messages inside its frame use unscaled CSS pixels.
84
92
  const scale = outer.height / this.scroller.offsetHeight || 1;
93
+
85
94
  return (box.top - outer.top) / scale - this.scroller.clientTop;
86
95
  }
87
96
 
@@ -89,12 +98,15 @@ export class VisualViewportController {
89
98
  if (this.frame || this.disposed) {
90
99
  return;
91
100
  }
101
+
92
102
  this.frame = this.view.requestAnimationFrame(() => {
93
103
  this.frame = 0;
104
+
94
105
  if (this.pendingDelta !== 0) {
95
106
  this.scroller.scrollBy({ top: this.pendingDelta, behavior: 'instant' });
96
107
  this.pendingDelta = 0;
97
108
  }
109
+
98
110
  this.refresh();
99
111
  });
100
112
  }
package/src/host-hooks.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  'use no memo';
2
2
  /*
3
- * The hooks a host hands the views, each called through a hook of its own here. To the React Compiler a
4
- * hook read from a context may be another function on the next render, so it leaves a component that
5
- * calls one as written. `ChatHostProvider` pins its host and a command look's hooks stay with its row's
6
- * key, so they never change under a mounted view; only these wrappers go uncompiled for it.
3
+ * The React Compiler leaves a component that calls a hook read from a context as written, so the host's
4
+ * hooks are called through these wrappers only. `ChatHostProvider` pins its host, so they never change.
7
5
  */
8
6
  import type { ProviderPlatform } from '@adecore/agent-contracts';
9
7
  import { referablesOfChats, type Referables } from './chat/chat-references';
10
- import { useChatHost, type ChatHost, type CommandLook } from './host';
8
+ import { useChatHost, type ChatHost, type CommandLook, type ComposerSendAction } from './host';
11
9
 
12
10
  export function useHostCodeMode(): 'light' | 'dark' {
13
11
  return useChatHost().code.useMode();
@@ -54,6 +52,7 @@ const useReferablesFromChats = (_scopeId: string, _chatId: string): Referables =
54
52
  /* A host without views of its own offers its chats. */
55
53
  export function useHostReferables(scopeId: string, chatId: string): Referables {
56
54
  const useReferables = useChatHost().useReferables ?? useReferablesFromChats;
55
+
57
56
  return useReferables(scopeId, chatId);
58
57
  }
59
58
 
@@ -66,6 +65,7 @@ const noPlatform = (): ProviderPlatform | null => null;
66
65
  /* Null where the host does not say, as it need not. */
67
66
  export function useHostMachinePlatform(scopeId: string): ProviderPlatform | null {
68
67
  const useMachinePlatform = useChatHost().useMachinePlatform ?? noPlatform;
68
+
69
69
  return useMachinePlatform(scopeId);
70
70
  }
71
71
 
@@ -101,6 +101,16 @@ export function useHostComposerPlaceholder(...args: Parameters<ChatHost['useComp
101
101
  return useChatHost().useComposerPlaceholder(...args);
102
102
  }
103
103
 
104
+ const NO_SEND_ACTIONS: readonly ComposerSendAction[] = [];
105
+
106
+ const noSendActions = (): readonly ComposerSendAction[] => NO_SEND_ACTIONS;
107
+
108
+ export function useHostComposerSendActions(scopeId: string, chatId: string): readonly ComposerSendAction[] {
109
+ const useSendActions = useChatHost().useComposerSendActions ?? noSendActions;
110
+
111
+ return useSendActions(scopeId, chatId);
112
+ }
113
+
104
114
  export function useHostThreadCards(...args: Parameters<ChatHost['useThreadCards']>): ReturnType<ChatHost['useThreadCards']> {
105
115
  return useChatHost().useThreadCards(...args);
106
116
  }