@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
@@ -0,0 +1,48 @@
1
+ /** @vitest-environment jsdom */
2
+
3
+ import { render, screen } from '@testing-library/react';
4
+ import type { ContentObject } from '@vertesia/common';
5
+ import { describe, expect, it, vi } from 'vitest';
6
+ import { I18nProvider } from '../../../../i18n/index.js';
7
+ import { TextEditorPanel } from './TextEditorPanel.js';
8
+
9
+ const mocks = vi.hoisted(() => ({
10
+ navigate: vi.fn(),
11
+ update: vi.fn(),
12
+ }));
13
+
14
+ vi.mock('@vertesia/ui/router', () => ({
15
+ useNavigate: () => mocks.navigate,
16
+ }));
17
+
18
+ vi.mock('@vertesia/ui/session', () => ({
19
+ useUserSession: () => ({
20
+ store: { objects: { update: mocks.update } },
21
+ }),
22
+ }));
23
+
24
+ describe('TextEditorPanel', () => {
25
+ it.each(['text/markdown', 'text/x-markdown'])(
26
+ 'uses the full rich-text document editor for %s content',
27
+ async (contentType) => {
28
+ const object = {
29
+ id: 'document-1',
30
+ content: {
31
+ type: contentType,
32
+ name: 'document.md',
33
+ etag: 'etag-1',
34
+ },
35
+ } as unknown as ContentObject;
36
+
37
+ render(
38
+ <I18nProvider lng="en">
39
+ <TextEditorPanel object={object} text="# Editable document" onClose={vi.fn()} onSaved={vi.fn()} />
40
+ </I18nProvider>,
41
+ );
42
+
43
+ expect(await screen.findByRole('textbox', { name: 'Markdown document editor' })).not.toBeNull();
44
+ expect(screen.getByRole('heading', { name: 'Editable document' })).not.toBeNull();
45
+ expect(await screen.findByRole('toolbar')).not.toBeNull();
46
+ },
47
+ );
48
+ });
@@ -1,10 +1,12 @@
1
1
  import type { ContentObject } from '@vertesia/common';
2
2
  import { Button, errorMessage, useTheme, useToast } from '@vertesia/ui/core';
3
3
  import { useUITranslation } from '@vertesia/ui/i18n';
4
+ import { VertesiaMarkdownDocumentEditor } from '@vertesia/ui/rich-text';
4
5
  import { useNavigate } from '@vertesia/ui/router';
5
6
  import { useUserSession } from '@vertesia/ui/session';
6
7
  import { type IEditorApi, MonacoEditor } from '@vertesia/ui/widgets';
7
- import { useCallback, useRef, useState } from 'react';
8
+ import { useCallback, useEffect, useRef, useState } from 'react';
9
+ import { isMarkdownContentType } from './documentEditingRun.js';
8
10
  import { SaveVersionConfirmModal } from './SaveVersionConfirmModal.js';
9
11
 
