@vertesia/ui 1.5.0-dev.20260722.120446Z → 1.5.0-dev.20260725.083715Z

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 (307) hide show
  1. package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  2. package/lib/core/components/shadcn/filters/filterBar.js +1 -1
  3. package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
  4. package/lib/core/components/shadcn/modal/dialog.d.ts +1 -1
  5. package/lib/core/components/shadcn/modal/dialog.d.ts.map +1 -1
  6. package/lib/core/components/shadcn/modal/dialog.js +2 -0
  7. package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
  8. package/lib/core/components/shadcn/tabs.d.ts +12 -1
  9. package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
  10. package/lib/core/components/shadcn/tabs.js +12 -4
  11. package/lib/core/components/shadcn/tabs.js.map +1 -1
  12. package/lib/features/agent/chat/AgentRightPanel.d.ts +5 -1
  13. package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
  14. package/lib/features/agent/chat/AgentRightPanel.js +2 -2
  15. package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
  16. package/lib/features/agent/chat/ArtifactsTab.d.ts +5 -1
  17. package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
  18. package/lib/features/agent/chat/ArtifactsTab.js +48 -11
  19. package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
  20. package/lib/features/agent/chat/DocumentEditingActionCard.d.ts +6 -0
  21. package/lib/features/agent/chat/DocumentEditingActionCard.d.ts.map +1 -0
  22. package/lib/features/agent/chat/DocumentEditingActionCard.js +129 -0
  23. package/lib/features/agent/chat/DocumentEditingActionCard.js.map +1 -0
  24. package/lib/features/agent/chat/ModernAgentConversation.d.ts +19 -1
  25. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  26. package/lib/features/agent/chat/ModernAgentConversation.js +125 -27
  27. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  28. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts +45 -0
  29. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts.map +1 -0
  30. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js +40 -0
  31. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js.map +1 -0
  32. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -1
  33. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  34. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +94 -49
  35. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  36. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts +2 -1
  37. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
  38. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +9 -5
  39. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
  40. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts +25 -0
  41. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts.map +1 -0
  42. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js +33 -0
  43. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js.map +1 -0
  44. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +3 -1
  45. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  46. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +22 -24
  47. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  48. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts +14 -0
  49. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts.map +1 -0
  50. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js +21 -0
  51. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js.map +1 -0
  52. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts +30 -0
  53. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts.map +1 -0
  54. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js +53 -0
  55. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js.map +1 -0
  56. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
  57. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +21 -2
  58. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js.map +1 -1
  59. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts +6 -0
  60. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts.map +1 -0
  61. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js +29 -0
  62. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js.map +1 -0
  63. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts +33 -0
  64. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts.map +1 -0
  65. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js +129 -0
  66. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js.map +1 -0
  67. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts +7 -0
  68. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
  69. package/lib/features/agent/chat/ModernAgentOutput/utils.js +11 -1
  70. package/lib/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
  71. package/lib/features/agent/chat/hooks/useAgentStream.d.ts +1 -1
  72. package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
  73. package/lib/features/agent/chat/hooks/useAgentStream.js +24 -9
  74. package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
  75. package/lib/features/agent/chat/index.d.ts +2 -1
  76. package/lib/features/agent/chat/index.d.ts.map +1 -1
  77. package/lib/features/agent/chat/index.js +3 -0
  78. package/lib/features/agent/chat/index.js.map +1 -1
  79. package/lib/features/store/objects/DocumentSearchResults.js +2 -2
  80. package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
  81. package/lib/features/store/objects/components/ContentOverview.d.ts +2 -1
  82. package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  83. package/lib/features/store/objects/components/ContentOverview.js +86 -15
  84. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  85. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts +16 -0
  86. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts.map +1 -0
  87. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js +116 -0
  88. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js.map +1 -0
  89. package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts +7 -0
  90. package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts.map +1 -0
  91. package/lib/features/store/objects/components/DocumentEditingLockStatus.js +13 -0
  92. package/lib/features/store/objects/components/DocumentEditingLockStatus.js.map +1 -0
  93. package/lib/features/store/objects/components/DocumentEditingPanel.d.ts +48 -0
  94. package/lib/features/store/objects/components/DocumentEditingPanel.d.ts.map +1 -0
  95. package/lib/features/store/objects/components/DocumentEditingPanel.js +720 -0
  96. package/lib/features/store/objects/components/DocumentEditingPanel.js.map +1 -0
  97. package/lib/features/store/objects/components/TextEditorPanel.d.ts.map +1 -1
  98. package/lib/features/store/objects/components/TextEditorPanel.js +22 -9
  99. package/lib/features/store/objects/components/TextEditorPanel.js.map +1 -1
  100. package/lib/features/store/objects/components/documentArtifactRefs.d.ts +26 -0
  101. package/lib/features/store/objects/components/documentArtifactRefs.d.ts.map +1 -0
  102. package/lib/features/store/objects/components/documentArtifactRefs.js +52 -0
  103. package/lib/features/store/objects/components/documentArtifactRefs.js.map +1 -0
  104. package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts +4 -0
  105. package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts.map +1 -0
  106. package/lib/features/store/objects/components/documentEditingAgentConfig.js +40 -0
  107. package/lib/features/store/objects/components/documentEditingAgentConfig.js.map +1 -0
  108. package/lib/features/store/objects/components/documentEditingPrompts.d.ts +3 -0
  109. package/lib/features/store/objects/components/documentEditingPrompts.d.ts.map +1 -0
  110. package/lib/features/store/objects/components/documentEditingPrompts.js +13 -0
  111. package/lib/features/store/objects/components/documentEditingPrompts.js.map +1 -0
  112. package/lib/features/store/objects/components/documentEditingRun.d.ts +27 -0
  113. package/lib/features/store/objects/components/documentEditingRun.d.ts.map +1 -0
  114. package/lib/features/store/objects/components/documentEditingRun.js +126 -0
  115. package/lib/features/store/objects/components/documentEditingRun.js.map +1 -0
  116. package/lib/features/store/objects/components/documentEditingTarget.d.ts +20 -0
  117. package/lib/features/store/objects/components/documentEditingTarget.d.ts.map +1 -0
  118. package/lib/features/store/objects/components/documentEditingTarget.js +40 -0
  119. package/lib/features/store/objects/components/documentEditingTarget.js.map +1 -0
  120. package/lib/features/store/objects/components/index.d.ts +1 -0
  121. package/lib/features/store/objects/components/index.d.ts.map +1 -1
  122. package/lib/features/store/objects/components/index.js +1 -0
  123. package/lib/features/store/objects/components/index.js.map +1 -1
  124. package/lib/features/store/objects/components/useContentPanelHooks.d.ts +5 -1
  125. package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
  126. package/lib/features/store/objects/components/useContentPanelHooks.js +53 -29
  127. package/lib/features/store/objects/components/useContentPanelHooks.js.map +1 -1
  128. package/lib/features/store/objects/components/useDownloadFile.d.ts +2 -1
  129. package/lib/features/store/objects/components/useDownloadFile.d.ts.map +1 -1
  130. package/lib/features/store/objects/components/useDownloadFile.js +3 -0
  131. package/lib/features/store/objects/components/useDownloadFile.js.map +1 -1
  132. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +1 -1
  133. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +1 -1
  134. package/lib/i18n/locales/ar.json +118 -1
  135. package/lib/i18n/locales/de.json +118 -1
  136. package/lib/i18n/locales/en.json +118 -0
  137. package/lib/i18n/locales/es.json +118 -1
  138. package/lib/i18n/locales/fr.json +118 -1
  139. package/lib/i18n/locales/it.json +118 -1
  140. package/lib/i18n/locales/ja.json +118 -1
  141. package/lib/i18n/locales/ko.json +118 -1
  142. package/lib/i18n/locales/pt.json +118 -1
  143. package/lib/i18n/locales/ru.json +118 -1
  144. package/lib/i18n/locales/tr.json +118 -1
  145. package/lib/i18n/locales/zh-TW.json +118 -1
  146. package/lib/i18n/locales/zh.json +118 -1
  147. package/lib/rich-text/EditorToolbar.d.ts +37 -0
  148. package/lib/rich-text/EditorToolbar.d.ts.map +1 -0
  149. package/lib/rich-text/EditorToolbar.js +215 -0
  150. package/lib/rich-text/EditorToolbar.js.map +1 -0
  151. package/lib/rich-text/VertesiaRichTextEditor.d.ts +26 -0
  152. package/lib/rich-text/VertesiaRichTextEditor.d.ts.map +1 -0
  153. package/lib/rich-text/VertesiaRichTextEditor.js +67 -0
  154. package/lib/rich-text/VertesiaRichTextEditor.js.map +1 -0
  155. package/lib/rich-text/index.d.ts +2 -0
  156. package/lib/rich-text/index.d.ts.map +1 -0
  157. package/lib/rich-text/index.js +2 -0
  158. package/lib/rich-text/index.js.map +1 -0
  159. package/lib/vertesia-ui-core.js +2 -2
  160. package/lib/vertesia-ui-core.js.map +1 -1
  161. package/lib/vertesia-ui-features.js +35 -30
  162. package/lib/vertesia-ui-features.js.map +1 -1
  163. package/lib/vertesia-ui-i18n.js +14 -14
  164. package/lib/vertesia-ui-i18n.js.map +1 -1
  165. package/lib/vertesia-ui-rich-text.js +5 -0
  166. package/lib/vertesia-ui-rich-text.js.map +1 -0
  167. package/lib/vertesia-ui-widgets.js +25 -7
  168. package/lib/vertesia-ui-widgets.js.map +1 -1
  169. package/lib/widgets/markdown/AgentResourceResolver.d.ts +31 -0
  170. package/lib/widgets/markdown/AgentResourceResolver.d.ts.map +1 -0
  171. package/lib/widgets/markdown/AgentResourceResolver.js +39 -0
  172. package/lib/widgets/markdown/AgentResourceResolver.js.map +1 -0
  173. package/lib/widgets/markdown/ArtifactEditingSurface.d.ts +63 -0
  174. package/lib/widgets/markdown/ArtifactEditingSurface.d.ts.map +1 -0
  175. package/lib/widgets/markdown/ArtifactEditingSurface.js +582 -0
  176. package/lib/widgets/markdown/ArtifactEditingSurface.js.map +1 -0
  177. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts +69 -0
  178. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts.map +1 -0
  179. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js +374 -0
  180. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js.map +1 -0
  181. package/lib/widgets/markdown/MarkdownLink.d.ts +3 -1
  182. package/lib/widgets/markdown/MarkdownLink.d.ts.map +1 -1
  183. package/lib/widgets/markdown/MarkdownLink.js +41 -14
  184. package/lib/widgets/markdown/MarkdownLink.js.map +1 -1
  185. package/lib/widgets/markdown/MarkdownRenderer.d.ts +8 -1
  186. package/lib/widgets/markdown/MarkdownRenderer.d.ts.map +1 -1
  187. package/lib/widgets/markdown/MarkdownRenderer.js +20 -4
  188. package/lib/widgets/markdown/MarkdownRenderer.js.map +1 -1
  189. package/lib/widgets/markdown/index.d.ts +5 -1
  190. package/lib/widgets/markdown/index.d.ts.map +1 -1
  191. package/lib/widgets/markdown/index.js +5 -1
  192. package/lib/widgets/markdown/index.js.map +1 -1
  193. package/lib/widgets/markdown/normalizeCustomSchemeLinks.d.ts.map +1 -1
  194. package/lib/widgets/markdown/normalizeCustomSchemeLinks.js +14 -2
  195. package/lib/widgets/markdown/normalizeCustomSchemeLinks.js.map +1 -1
  196. package/lib/widgets/markdown/textDiff.d.ts +52 -0
  197. package/lib/widgets/markdown/textDiff.d.ts.map +1 -0
  198. package/lib/widgets/markdown/textDiff.js +362 -0
  199. package/lib/widgets/markdown/textDiff.js.map +1 -0
  200. package/lib/widgets/markdown/useArtifactContent.d.ts +5 -3
  201. package/lib/widgets/markdown/useArtifactContent.d.ts.map +1 -1
  202. package/lib/widgets/markdown/useArtifactContent.js +6 -3
  203. package/lib/widgets/markdown/useArtifactContent.js.map +1 -1
  204. package/lib/widgets/markdown/useResolvedUrl.d.ts +1 -5
  205. package/lib/widgets/markdown/useResolvedUrl.d.ts.map +1 -1
  206. package/lib/widgets/markdown/useResolvedUrl.js +19 -31
  207. package/lib/widgets/markdown/useResolvedUrl.js.map +1 -1
  208. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts +9 -0
  209. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts.map +1 -1
  210. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js +20 -17
  211. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js.map +1 -1
  212. package/package.json +13 -9
  213. package/src/__tests__/a11y.test.tsx +4 -1
  214. package/src/__tests__/axe-helper.ts +17 -7
  215. package/src/__tests__/setup.ts +25 -0
  216. package/src/core/components/shadcn/filters/filterBar.tsx +1 -0
  217. package/src/core/components/shadcn/modal/dialog.tsx +3 -1
  218. package/src/core/components/shadcn/tabs.test.tsx +65 -0
  219. package/src/core/components/shadcn/tabs.tsx +25 -3
  220. package/src/features/agent/chat/AgentRightPanel.tsx +16 -1
  221. package/src/features/agent/chat/ArtifactsTab.test.tsx +55 -0
  222. package/src/features/agent/chat/ArtifactsTab.tsx +203 -52
  223. package/src/features/agent/chat/DocumentEditingActionCard.test.tsx +105 -0
  224. package/src/features/agent/chat/DocumentEditingActionCard.tsx +210 -0
  225. package/src/features/agent/chat/ModernAgentConversation.test.tsx +237 -40
  226. package/src/features/agent/chat/ModernAgentConversation.tsx +247 -75
  227. package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.test.tsx +79 -0
  228. package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.tsx +94 -0
  229. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +257 -34
  230. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +217 -105
  231. package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +17 -0
  232. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.test.tsx +44 -0
  233. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx +88 -0
  234. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +30 -46
  235. package/src/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.tsx +40 -0
  236. package/src/features/agent/chat/ModernAgentOutput/ResourceChip.test.tsx +148 -0
  237. package/src/features/agent/chat/ModernAgentOutput/ResourceChip.tsx +141 -0
  238. package/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +33 -1
  239. package/src/features/agent/chat/ModernAgentOutput/resourceLinks.tsx +43 -0
  240. package/src/features/agent/chat/ModernAgentOutput/resourceSummary.test.ts +229 -0
  241. package/src/features/agent/chat/ModernAgentOutput/resourceSummary.ts +152 -0
  242. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +17 -0
  243. package/src/features/agent/chat/ModernAgentOutput/utils.ts +14 -1
  244. package/src/features/agent/chat/hooks/useAgentStream.test.tsx +188 -0
  245. package/src/features/agent/chat/hooks/useAgentStream.ts +28 -8
  246. package/src/features/agent/chat/index.ts +11 -0
  247. package/src/features/store/objects/DocumentSearchResults.tsx +2 -2
  248. package/src/features/store/objects/components/ContentOverview.tsx +216 -65
  249. package/src/features/store/objects/components/DocumentEditingConfigurationSelector.test.ts +34 -0
  250. package/src/features/store/objects/components/DocumentEditingConfigurationSelector.tsx +211 -0
  251. package/src/features/store/objects/components/DocumentEditingLockStatus.test.tsx +32 -0
  252. package/src/features/store/objects/components/DocumentEditingLockStatus.tsx +45 -0
  253. package/src/features/store/objects/components/DocumentEditingPanel.test.tsx +336 -0
  254. package/src/features/store/objects/components/DocumentEditingPanel.tsx +1236 -0
  255. package/src/features/store/objects/components/TextEditorPanel.test.tsx +48 -0
  256. package/src/features/store/objects/components/TextEditorPanel.tsx +41 -20
  257. package/src/features/store/objects/components/documentArtifactRefs.test.ts +65 -0
  258. package/src/features/store/objects/components/documentArtifactRefs.ts +72 -0
  259. package/src/features/store/objects/components/documentEditingAgentConfig.test.ts +28 -0
  260. package/src/features/store/objects/components/documentEditingAgentConfig.ts +41 -0
  261. package/src/features/store/objects/components/documentEditingPrompts.test.ts +26 -0
  262. package/src/features/store/objects/components/documentEditingPrompts.ts +12 -0
  263. package/src/features/store/objects/components/documentEditingRun.test.ts +176 -0
  264. package/src/features/store/objects/components/documentEditingRun.ts +184 -0
  265. package/src/features/store/objects/components/documentEditingTarget.test.ts +96 -0
  266. package/src/features/store/objects/components/documentEditingTarget.ts +63 -0
  267. package/src/features/store/objects/components/index.ts +1 -0
  268. package/src/features/store/objects/components/useContentPanelHooks.ts +52 -27
  269. package/src/features/store/objects/components/useDownloadFile.test.ts +20 -0
  270. package/src/features/store/objects/components/useDownloadFile.ts +5 -1
  271. package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +2 -2
  272. package/src/i18n/locales/ar.json +118 -1
  273. package/src/i18n/locales/de.json +118 -1
  274. package/src/i18n/locales/en.json +118 -0
  275. package/src/i18n/locales/es.json +118 -1
  276. package/src/i18n/locales/fr.json +118 -1
  277. package/src/i18n/locales/it.json +118 -1
  278. package/src/i18n/locales/ja.json +118 -1
  279. package/src/i18n/locales/ko.json +118 -1
  280. package/src/i18n/locales/pt.json +118 -1
  281. package/src/i18n/locales/ru.json +118 -1
  282. package/src/i18n/locales/tr.json +118 -1
  283. package/src/i18n/locales/zh-TW.json +118 -1
  284. package/src/i18n/locales/zh.json +118 -1
  285. package/src/rich-text/EditorToolbar.tsx +708 -0
  286. package/src/rich-text/VertesiaRichTextEditor.test.tsx +211 -0
  287. package/src/rich-text/VertesiaRichTextEditor.tsx +241 -0
  288. package/src/rich-text/index.ts +9 -0
  289. package/src/session/UserSession.test.ts +11 -11
  290. package/src/widgets/markdown/AgentResourceResolver.test.ts +24 -0
  291. package/src/widgets/markdown/AgentResourceResolver.tsx +70 -0
  292. package/src/widgets/markdown/ArtifactEditingSurface.test.tsx +537 -0
  293. package/src/widgets/markdown/ArtifactEditingSurface.tsx +878 -0
  294. package/src/widgets/markdown/CollaborativeMarkdownRenderer.test.tsx +484 -0
  295. package/src/widgets/markdown/CollaborativeMarkdownRenderer.tsx +825 -0
  296. package/src/widgets/markdown/MarkdownLink.test.tsx +122 -0
  297. package/src/widgets/markdown/MarkdownLink.tsx +102 -17
  298. package/src/widgets/markdown/MarkdownRenderer.tsx +30 -3
  299. package/src/widgets/markdown/index.ts +39 -1
  300. package/src/widgets/markdown/normalizeCustomSchemeLinks.ts +15 -2
  301. package/src/widgets/markdown/textDiff.test.ts +156 -0
  302. package/src/widgets/markdown/textDiff.ts +399 -0
  303. package/src/widgets/markdown/useArtifactContent.ts +9 -3
  304. package/src/widgets/markdown/useResolvedUrl.test.ts +29 -40
  305. package/src/widgets/markdown/useResolvedUrl.ts +33 -35
  306. package/src/widgets/xml-viewer/hooks/useXMLViewer.test.ts +26 -0
  307. package/src/widgets/xml-viewer/hooks/useXMLViewer.ts +23 -18