10
12
  interface TextEditorPanelProps {
@@ -17,6 +19,7 @@ interface TextEditorPanelProps {
17
19
  function getMonacoLanguage(contentType?: string): string {
18
20
  switch (contentType) {
19
21
  case 'text/markdown':
22
+ case 'text/x-markdown':
20
23
  return 'markdown';
21
24
  case 'application/json':
22
25
  return 'json';
@@ -35,34 +38,48 @@ export function TextEditorPanel({ object, text, onClose, onSaved }: TextEditorPa
35
38
  const { theme } = useTheme();
36
39
  const navigate = useNavigate();
37
40
  const editorRef = useRef<IEditorApi | undefined>(undefined);
41
+ const [editorText, setEditorText] = useState(text);
38
42
  const [isDirty, setIsDirty] = useState(false);
39
43
  const [isSaving, setIsSaving] = useState(false);
40
44
  const [showConfirmation, setShowConfirmation] = useState(false);
41
45
 
42
46
  const language = getMonacoLanguage(object.content?.type);
43
- console.log('Determined language for Monaco Editor:', language);
44
- console.log('TextEditorPanel rendered with object:', object, text);
47
+ const isMarkdown = isMarkdownContentType(object.content?.type);
48
+
49
+ useEffect(() => {
50
+ setEditorText(text);
51
+ setIsDirty(false);
52
+ }, [text]);
45
53
 
46
54
  const handleEditorChange = useCallback(() => {
47
55
  if (!isDirty) setIsDirty(true);
48
56
  }, [isDirty]);
49
57
 
58
+ const handleMarkdownChange = useCallback(
59
+ (markdown: string) => {
60
+ setEditorText(markdown);
61
+ setIsDirty(markdown !== text);
62
+ },
63
+ [text],
64
+ );
65
+
50
66
  function handleSave() {
51
- if (!editorRef.current) return;
67
+ if (!isMarkdown && !editorRef.current) return;
52
68
  setShowConfirmation(true);
53
69
  }
54
70
 
55
71
  async function saveText(createVersion: boolean, versionLabel?: string) {
56
- if (!editorRef.current) return;
72
+ if (!isMarkdown && !editorRef.current) return;
57
73
 
58
- const editorText = editorRef.current.getValue();
74
+ const content = isMarkdown ? editorText : editorRef.current?.getValue();
75
+ if (content === undefined) return;
59
76
  const contentType = object.content?.type || 'text/plain';
60
77
  const fileName = object.content?.name || 'content.txt';
61
78
 
62
79
  try {
63
80
  setIsSaving(true);
64
81
 
65
- const blob = new Blob([editorText], { type: contentType });
82
+ const blob = new Blob([content], { type: contentType });
66
83
  const file = new File([blob], fileName, { type: contentType });
67
84
 
68
85
  const response = await store.objects.update(
@@ -121,19 +138,23 @@ export function TextEditorPanel({ object, text, onClose, onSaved }: TextEditorPa
121
138
  </Button>
122
139
  </div>
123
140
  <div className="flex-1 min-h-0 border rounded-md overflow-hidden mx-2 mb-2">
124
- <MonacoEditor
125
- value={text}
126
- language={language}
127
- editorRef={editorRef}
128
- onChange={handleEditorChange}
129
- theme={theme === 'dark' ? 'vs-dark' : 'vs'}
130
- options={{
131
- wordWrap: 'on',
132
- minimap: { enabled: false },
133
- lineNumbers: 'on',
134
- scrollBeyondLastLine: false,
135
- }}
136
- />
141
+ {isMarkdown ? (
142
+ <VertesiaMarkdownDocumentEditor value={editorText} onChange={handleMarkdownChange} />
143
+ ) : (
144
+ <MonacoEditor
145
+ value={text}
146
+ language={language}
147
+ editorRef={editorRef}
148
+ onChange={handleEditorChange}
149
+ theme={theme === 'dark' ? 'vs-dark' : 'vs'}
150
+ options={{
151
+ wordWrap: 'on',
152
+ minimap: { enabled: false },
153
+ lineNumbers: 'on',
154
+ scrollBeyondLastLine: false,
155
+ }}
156
+ />
157
+ )}
137
158
  </div>
138
159
 
139
160
  <SaveVersionConfirmModal
@@ -0,0 +1,65 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { collectRunLocalArtifactRefs, persistRunLocalArtifactRefs } from './documentArtifactRefs.js';
3
+
4
+ const DOC = [
5
+ '![Chart 1](artifact:documents/abc-123/out/01_chart.png)',
6
+ '![Chart 2](artifact:out/02_chart.png)',
7
+ '![Chart 2 again](artifact:out/02_chart.png)',
8
+ '![Legacy](artifact:agents/run-9/out/03_chart.png)',
9
+ '[data file](artifact:files/data.csv)',
10
+ '![external](https://example.com/x.png)',
11
+ ].join('\n');
12
+
13
+ describe('collectRunLocalArtifactRefs', () => {
14
+ it('collects only run-local refs, deduplicated', () => {
15
+ expect(collectRunLocalArtifactRefs(DOC)).toEqual(['out/02_chart.png', 'files/data.csv']);
16
+ });
17
+
18
+ it('ignores documents/ and agents/ refs and non-artifact urls', () => {
19
+ expect(collectRunLocalArtifactRefs('![a](artifact:documents/x/y.png) ![b](https://e.com/i.png)')).toEqual([]);
20
+ });
21
+ });
22
+
23
+ describe('persistRunLocalArtifactRefs', () => {
24
+ it('copies each run-local ref once and rewrites all occurrences', async () => {
25
+ const copyFile = vi.fn().mockResolvedValue('ok');
26
+ const result = await persistRunLocalArtifactRefs({ copyFile }, DOC, 'run-1', 'batch-1');
27
+
28
+ expect(copyFile).toHaveBeenCalledTimes(2);
29
+ expect(copyFile).toHaveBeenCalledWith('agents/run-1/out/02_chart.png', 'documents/batch-1/out/02_chart.png');
30
+ expect(copyFile).toHaveBeenCalledWith('agents/run-1/files/data.csv', 'documents/batch-1/files/data.csv');
31
+
32
+ expect(result.content).toContain('![Chart 2](artifact:documents/batch-1/out/02_chart.png)');
33
+ expect(result.content).toContain('![Chart 2 again](artifact:documents/batch-1/out/02_chart.png)');
34
+ expect(result.content).toContain('[data file](artifact:documents/batch-1/files/data.csv)');
35
+ // Durable and external refs stay untouched
36
+ expect(result.content).toContain('artifact:documents/abc-123/out/01_chart.png');
37
+ expect(result.content).toContain('artifact:agents/run-9/out/03_chart.png');
38
+ expect(result.content).toContain('https://example.com/x.png');
39
+ expect(result.persisted).toHaveLength(2);
40
+ expect(result.failed).toHaveLength(0);
41
+ });
42
+
43
+ it('pins failed copies to the explicit run-scoped path', async () => {
44
+ const copyFile = vi.fn().mockRejectedValue(new Error('not found'));
45
+ const result = await persistRunLocalArtifactRefs(
46
+ { copyFile },
47
+ '![Chart 2](artifact:out/02_chart.png)',
48
+ 'run-1',
49
+ 'batch-1',
50
+ );
51
+
52
+ expect(result.content).toBe('![Chart 2](artifact:agents/run-1/out/02_chart.png)');
53
+ expect(result.failed).toEqual(['out/02_chart.png']);
54
+ expect(result.persisted).toHaveLength(0);
55
+ });
56
+
57
+ it('returns content unchanged and skips IO when there is nothing to persist', async () => {
58
+ const copyFile = vi.fn();
59
+ const content = '![a](artifact:documents/x/y.png)';
60
+ const result = await persistRunLocalArtifactRefs({ copyFile }, content, 'run-1', 'batch-1');
61
+
62
+ expect(copyFile).not.toHaveBeenCalled();
63
+ expect(result.content).toBe(content);
64
+ });
65
+ });
@@ -0,0 +1,72 @@
1
+ export interface DocumentArtifactCopyApi {
2
+ copyFile(source: string, dest: string): Promise<unknown>;
3
+ }
4
+
5
+ // Run-local artifact links — excludes paths already pointing at durable storage
6
+ // (agents/ run prefixes, documents/ repository copies). Mirrors the regex used by
7
+ // the create_document tool so both publication paths persist the same links.
8
+ const RUN_LOCAL_ARTIFACT_REF_REGEX = /artifact:(?!agents\/)(?!documents\/)([^\s)\]"]+)/g;
9
+
10
+ export function collectRunLocalArtifactRefs(content: string): string[] {
11
+ const refs = new Set<string>();
12
+ for (const match of content.matchAll(RUN_LOCAL_ARTIFACT_REF_REGEX)) {
13
+ refs.add(match[1].replace(/^\/+/, ''));
14
+ }
15
+ return [...refs];
16
+ }
17
+
18
+ export interface PersistRunLocalArtifactRefsResult {
19
+ content: string;
20
+ /** Links rewritten to a durable documents/ copy. */
21
+ persisted: Array<{ from: string; to: string }>;
22
+ /** Links whose copy failed; rewritten to the run-scoped agents/ path instead. */
23
+ failed: string[];
24
+ }
25
+
26
+ /**
27
+ * Copy run-local `artifact:` references into durable `documents/{batchId}/` storage
28
+ * and rewrite the links — the Save-to-document counterpart of the persistence pass
29
+ * `create_document` applies when a document is first created from agent content.
30
+ *
31
+ * Run artifact storage is scoped to the agent run and does not outlive it, so a
32
+ * published document must never depend on it. When a copy fails the link is pinned
33
+ * to the explicit run-scoped `agents/` path rather than left run-relative, so it
34
+ * can never silently resolve against a different run's artifact space.
35
+ */
36
+ export async function persistRunLocalArtifactRefs(
37
+ files: DocumentArtifactCopyApi,
38
+ content: string,
39
+ agentRunId: string,
40
+ batchId: string,
41
+ ): Promise<PersistRunLocalArtifactRefsResult> {
42
+ const refs = collectRunLocalArtifactRefs(content);
43
+ if (refs.length === 0) {
44
+ return { content, persisted: [], failed: [] };
45
+ }
46
+
47
+ const rewrites = new Map<string, string>();
48
+ const persisted: Array<{ from: string; to: string }> = [];
49
+ const failed: string[] = [];
50
+ for (const relPath of refs) {
51
+ const destPath = `documents/${batchId}/${relPath}`;
52
+ try {
53
+ await files.copyFile(`agents/${agentRunId}/${relPath}`, destPath);
54
+ rewrites.set(relPath, destPath);
55
+ persisted.push({ from: relPath, to: destPath });
56
+ } catch (error: unknown) {
57
+ console.warn('Failed to persist a referenced artifact; keeping a run-scoped link', {
58
+ artifact_path: relPath,
59
+ error: error instanceof Error ? error.message : String(error),
60
+ });
61
+ rewrites.set(relPath, `agents/${agentRunId}/${relPath}`);
62
+ failed.push(relPath);
63
+ }
64
+ }
65
+
66
+ const rewritten = content.replace(RUN_LOCAL_ARTIFACT_REF_REGEX, (match, relPath: string) => {
67
+ const destPath = rewrites.get(relPath.replace(/^\/+/, ''));
68
+ return destPath ? `artifact:${destPath}` : match;
69
+ });
70
+
71
+ return { content: rewritten, persisted, failed };
72
+ }
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ DOCUMENT_EDITING_EXCLUDED_TOOLS,
4
+ DOCUMENT_EDITING_INITIAL_SKILLS,
5
+ DOCUMENT_EDITING_TOOLS,
6
+ } from './documentEditingAgentConfig.js';
7
+
8
+ describe('document editing agent configuration', () => {
9
+ it('starts with artifact and code execution guidance and permits shell execution', () => {
10
+ expect(DOCUMENT_EDITING_INITIAL_SKILLS).toEqual(['artifact_operations', 'code_execution']);
11
+ expect(DOCUMENT_EDITING_TOOLS).toContain('execute_shell');
12
+ expect(DOCUMENT_EDITING_EXCLUDED_TOOLS).not.toContain('execute_shell');
13
+ });
14
+
15
+ it('offers relevant skills without exposing canonical document writes', () => {
16
+ expect(DOCUMENT_EDITING_TOOLS).toEqual(
17
+ expect.arrayContaining([
18
+ 'learn_content_authoring',
19
+ 'learn_document_management',
20
+ 'learn_document_search',
21
+ 'learn_image_analysis',
22
+ ]),
23
+ );
24
+ expect(DOCUMENT_EDITING_EXCLUDED_TOOLS).toEqual(
25
+ expect.arrayContaining(['create_document', 'update_document', 'merge_documents', 'batch_execute']),
26
+ );
27
+ });
28
+ });
@@ -0,0 +1,41 @@
1
+ export const DOCUMENT_EDITING_TOOLS = [
2
+ 'ask_user',
3
+ 'think',
4
+ 'plan',
5
+ 'update_plan',
6
+ 'end_conversation',
7
+ 'learn_artifact_operations',
8
+ 'learn_code_execution',
9
+ 'learn_content_authoring',
10
+ 'learn_document_management',
11
+ 'learn_document_search',
12
+ 'learn_image_analysis',
13
+ // Available immediately as well as through the code_execution skill so an
14
+ // editing agent can regenerate charts and other derived assets.
15
+ 'execute_shell',
16
+ // Read access to canonical revisions: reconcile-after-conflict and change
17
+ // summaries fetch other revisions into reference artifacts for comparison.
18
+ 'fetch_document',
19
+ ];
20
+
21
+ // Artifact operations and code execution are core editing capabilities, so their
22
+ // guidance and tools are active before the first model turn. Other skills remain
23
+ // available through their learn_* tools when the task calls for them.
24
+ export const DOCUMENT_EDITING_INITIAL_SKILLS = ['artifact_operations', 'code_execution'];
25
+
26
+ // Editing sessions mutate only the working-copy artifact; the canonical document is
27
+ // published exclusively through Save to document. These tools stay unavailable even
28
+ // if a skill or tool refresh would otherwise unlock them.
29
+ export const DOCUMENT_EDITING_EXCLUDED_TOOLS = [
30
+ 'update_document',
31
+ 'update_document_properties',
32
+ 'create_document',
33
+ 'set_document_type',
34
+ 'merge_documents',
35
+ 'import_file',
36
+ 'create_or_update_object_type',
37
+ 'disable_type',
38
+ // Keep multi-command fan-out unavailable; execute_shell is intentionally
39
+ // allowed as the single, observable sandbox execution path.
40
+ 'batch_execute',
41
+ ];
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { createDirectEditsAppliedPrompt } from './documentEditingPrompts.js';
3
+
4
+ describe('createDirectEditsAppliedPrompt', () => {
5
+ it('sends a compact unified diff with file headers', () => {
6
+ const prompt = createDirectEditsAppliedPrompt(
7
+ 'drafts/document.md',
8
+ ['@@ -2,3 +2,3 @@', ' unchanged', '-First paragraph.', '+Updated paragraph.'].join('\n'),
9
+ );
10
+
11
+ expect(prompt).toContain('--- a/drafts/document.md');
12
+ expect(prompt).toContain('+++ b/drafts/document.md');
13
+ expect(prompt).toContain('@@ -2,3 +2,3 @@');
14
+ expect(prompt).toContain('-First paragraph.');
15
+ expect(prompt).toContain('+Updated paragraph.');
16
+ expect(prompt).toContain('do not re-apply it');
17
+ });
18
+
19
+ it('does not discard a unified diff larger than the previous fallback limit', () => {
20
+ const unifiedDiff = Array.from({ length: 600 }, (_, index) => `-${'before'.repeat(2)} ${index}`).join('\n');
21
+ const prompt = createDirectEditsAppliedPrompt('drafts/large.md', unifiedDiff);
22
+
23
+ expect(prompt.length).toBeGreaterThan(4000);
24
+ expect(prompt).toContain('-beforebefore 599');
25
+ });
26
+ });
@@ -0,0 +1,12 @@
1
+ /** Chat notice handing the agent direct editor edits that already exist in the working copy. */
2
+ export function createDirectEditsAppliedPrompt(draftPath: string, unifiedDiff: string): string {
3
+ return [
4
+ `Direct edits are already saved to '${draftPath}'. Use this compact unified diff as context only;`,
5
+ 'do not re-apply it. The artifact remains the current source of truth.',
6
+ '```diff',
7
+ `--- a/${draftPath}`,
8
+ `+++ b/${draftPath}`,
9
+ unifiedDiff,
10
+ '```',
11
+ ].join('\n');
12
+ }
@@ -0,0 +1,176 @@
1
+ import type { AgentRun } from '@vertesia/common';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import {
4
+ createDocumentEditingRunIdentity,
5
+ createDocumentEditingScopeKey,
6
+ findDocumentEditingRun,
7
+ getDocumentTextActionAccess,
8
+ isDocumentEditingContentType,
9
+ isDocumentEditingRun,
10
+ isDocumentEditingScopeOpen,
11
+ isMarkdownContentType,
12
+ setDocumentEditingScopeOpen,
13
+ } from './documentEditingRun.js';
14
+
15
+ const startedBy = 'user:user-1';
16
+
17
+ function createRun(overrides: Partial<AgentRun> = {}): AgentRun {
18
+ return {
19
+ id: 'run-1',
20
+ run_kind: 'agent',
21
+ run_type: 'autonomous',
22
+ interaction: 'sys:GeneralAgent',
23
+ interactionRef: { id: 'sys:GeneralAgent' },
24
+ status: 'running',
25
+ started_by: startedBy,
26
+ started_at: new Date(),
27
+ created_at: new Date(),
28
+ updated_at: new Date(),
29
+ ...overrides,
30
+ } as AgentRun;
31
+ }
32
+
33
+ describe('document editing run identity', () => {
34
+ it('keeps the editing panel open across document component remounts', () => {
35
+ const scopeKey = createDocumentEditingScopeKey('project-1', 'document-root');
36
+
37
+ setDocumentEditingScopeOpen(scopeKey, true);
38
+ expect(isDocumentEditingScopeOpen(scopeKey)).toBe(true);
39
+
40
+ setDocumentEditingScopeOpen(scopeKey, false);
41
+ expect(isDocumentEditingScopeOpen(scopeKey)).toBe(false);
42
+ });
43
+
44
+ it('keeps collaboration access independent from direct editing access', () => {
45
+ expect(getDocumentTextActionAccess(false, true)).toEqual({
46
+ canEdit: false,
47
+ canCollaborate: true,
48
+ });
49
+ });
50
+
51
+ it.each(['text/markdown', 'text/x-markdown', 'text/plain', 'TEXT/MARKDOWN; charset=utf-8'])(
52
+ 'allows AI document editing for %s',
53
+ (contentType) => {
54
+ expect(isDocumentEditingContentType(contentType)).toBe(true);
55
+ },
56
+ );
57
+
58
+ it.each(['application/octet-stream', 'application/json', undefined])(
59
+ 'does not treat %s as AI-editable text',
60
+ (contentType) => {
61
+ expect(isDocumentEditingContentType(contentType)).toBe(false);
62
+ },
63
+ );
64
+
65
+ it('recognizes canonical and legacy Markdown MIME types', () => {
66
+ expect(isMarkdownContentType('text/markdown')).toBe(true);
67
+ expect(isMarkdownContentType('text/x-markdown')).toBe(true);
68
+ expect(isMarkdownContentType('text/plain')).toBe(false);
69
+ });
70
+
71
+ it('creates stable revision-root tags and properties', () => {
72
+ expect(createDocumentEditingRunIdentity('document-2', 'document-root')).toEqual({
73
+ tags: ['document-editing', 'document-root:document-root', 'document:document-2'],
74
+ properties: {
75
+ resource_kind: 'store_document',
76
+ document_id: 'document-2',
77
+ document_root_id: 'document-root',
78
+ },
79
+ });
80
+ });
81
+
82
+ it('requires the same user and matching root properties for stable runs', () => {
83
+ const identity = createDocumentEditingRunIdentity('document-2', 'document-root');
84
+ const run = createRun(identity);
85
+
86
+ expect(isDocumentEditingRun(run, 'document-3', 'document-root', startedBy)).toBe(true);
87
+ expect(isDocumentEditingRun(run, 'document-3', 'another-root', startedBy)).toBe(false);
88
+ expect(isDocumentEditingRun(run, 'document-3', 'document-root', 'user:user-2')).toBe(false);
89
+ });
90
+
91
+ it('reattaches a legacy document-tagged run owned by the user', () => {
92
+ const run = createRun({ tags: ['document-editing', 'document:document-root'] });
93
+
94
+ expect(isDocumentEditingRun(run, 'document-2', 'document-root', startedBy)).toBe(true);
95
+ });
96
+
97
+ it('never resumes a run the user terminated', () => {
98
+ const identity = createDocumentEditingRunIdentity('document-2', 'document-root');
99
+ const run = createRun({ ...identity, status: 'cancelled' });
100
+
101
+ expect(isDocumentEditingRun(run, 'document-2', 'document-root', startedBy)).toBe(false);
102
+ });
103
+
104
+ it('matches the interaction configured for the document type', () => {
105
+ const identity = createDocumentEditingRunIdentity('document-2', 'document-root');
106
+ const run = createRun({ ...identity, interaction: 'custom-editor' });
107
+
108
+ expect(isDocumentEditingRun(run, 'document-2', 'document-root', startedBy, 'custom-editor')).toBe(true);
109
+ expect(isDocumentEditingRun(run, 'document-2', 'document-root', startedBy)).toBe(false);
110
+ });
111
+ });
112
+
113
+ describe('findDocumentEditingRun', () => {
114
+ it('searches by root tag and verifies the retrieved run', async () => {
115
+ const identity = createDocumentEditingRunIdentity('document-2', 'document-root');
116
+ const run = createRun(identity);
117
+ const agents = {
118
+ search: vi.fn().mockResolvedValue({
119
+ hits: [{ id: run.id, created_at: '2026-01-01', updated_at: '2026-01-02' }],
120
+ total: 1,
121
+ }),
122
+ retrieve: vi.fn().mockResolvedValue(run),
123
+ list: vi.fn(),
124
+ };
125
+
126
+ await expect(findDocumentEditingRun(agents, 'document-2', 'document-root', startedBy)).resolves.toBe(run);
127
+ expect(agents.search).toHaveBeenCalledWith({
128
+ interaction: 'sys:GeneralAgent',
129
+ started_by: startedBy,
130
+ tags: ['document-root:document-root'],
131
+ limit: 20,
132
+ sort: ['updated_at:desc'],
133
+ });
134
+ expect(agents.list).not.toHaveBeenCalled();
135
+ });
136
+
137
+ it('falls back to recent user-owned runs when search is unavailable', async () => {
138
+ const run = createRun({ tags: ['document-editing', 'document:document-root'] });
139
+ const agents = {
140
+ search: vi.fn().mockRejectedValue(new Error('search unavailable')),
141
+ retrieve: vi.fn(),
142
+ list: vi.fn().mockResolvedValue({ items: [run], total_count: 1, next_cursor: null }),
143
+ };
144
+
145
+ await expect(findDocumentEditingRun(agents, 'document-2', 'document-root', startedBy)).resolves.toBe(run);
146
+ expect(agents.list).toHaveBeenCalledWith({
147
+ interaction: 'sys:GeneralAgent',
148
+ started_by: startedBy,
149
+ limit: 100,
150
+ sort: 'updated_at',
151
+ order: 'desc',
152
+ });
153
+ });
154
+
155
+ it('searches and validates runs using a type-specific interaction', async () => {
156
+ const identity = createDocumentEditingRunIdentity('document-2', 'document-root');
157
+ const run = createRun({ ...identity, interaction: 'custom-editor' });
158
+ const agents = {
159
+ search: vi.fn().mockResolvedValue({
160
+ hits: [{ id: run.id, created_at: '2026-01-01', updated_at: '2026-01-02' }],
161
+ total: 1,
162
+ }),
163
+ retrieve: vi.fn().mockResolvedValue(run),
164
+ list: vi.fn(),
165
+ };
166
+
167
+ await expect(
168
+ findDocumentEditingRun(agents, 'document-2', 'document-root', startedBy, 'custom-editor'),
169
+ ).resolves.toBe(run);
170
+ expect(agents.search).toHaveBeenCalledWith(
171
+ expect.objectContaining({
172
+ interaction: 'custom-editor',
173
+ }),
174
+ );
175
+ });
176
+ });