@@ -1,14 +1,16 @@
1
1
  import {
2
2
  type AgentMessage,
3
3
  AgentMessageType,
4
+ type AgentResourceReference,
4
5
  type AskUserMessageDetails,
5
6
  type BatchProgressDetails,
7
+ getResourcesFromMessage,
6
8
  type JSONSchema,
7
9
  type Plan,
8
10
  } from '@vertesia/common';
9
- import { Badge, Button, cn } from '@vertesia/ui/core';
11
+ import { Badge, Button, cn, VTooltip } from '@vertesia/ui/core';
10
12
  import { i18nInstance, NAMESPACE, useUITranslation } from '@vertesia/ui/i18n';
11
- import { NavLink } from '@vertesia/ui/router';
13
+ import { useRouterContext } from '@vertesia/ui/router';
12
14
  import { MarkdownRenderer, type MarkdownRendererProps } from '@vertesia/ui/widgets';
13
15
  import type { Element } from 'hast';
14
16
  import {
@@ -29,6 +31,7 @@ import {
29
31
  import React, { Component, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
30
32
  import { AnimatedThinkingDots, PulsatingCircle } from '../AnimatedThinkingDots';
31
33
  import { AskUserWidget } from '../AskUserWidget';
34
+ import { DocumentEditingActionCard, parseMarkdownEditingAction } from '../DocumentEditingActionCard.js';
32
35
  import { ThinkingMessages } from '../WaitingMessages';
33
36
  import {
34
37
  formatWorkstreamName,
@@ -37,10 +40,19 @@ import {
37
40
  getWorkstreamLaunchDetails,
38
41
  type WorkstreamLaunchDetails,
39
42
  } from '../workstreams.js';
43
+ import { createAgentMarkdownAnchor } from './AgentMarkdownAnchor';
40
44
  import { AttachmentPreviewList, parseUserMessageAttachments } from './AttachmentPreview';
41
45
  import BatchProgressPanel, { type BatchProgressPanelClassNames } from './BatchProgressPanel';
46
+ import {
47
+ AGENT_LINE_CLAMP_CLASS,
48
+ AGENT_PROSE_CLASS as SUMMARY_PROSE_CLASS,
49
+ AGENT_COLLAPSE_LINES as SUMMARY_THOUGHT_COLLAPSE_LINES,
50
+ AGENT_COLLAPSE_THRESHOLD as SUMMARY_THOUGHT_COLLAPSE_THRESHOLD,
51
+ } from './CollapsibleAgentMarkdown.js';
42
52
  import { getMessageDeliveryStatus, MessageDeliveryStatus } from './MessageDeliveryStatus';
43
53
  import MessageItem, { type MessageItemClassNames, type MessageItemProps } from './MessageItem';
54
+ import { ResourceChangeSummary } from './ResourceChangeSummary';
55
+ import { ResourceActivityLinkList } from './ResourceChip';
44
56
  import {
45
57
  getAnsweredRequestInputKeys,
46
58
  getAnsweredToolApprovalRequestInputKeys,
@@ -57,6 +69,12 @@ import {
57
69
  isToolApprovalRequestInputHidden,
58
70
  type RequestInputMessageWithUx,
59
71
  } from './requestInputMessages';
72
+ import {
73
+ interleaveTurnSummaries,
74
+ isResourceSummaryReady,
75
+ type ResourceSummaryRenderItem,
76
+ segmentTurnResources,
77
+ } from './resourceSummary';
60
78
  import StreamingMessage, { type StreamingMessageClassNames } from './StreamingMessage';
61
79
  import {
62
80
  buildSummaryConversationItems,
@@ -283,6 +301,7 @@ function getMessageText(message: AgentMessage): string {
283
301
  interface SummaryMessageProps {
284
302
  message: AgentMessage;
285
303
  onSendMessage?: (message: string, metadata?: Record<string, unknown>) => void;
304
+ onOpenArtifact?: (path: string) => void;
286
305
  onSelectWorkstream?: (workstreamId: string) => void;
287
306
  requestInputAnswered?: boolean;
288
307
  StoreLinkComponent?: React.ComponentType<{ href: string; documentId: string; children: React.ReactNode }>;
@@ -330,17 +349,7 @@ function SummaryWorkstreamLaunchMessage({
330
349
  );
331
350
  }
332
351
 
333
- const SUMMARY_PROSE_CLASS = [
334
- 'agent-markdown vprose prose max-w-none break-words text-sm leading-6 text-foreground/80',
335
- 'prose-p:my-2 prose-p:leading-6 prose-li:my-0.5 prose-pre:my-3 prose-headings:tracking-normal',
336
- 'prose-headings:text-foreground prose-strong:text-foreground prose-code:text-foreground',
337
- 'prose-a:text-foreground prose-a:underline prose-a:decoration-muted prose-a:underline-offset-4',
338
- '[&_p]:text-foreground/80 [&_li]:text-foreground/80 [&_li::marker]:text-muted',
339
- ].join(' ');
340
-
341
352
  const USER_BUBBLE_COLLAPSE_THRESHOLD = 520;
342
- const SUMMARY_THOUGHT_COLLAPSE_LINES = 6;
343
- const SUMMARY_THOUGHT_COLLAPSE_THRESHOLD = 520;
344
353
  const STORE_LINK_MARKDOWN_RE =
345
354
  /\[[^\]]+\]\((?:\/store\/(?:objects|collections)\/|store:|document:|document:\/\/|collection:)[^)]+\)/;
346
355
  const DEFAULT_AGENT_MARKDOWN_COMPONENTS: MarkdownRendererProps['components'] = {
@@ -561,6 +570,7 @@ function SummaryUserBubble({
561
570
  workstreamId,
562
571
  className,
563
572
  artifactRunId,
573
+ onOpenArtifact,
564
574
  markdownComponents,
565
575
  }: {
566
576
  children: React.ReactNode;
@@ -568,6 +578,7 @@ function SummaryUserBubble({
568
578
  workstreamId?: string;
569
579
  className?: string;
570
580
  artifactRunId?: string;
581
+ onOpenArtifact?: (path: string) => void;
571
582
  markdownComponents?: MarkdownRendererProps['components'];
572
583
  }) {
573
584
  const { t } = useUITranslation();
@@ -607,6 +618,7 @@ function SummaryUserBubble({
607
618
  {shouldRenderMarkdown ? (
608
619
  <MarkdownRenderer
609
620
  artifactRunId={artifactRunId}
621
+ onArtifactOpen={onOpenArtifact}
610
622
  components={markdownComponents}
611
623
  className={cn(
612
624
  'agent-markdown vprose prose max-w-none break-words text-sm leading-6 text-foreground/90',
@@ -647,11 +659,13 @@ function SummaryUserBubble({
647
659
  function SummaryMessage({
648
660
  message,
649
661
  onSendMessage,
662
+ onOpenArtifact,
650
663
  onSelectWorkstream,
651
664
  requestInputAnswered = false,
652
665
  StoreLinkComponent,
653
666
  CollectionLinkComponent,
654
667
  }: SummaryMessageProps) {
668
+ const { router } = useRouterContext();
655
669
  const content =
656
670
  message.type === AgentMessageType.REQUEST_INPUT ? getRequestInputDisplayText(message) : getMessageText(message);
657
671
  const workstreamLaunchDetails = getWorkstreamLaunchDetails(message) ?? getWorkstreamActivityDetails(message);
@@ -661,55 +675,24 @@ function SummaryMessage({
661
675
  () => (message.type === AgentMessageType.QUESTION ? parseUserMessageAttachments(content) : null),
662
676
  [content, message.type],
663
677
  );
678
+ const editingAction = useMemo(
679
+ () =>
680
+ message.type === AgentMessageType.QUESTION
681
+ ? parseMarkdownEditingAction(message.details?.editing_action)
682
+ : undefined,
683
+ [message.details, message.type],
684
+ );
664
685
 
665
686
  const markdownComponents = useMemo(
666
687
  () => ({
667
688
  ...DEFAULT_AGENT_MARKDOWN_COMPONENTS,
668
- a: ({
669
- node: _node,
670
- ref: _ref,
671
- ...props
672
- }: {
673
- node?: unknown;
674
- ref?: unknown;
675
- href?: string;
676
- children?: React.ReactNode;
677
- }) => {
678
- const href = props.href || '';
679
- if (href.includes('/store/objects')) {
680
- const documentId = href.split('/store/objects/')[1] || '';
681
- if (StoreLinkComponent) {
682
- return (
683
- <StoreLinkComponent href={href} documentId={documentId}>
684
- {props.children}
685
- </StoreLinkComponent>
686
- );
687
- }
688
- return (
689
- <NavLink href={href} topLevelNav>
690
- {props.children}
691
- </NavLink>
692
- );
693
- }
694
- if (href.includes('/store/collections')) {
695
- const collectionId = href.split('/store/collections/')[1] || '';
696
- if (CollectionLinkComponent) {
697
- return (
698
- <CollectionLinkComponent href={href} collectionId={collectionId}>
699
- {props.children}
700
- </CollectionLinkComponent>
701
- );
702
- }
703
- return (
704
- <NavLink href={href} topLevelNav>
705
- {props.children}
706
- </NavLink>
707
- );
708
- }
709
- return <a {...props} target="_blank" rel="noopener noreferrer" />;
710
- },
689
+ a: createAgentMarkdownAnchor({
690
+ StoreLinkComponent,
691
+ CollectionLinkComponent,
692
+ addStickyParams: (href: string) => router.getTopRouter().navigator.addStickyParams(href),
693
+ }),
711
694
  }),
712
- [StoreLinkComponent, CollectionLinkComponent],
695
+ [StoreLinkComponent, CollectionLinkComponent, router],
713
696
  );
714
697
 
715
698
  if (workstreamLaunchDetails) {
@@ -733,6 +716,7 @@ function SummaryMessage({
733
716
  <AttachmentPreviewList
734
717
  items={attachments}
735
718
  artifactRunId={runId}
719
+ onOpenArtifact={onOpenArtifact}
736
720
  align="end"
737
721
  variant="message"
738
722
  StoreLinkComponent={StoreLinkComponent}
@@ -745,9 +729,13 @@ function SummaryMessage({
745
729
  message={message}
746
730
  workstreamId={workstreamId}
747
731
  artifactRunId={runId}
732
+ onOpenArtifact={onOpenArtifact}
748
733
  markdownComponents={markdownComponents}
734
+ // Comments/edits are user actions: keep the user-message background and end
735
+ // alignment, just give the card a little more room for tables and diffs.
736
+ className={editingAction ? 'max-w-[min(44rem,90%)]' : undefined}
749
737
  >
750
- {questionBody}
738
+ {editingAction ? <DocumentEditingActionCard action={editingAction} /> : questionBody}
751
739
  </SummaryUserBubble>
752
740
  )}
753
741
  </>
@@ -799,6 +787,7 @@ function SummaryMessage({
799
787
  >
800
788
  <MarkdownRenderer
801
789
  artifactRunId={runId}
790
+ onArtifactOpen={onOpenArtifact}
802
791
  onProposalSelect={(optionId) => onSendMessage?.(optionId)}
803
792
  onProposalSubmit={(text) => onSendMessage?.(text)}
804
793
  components={markdownComponents}
@@ -1028,6 +1017,8 @@ interface SummaryToolDetailItem {
1028
1017
  finishedAt?: number | string;
1029
1018
  status?: ToolExecutionStatus;
1030
1019
  decisionText?: string;
1020
+ resources: AgentResourceReference[];
1021
+ workflowRunId?: string;
1031
1022
  sections: SummaryToolDetailSection[];
1032
1023
  }
1033
1024
 
@@ -1043,6 +1034,7 @@ const TOOL_DETAIL_SYSTEM_KEYS = new Set([
1043
1034
  'collection_id',
1044
1035
  'document_id',
1045
1036
  'display_role',
1037
+ 'duration_ms',
1046
1038
  'event_class',
1047
1039
  'id',
1048
1040
  'object_id',
@@ -1060,6 +1052,7 @@ const TOOL_DETAIL_SYSTEM_KEYS = new Set([
1060
1052
  'tools',
1061
1053
  'message_to_human',
1062
1054
  'progress_messages',
1055
+ 'resources',
1063
1056
  'source',
1064
1057
  'token_usage',
1065
1058
  'checkpoint_at',
@@ -1394,7 +1387,12 @@ function getRenderableGroupKey(group: RenderableGroup): string {
1394
1387
  return ['group', group.toolRunId, group.firstTimestamp, firstKey, lastKey].filter(Boolean).join(':');
1395
1388
  }
1396
1389
 
1397
- function getPreviousRenderableGroupTimestamp(groups: RenderableGroup[], index: number): number | string | undefined {
1390
+ // Accepts the augmented render list (which may contain interleaved resource_summary items); those
1391
+ // are skipped so the previous *group* timestamp is found regardless of inserted summaries.
1392
+ function getPreviousRenderableGroupTimestamp(
1393
+ groups: Array<RenderableGroup | ResourceSummaryRenderItem>,
1394
+ index: number,
1395
+ ): number | string | undefined {
1398
1396
  for (let previousIndex = index - 1; previousIndex >= 0; previousIndex--) {
1399
1397
  const group = groups[previousIndex];
1400
1398
  if (group.type === 'single') return group.message.timestamp;
@@ -1512,6 +1510,8 @@ function buildSummaryToolDetailItem(message: AgentMessage, index: number): Summa
1512
1510
  finishedAt: getTimestampDetail(details[SUMMARY_TOOL_FINISHED_AT_DETAIL_KEY]) ?? message.timestamp,
1513
1511
  status: approvalDecisionTitle ? 'error' : (details.tool_status as ToolExecutionStatus | undefined),
1514
1512
  decisionText,
1513
+ resources: getResourcesFromMessage(message),
1514
+ workflowRunId: message.workflow_run_id,
1515
1515
  sections: getToolDetailSections(message),
1516
1516
  };
1517
1517
  }
@@ -1846,6 +1846,13 @@ function formatToolDetailCopyText(item: SummaryToolDetailItem): string {
1846
1846
  const metadata = getToolDetailMetadata(item).map((entry) => `${entry.label}: ${entry.value}`);
1847
1847
  if (metadata.length > 0) parts.push(metadata.join('\n'));
1848
1848
  if (item.text) parts.push(item.text);
1849
+ if (item.resources.length > 0) {
1850
+ parts.push(
1851
+ `Resources\n${item.resources
1852
+ .map((resource) => `${resource.label} (${resource.type}:${resource.id}) — ${resource.action}`)
1853
+ .join('\n')}`,
1854
+ );
1855
+ }
1849
1856
 
1850
1857
  for (const section of item.sections) {
1851
1858
  parts.push(`${section.label}\n${formatToolSectionValue(section.value)}`);
@@ -1953,52 +1960,80 @@ function SummaryToolDetailPanel({ item }: { item: SummaryToolDetailItem }) {
1953
1960
  }
1954
1961
 
1955
1962
  function SummaryToolTimelineItem({ item }: { item: SummaryToolDetailItem }) {
1963
+ const { t } = useUITranslation();
1956
1964
  const isDecision = Boolean(item.decisionText);
1957
1965
  const isAttention = !isDecision && (item.status === 'error' || item.status === 'warning');
1958
1966
  const hasMetadata = getToolDetailMetadata(item).length > 0;
1959
1967
  const hasDetails = Boolean(item.command || item.text || item.sections.length || hasMetadata);
1968
+ const hasResources = item.resources.length > 0;
1960
1969
  const [isExpanded, setIsExpanded] = useState(false);
1961
1970
  const iconStatus = isDecision ? undefined : item.status;
1971
+ const toggleDetailsLabel = isExpanded ? t('agent.hideDetails') : t('agent.showDetails');
1972
+ const detailsSubject = hasResources ? item.resources.map((resource) => resource.label).join(', ') : item.title;
1973
+
1974
+ const icon = (
1975
+ <span
1976
+ className={cn(
1977
+ 'flex size-5 items-center justify-center pt-0.5',
1978
+ isAttention ? 'text-attention' : 'text-muted',
1979
+ )}
1980
+ >
1981
+ <ToolDetailIcon kind={item.kind} status={iconStatus} />
1982
+ </span>
1983
+ );
1984
+
1985
+ const chevron = (
1986
+ <ChevronDown
1987
+ className={cn('size-4 shrink-0 text-muted opacity-50 transition-transform', !isExpanded && '-rotate-90')}
1988
+ aria-hidden="true"
1989
+ />
1990
+ );
1962
1991
 
1963
1992
  return (
1964
1993
  <div className="min-w-0">
1965
- <button
1966
- type="button"
1967
- className={cn(
1968
- 'grid w-full grid-cols-[1.5rem_1fr_auto] gap-2 text-start outline-none transition-colors',
1969
- 'focus-visible:text-foreground focus-visible:underline focus-visible:underline-offset-4',
1970
- hasDetails ? 'cursor-pointer hover:text-foreground' : 'cursor-default',
1971
- )}
1972
- onClick={() => hasDetails && setIsExpanded((current) => !current)}
1973
- aria-expanded={hasDetails ? isExpanded : undefined}
1974
- disabled={!hasDetails}
1975
- >
1976
- <span
1994
+ {hasResources ? (
1995
+ <div className="grid w-full grid-cols-[1.5rem_1fr_auto] items-center gap-2 text-start">
1996
+ {icon}
1997
+ <ResourceActivityLinkList resources={item.resources} workflowRunId={item.workflowRunId} />
1998
+ {hasDetails ? (
1999
+ <VTooltip description={toggleDetailsLabel} asChild>
2000
+ <Button
2001
+ variant="ghost"
2002
+ size="xs"
2003
+ className="size-6 p-0 text-muted"
2004
+ onClick={() => setIsExpanded((current) => !current)}
2005
+ aria-expanded={isExpanded}
2006
+ aria-label={`${toggleDetailsLabel}: ${detailsSubject}`}
2007
+ >
2008
+ {chevron}
2009
+ </Button>
2010
+ </VTooltip>
2011
+ ) : null}
2012
+ </div>
2013
+ ) : (
2014
+ <button
2015
+ type="button"
1977
2016
  className={cn(
1978
- 'flex size-5 items-center justify-center pt-0.5',
1979
- isAttention ? 'text-attention' : 'text-muted',
2017
+ 'grid w-full grid-cols-[1.5rem_1fr_auto] gap-2 text-start outline-none transition-colors',
2018
+ 'focus-visible:text-foreground focus-visible:underline focus-visible:underline-offset-4',
2019
+ hasDetails ? 'cursor-pointer hover:text-foreground' : 'cursor-default',
1980
2020
  )}
2021
+ onClick={() => hasDetails && setIsExpanded((current) => !current)}
2022
+ aria-expanded={hasDetails ? isExpanded : undefined}
2023
+ disabled={!hasDetails}
1981
2024
  >
1982
- <ToolDetailIcon kind={item.kind} status={iconStatus} />
1983
- </span>
1984
- <span className="min-w-0 text-sm text-muted">
1985
- <span className="break-words">{item.title}</span>
1986
- {item.decisionText ? (
1987
- <Badge variant="destructive" className="ms-2 rounded-full shadow-sm shadow-destructive/10">
1988
- {item.decisionText}
1989
- </Badge>
1990
- ) : null}
1991
- </span>
1992
- {hasDetails ? (
1993
- <ChevronDown
1994
- className={cn(
1995
- 'mt-0.5 size-4 shrink-0 text-muted opacity-50 transition-transform',
1996
- !isExpanded && '-rotate-90',
1997
- )}
1998
- aria-hidden="true"
1999
- />
2000
- ) : null}
2001
- </button>
2025
+ {icon}
2026
+ <span className="min-w-0 text-sm text-muted">
2027
+ <span className="break-words">{item.title}</span>
2028
+ {item.decisionText ? (
2029
+ <Badge variant="destructive" className="ms-2 rounded-full shadow-sm shadow-destructive/10">
2030
+ {item.decisionText}
2031
+ </Badge>
2032
+ ) : null}
2033
+ </span>
2034
+ {hasDetails ? chevron : null}
2035
+ </button>
2036
+ )}
2002
2037
  {hasDetails && isExpanded ? (
2003
2038
  <div className="mt-1">
2004
2039
  <SummaryToolDetailPanel item={item} />
@@ -2031,13 +2066,7 @@ function SummaryThoughtProseItem({
2031
2066
  <div className="min-w-0 py-1">
2032
2067
  <div
2033
2068
  data-testid="summary-thought-prose"
2034
- className={cn(
2035
- SUMMARY_PROSE_CLASS,
2036
- isLong &&
2037
- !disableCollapse &&
2038
- !isExpanded &&
2039
- '[display:-webkit-box] overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:6]',
2040
- )}
2069
+ className={cn(SUMMARY_PROSE_CLASS, isLong && !disableCollapse && !isExpanded && AGENT_LINE_CLAMP_CLASS)}
2041
2070
  style={{ overflowWrap: 'anywhere' }}
2042
2071
  >
2043
2072
  <MarkdownRenderer artifactRunId={artifactRunId} components={DEFAULT_AGENT_MARKDOWN_COMPONENTS}>
@@ -2136,9 +2165,9 @@ function SummaryStoppedMessage({
2136
2165
 
2137
2166
  return (
2138
2167
  <div className={cn('mx-auto w-full max-w-3xl px-1', className)} data-testid="summary-stopped-message">
2139
- <div className="flex items-center justify-end gap-2 text-lg font-medium text-muted sm:text-xl">
2168
+ <div className="flex items-center justify-end gap-2 text-sm font-medium text-muted">
2140
2169
  <span>{t('agent.youStoppedAfter', { duration })}</span>
2141
- <MessageDeliveryStatus message={message} className="h-5 w-5" />
2170
+ <MessageDeliveryStatus message={message} className="h-4 w-4" />
2142
2171
  </div>
2143
2172
  <div className="mt-5 border-b border-border/70" />
2144
2173
  </div>
@@ -2387,6 +2416,8 @@ interface AllMessagesMixedProps {
2387
2416
  streamingMessages?: Map<string, StreamingData>; // Real-time streaming chunks
2388
2417
  /** Callback when user sends a message (e.g., from proposal selection) */
2389
2418
  onSendMessage?: (message: string, metadata?: Record<string, unknown>) => void;
2419
+ /** Open a Markdown artifact in the surrounding agent panel. */
2420
+ onOpenArtifact?: (path: string) => void;
2390
2421
  /** Stable index for thinking messages (changes on 4s interval) */
2391
2422
  thinkingMessageIndex?: number;
2392
2423
  /** className overrides passed to every MessageItem */
@@ -2446,6 +2477,7 @@ function AllMessagesMixedComponent({
2446
2477
  isCompleted = false,
2447
2478
  streamingMessages = new Map(),
2448
2479
  onSendMessage,
2480
+ onOpenArtifact,
2449
2481
  messageItemClassNames,
2450
2482
  messageStyleOverrides,
2451
2483
  toolCallGroupClassNames,
@@ -2767,6 +2799,36 @@ function AllMessagesMixedComponent({
2767
2799
  return isCompleted || !isInProgress(completionDisplayMessages);
2768
2800
  }, [completionDisplayMessages, hasLatestToolApprovalAllow, hasPendingToolApprovalRequest, isCompleted]);
2769
2801
 
2802
+ // Per-turn resource summaries. Derived from the full source stream (not the active-tab view) so
2803
+ // child-workstream resources are included, but CLIPPED to the playback cursor so scrubbing back
2804
+ // never reveals resources created later in the turn. Gated on the MAIN workstream reaching either
2805
+ // a terminal state or its final ANSWER, so a completed child tab can't surface a summary early while
2806
+ // reusable interactive workflows can summarize a completed turn. Built from structured tool metadata
2807
+ // (details.resources), never parsed from prose.
2808
+ //
2809
+ // Clip by the cursor message's POSITION in the ordered source, not by `timestamp <= cursor`:
2810
+ // millisecond timestamps are not unique, so a later message sharing the cursor's timestamp would
2811
+ // otherwise leak. indexOf works because sortAndDedupeMessages preserves message references; a
2812
+ // timestamp compare is the fallback if the cursor can't be located (e.g. collapsed by dedup).
2813
+ const clippedResourceSource = useMemo(() => {
2814
+ if (workstreamSourceSorted.length === 0) return workstreamSourceSorted;
2815
+ const cursorMessage = messages[messages.length - 1];
2816
+ if (!cursorMessage) return [];
2817
+ const cursorIndex = workstreamSourceSorted.indexOf(cursorMessage);
2818
+ if (cursorIndex >= 0) {
2819
+ return cursorIndex >= workstreamSourceSorted.length - 1
2820
+ ? workstreamSourceSorted
2821
+ : workstreamSourceSorted.slice(0, cursorIndex + 1);
2822
+ }
2823
+ const cursorTs = getTimestampMs(cursorMessage.timestamp);
2824
+ return workstreamSourceSorted.filter((m) => getTimestampMs(m.timestamp) <= cursorTs);
2825
+ }, [workstreamSourceSorted, messages]);
2826
+ const isLatestResourceTurnComplete = useMemo(
2827
+ () => isCompleted || isResourceSummaryReady(clippedResourceSource),
2828
+ [isCompleted, clippedResourceSource],
2829
+ );
2830
+ const turnResourceSummaries = useMemo(() => segmentTurnResources(clippedResourceSource), [clippedResourceSource]);
2831
+
2770
2832
  // Split streaming messages:
2771
2833
  // - complete (or stale incomplete) ones are interleaved chronologically
2772
2834
  // - actively incomplete ones render at the end
@@ -2836,6 +2898,19 @@ function AllMessagesMixedComponent({
2836
2898
  [visibleSummaryDisplayMessages, isDisplayCompleted, latestSummaryObservedTimestamp],
2837
2899
  );
2838
2900
 
2901
+ // Interleave per-turn resource summaries into the sliding (summary) timeline, mirroring the
2902
+ // stacked view. Turn boundaries are user QUESTION message items.
2903
+ const slidingRenderItems = React.useMemo(
2904
+ () =>
2905
+ interleaveTurnSummaries(
2906
+ summaryConversationItems,
2907
+ (item) => item.type === 'message' && item.message.type === AgentMessageType.QUESTION,
2908
+ turnResourceSummaries,
2909
+ isLatestResourceTurnComplete,
2910
+ ),
2911
+ [summaryConversationItems, turnResourceSummaries, isLatestResourceTurnComplete],
2912
+ );
2913
+
2839
2914
  // Group messages with ONLY complete streaming interleaved for stacked view.
2840
2915
  // Incomplete streaming is rendered separately at the end (avoids re-grouping on every chunk).
2841
2916
  // Assistant prose stays as its own timeline item so thoughts remain visible between tool calls.
@@ -2847,6 +2922,19 @@ function AllMessagesMixedComponent({
2847
2922
  [displayMessages, completeStreaming, activeWorkstream],
2848
2923
  );
2849
2924
 
2925
+ // Interleave per-turn resource summaries into the stacked timeline. Turn boundaries are user
2926
+ // QUESTION groups; turn N's resources come from the full-stream segmentation.
2927
+ const stackedRenderItems = React.useMemo(
2928
+ () =>
2929
+ interleaveTurnSummaries(
2930
+ groupedMessages,
2931
+ (group) => group.type === 'single' && group.message.type === AgentMessageType.QUESTION,
2932
+ turnResourceSummaries,
2933
+ isLatestResourceTurnComplete,
2934
+ ),
2935
+ [groupedMessages, turnResourceSummaries, isLatestResourceTurnComplete],
2936
+ );
2937
+
2850
2938
  // Show an activity indicator when the latest visible conversation state is not terminal.
2851
2939
  // Older idle/complete messages from previous turns must not suppress the new turn.
2852
2940
  const isAgentWorking = useMemo(() => {
@@ -3299,8 +3387,19 @@ function AllMessagesMixedComponent({
3299
3387
  {viewMode === 'stacked' ? (
3300
3388
  // Details view - show ALL messages with streaming interleaved
3301
3389
  <>
3302
- {groupedMessages.map((group, groupIndex) => {
3303
- const isLastGroup = groupIndex === groupedMessages.length - 1;
3390
+ {stackedRenderItems.map((group, groupIndex) => {
3391
+ const isLastGroup = groupIndex === stackedRenderItems.length - 1;
3392
+
3393
+ if (group.type === 'resource_summary') {
3394
+ return (
3395
+ <ResourceChangeSummary
3396
+ key={group.key}
3397
+ resources={group.resources}
3398
+ workflowRunId={artifactRunId}
3399
+ className="my-2"
3400
+ />
3401
+ );
3402
+ }
3304
3403
 
3305
3404
  if (group.type === 'tool_group') {
3306
3405
  // Render grouped tool calls
@@ -3373,7 +3472,7 @@ function AllMessagesMixedComponent({
3373
3472
  message={message}
3374
3473
  startTimestamp={
3375
3474
  getPreviousRenderableGroupTimestamp(
3376
- groupedMessages,
3475
+ stackedRenderItems,
3377
3476
  groupIndex,
3378
3477
  ) ?? message.timestamp
3379
3478
  }
@@ -3409,6 +3508,7 @@ function AllMessagesMixedComponent({
3409
3508
  message={message}
3410
3509
  showPulsatingCircle={isLatestMessage}
3411
3510
  onSendMessage={onSendMessage}
3511
+ onOpenArtifact={onOpenArtifact}
3412
3512
  requestInputAnswered={isRequestInputAnswered(
3413
3513
  message,
3414
3514
  answeredRequestInputKeys,
@@ -3469,7 +3569,18 @@ function AllMessagesMixedComponent({
3469
3569
  ) : (
3470
3570
  // Summary view - conversation turns with per-turn work disclosure
3471
3571
  <>
3472
- {summaryConversationItems.map((item) => {
3572
+ {slidingRenderItems.map((item) => {
3573
+ if (item.type === 'resource_summary') {
3574
+ return (
3575
+ <ResourceChangeSummary
3576
+ key={item.key}
3577
+ resources={item.resources}
3578
+ workflowRunId={artifactRunId}
3579
+ className="my-2"
3580
+ />
3581
+ );
3582
+ }
3583
+
3473
3584
  if (item.type === 'work') {
3474
3585
  if (hideToolCallsInViewMode?.includes(viewMode)) return null;
3475
3586
  const isThinkingOnlyWork = isTransientThinkingWork(item.messages);
@@ -3529,6 +3640,7 @@ function AllMessagesMixedComponent({
3529
3640
  <SummaryMessage
3530
3641
  message={message}
3531
3642
  onSendMessage={onSendMessage}
3643
+ onOpenArtifact={onOpenArtifact}
3532
3644
  onSelectWorkstream={handleSelectWorkstream}
3533
3645
  requestInputAnswered={isRequestInputAnswered(
3534
3646
  message,
@@ -47,6 +47,7 @@ export interface ParsedUserAttachments {
47
47
  interface AttachmentPreviewListProps {
48
48
  items: AttachmentPreviewItem[];
49
49
  artifactRunId?: string;
50
+ onOpenArtifact?: (path: string) => void;
50
51
  className?: string;
51
52
  align?: 'start' | 'end';
52
53
  variant?: 'composer' | 'message';
@@ -103,17 +104,28 @@ function isCollectionHref(href: string | undefined): boolean {
103
104
  function AttachmentLink({
104
105
  item,
105
106
  children,
107
+ onOpenArtifact,
106
108
  StoreLinkComponent,
107
109
  CollectionLinkComponent,
108
110
  }: {
109
111
  item: AttachmentPreviewItem;
110
112
  children: React.ReactNode;
113
+ onOpenArtifact?: (path: string) => void;
111
114
  StoreLinkComponent?: AttachmentPreviewListProps['StoreLinkComponent'];
112
115
  CollectionLinkComponent?: AttachmentPreviewListProps['CollectionLinkComponent'];
113
116
  }) {
114
117
  const href = item.href;
115
118
  if (!href) return <>{children}</>;
116
119
 
120
+ const artifactPath = getArtifactPath(item);
121
+ if (artifactPath && /\.md$/i.test(artifactPath) && onOpenArtifact) {
122
+ return (
123
+ <button type="button" className="max-w-full text-start" onClick={() => onOpenArtifact(artifactPath)}>
124
+ {children}
125
+ </button>
126
+ );
127
+ }
128
+
117
129
  if (isStoreObjectHref(href)) {
118
130
  const documentId = getStoreObjectId(href);
119
131
  if (StoreLinkComponent) {
@@ -158,6 +170,7 @@ function AttachmentPreview({
158
170
  artifactRunId,
159
171
  variant,
160
172
  onRemove,
173
+ onOpenArtifact,
161
174
  StoreLinkComponent,
162
175
  CollectionLinkComponent,
163
176
  }: {
@@ -165,6 +178,7 @@ function AttachmentPreview({
165
178
  artifactRunId?: string;
166
179
  variant: 'composer' | 'message';
167
180
  onRemove?: (fileId: string) => void;
181
+ onOpenArtifact?: (path: string) => void;
168
182
  StoreLinkComponent?: AttachmentPreviewListProps['StoreLinkComponent'];
169
183
  CollectionLinkComponent?: AttachmentPreviewListProps['CollectionLinkComponent'];
170
184
  }) {
@@ -338,6 +352,7 @@ function AttachmentPreview({
338
352
  <span className="relative inline-flex max-w-full">
339
353
  <AttachmentLink
340
354
  item={item}
355
+ onOpenArtifact={onOpenArtifact}
341
356
  StoreLinkComponent={StoreLinkComponent}
342
357
  CollectionLinkComponent={CollectionLinkComponent}
343
358
  >
@@ -355,6 +370,7 @@ export function AttachmentPreviewList({
355
370
  align = 'start',
356
371
  variant = 'message',
357
372
  onRemove,
373
+ onOpenArtifact,
358
374
  StoreLinkComponent,
359
375
  CollectionLinkComponent,
360
376
  }: AttachmentPreviewListProps) {
@@ -369,6 +385,7 @@ export function AttachmentPreviewList({
369
385
  artifactRunId={artifactRunId}
370
386
  variant={variant}
371
387
  onRemove={onRemove}
388
+ onOpenArtifact={onOpenArtifact}
372
389
  StoreLinkComponent={StoreLinkComponent}
373
390
  CollectionLinkComponent={CollectionLinkComponent}
374
391
  />