@miragon/mcp-toolkit-ui 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (511) hide show
  1. package/README.md +14 -5
  2. package/dist/app/adapt-data-widget.d.ts +1 -22
  3. package/dist/app/adapt-data-widget.d.ts.map +1 -1
  4. package/dist/app/adapt-data-widget.js +1 -35
  5. package/dist/app/adapt-data-widget.js.map +1 -1
  6. package/dist/app/builder/BuilderBanners.d.ts +7 -2
  7. package/dist/app/builder/BuilderBanners.d.ts.map +1 -1
  8. package/dist/app/builder/BuilderBanners.js +8 -3
  9. package/dist/app/builder/BuilderBanners.js.map +1 -1
  10. package/dist/app/builder/CanvasRow.d.ts +4 -2
  11. package/dist/app/builder/CanvasRow.d.ts.map +1 -1
  12. package/dist/app/builder/CanvasRow.js +29 -17
  13. package/dist/app/builder/CanvasRow.js.map +1 -1
  14. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
  15. package/dist/app/builder/CatalogueSheet.js +14 -6
  16. package/dist/app/builder/CatalogueSheet.js.map +1 -1
  17. package/dist/app/builder/CellPropsSheet.d.ts +7 -3
  18. package/dist/app/builder/CellPropsSheet.d.ts.map +1 -1
  19. package/dist/app/builder/CellPropsSheet.js +10 -3
  20. package/dist/app/builder/CellPropsSheet.js.map +1 -1
  21. package/dist/app/builder/DiscardDialog.d.ts +18 -0
  22. package/dist/app/builder/DiscardDialog.d.ts.map +1 -0
  23. package/dist/app/builder/DiscardDialog.js +18 -0
  24. package/dist/app/builder/DiscardDialog.js.map +1 -0
  25. package/dist/app/builder/EmptyHint.d.ts +4 -0
  26. package/dist/app/builder/EmptyHint.d.ts.map +1 -1
  27. package/dist/app/builder/EmptyHint.js +6 -1
  28. package/dist/app/builder/EmptyHint.js.map +1 -1
  29. package/dist/app/builder/LayoutSurface.d.ts +33 -9
  30. package/dist/app/builder/LayoutSurface.d.ts.map +1 -1
  31. package/dist/app/builder/LayoutSurface.js +74 -23
  32. package/dist/app/builder/LayoutSurface.js.map +1 -1
  33. package/dist/app/builder/PipelineStrip.d.ts +5 -2
  34. package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
  35. package/dist/app/builder/PipelineStrip.js +33 -15
  36. package/dist/app/builder/PipelineStrip.js.map +1 -1
  37. package/dist/app/builder/SaveDialog.d.ts +9 -1
  38. package/dist/app/builder/SaveDialog.d.ts.map +1 -1
  39. package/dist/app/builder/SaveDialog.js +9 -3
  40. package/dist/app/builder/SaveDialog.js.map +1 -1
  41. package/dist/app/builder/Toolbar.d.ts +14 -4
  42. package/dist/app/builder/Toolbar.d.ts.map +1 -1
  43. package/dist/app/builder/Toolbar.js +24 -7
  44. package/dist/app/builder/Toolbar.js.map +1 -1
  45. package/dist/app/builder/Workspace.d.ts +23 -3
  46. package/dist/app/builder/Workspace.d.ts.map +1 -1
  47. package/dist/app/builder/Workspace.js +58 -8
  48. package/dist/app/builder/Workspace.js.map +1 -1
  49. package/dist/app/builder/builder-model.d.ts +146 -3
  50. package/dist/app/builder/builder-model.d.ts.map +1 -1
  51. package/dist/app/builder/builder-model.js +196 -5
  52. package/dist/app/builder/builder-model.js.map +1 -1
  53. package/dist/app/builder/labels.d.ts +13 -62
  54. package/dist/app/builder/labels.d.ts.map +1 -1
  55. package/dist/app/builder/labels.js +9 -58
  56. package/dist/app/builder/labels.js.map +1 -1
  57. package/dist/app/builder/reveal.d.ts +10 -0
  58. package/dist/app/builder/reveal.d.ts.map +1 -0
  59. package/dist/app/builder/reveal.js +10 -0
  60. package/dist/app/builder/reveal.js.map +1 -0
  61. package/dist/app/builder/status-chip.d.ts.map +1 -1
  62. package/dist/app/builder/status-chip.js +3 -2
  63. package/dist/app/builder/status-chip.js.map +1 -1
  64. package/dist/app/builder/use-builder-catalogue.d.ts +5 -1
  65. package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
  66. package/dist/app/builder/use-builder-catalogue.js +50 -17
  67. package/dist/app/builder/use-builder-catalogue.js.map +1 -1
  68. package/dist/app/builder/use-builder-session.d.ts +30 -0
  69. package/dist/app/builder/use-builder-session.d.ts.map +1 -0
  70. package/dist/app/builder/use-builder-session.js +58 -0
  71. package/dist/app/builder/use-builder-session.js.map +1 -0
  72. package/dist/app/builder/use-display-resolver.d.ts +17 -0
  73. package/dist/app/builder/use-display-resolver.d.ts.map +1 -0
  74. package/dist/app/builder/use-display-resolver.js +16 -0
  75. package/dist/app/builder/use-display-resolver.js.map +1 -0
  76. package/dist/app/builder/use-save-dashboard.d.ts +3 -1
  77. package/dist/app/builder/use-save-dashboard.d.ts.map +1 -1
  78. package/dist/app/builder/use-save-dashboard.js +3 -1
  79. package/dist/app/builder/use-save-dashboard.js.map +1 -1
  80. package/dist/app/index.d.ts +8 -3
  81. package/dist/app/index.d.ts.map +1 -1
  82. package/dist/app/index.js +8 -2
  83. package/dist/app/index.js.map +1 -1
  84. package/dist/app/layout-builder.d.ts +18 -25
  85. package/dist/app/layout-builder.d.ts.map +1 -1
  86. package/dist/app/layout-builder.js +84 -78
  87. package/dist/app/layout-builder.js.map +1 -1
  88. package/dist/app/mcp-app-view-chrome.d.ts +103 -20
  89. package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
  90. package/dist/app/mcp-app-view-chrome.js +125 -40
  91. package/dist/app/mcp-app-view-chrome.js.map +1 -1
  92. package/dist/app/mcp-app-view-states.d.ts +23 -0
  93. package/dist/app/mcp-app-view-states.d.ts.map +1 -0
  94. package/dist/app/mcp-app-view-states.js +55 -0
  95. package/dist/app/mcp-app-view-states.js.map +1 -0
  96. package/dist/app/mcp-app-view.d.ts +72 -5
  97. package/dist/app/mcp-app-view.d.ts.map +1 -1
  98. package/dist/app/mcp-app-view.js +241 -141
  99. package/dist/app/mcp-app-view.js.map +1 -1
  100. package/dist/app/mcp-toolkit-app.d.ts +8 -2
  101. package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
  102. package/dist/app/mcp-toolkit-app.js +33 -4
  103. package/dist/app/mcp-toolkit-app.js.map +1 -1
  104. package/dist/app/mcp-use-host-bridge.d.ts +15 -6
  105. package/dist/app/mcp-use-host-bridge.d.ts.map +1 -1
  106. package/dist/app/mcp-use-host-bridge.js +51 -22
  107. package/dist/app/mcp-use-host-bridge.js.map +1 -1
  108. package/dist/app/model-context.d.ts +71 -0
  109. package/dist/app/model-context.d.ts.map +1 -0
  110. package/dist/app/model-context.js +101 -0
  111. package/dist/app/model-context.js.map +1 -0
  112. package/dist/app/use-build-mode-focus.d.ts +33 -0
  113. package/dist/app/use-build-mode-focus.d.ts.map +1 -0
  114. package/dist/app/use-build-mode-focus.js +59 -0
  115. package/dist/app/use-build-mode-focus.js.map +1 -0
  116. package/dist/app/use-view-actions.d.ts +31 -0
  117. package/dist/app/use-view-actions.d.ts.map +1 -0
  118. package/dist/app/use-view-actions.js +81 -0
  119. package/dist/app/use-view-actions.js.map +1 -0
  120. package/dist/app/use-view-lifecycle.d.ts +42 -0
  121. package/dist/app/use-view-lifecycle.d.ts.map +1 -0
  122. package/dist/app/use-view-lifecycle.js +81 -0
  123. package/dist/app/use-view-lifecycle.js.map +1 -0
  124. package/dist/app/view-lifecycle.d.ts +185 -0
  125. package/dist/app/view-lifecycle.d.ts.map +1 -0
  126. package/dist/app/view-lifecycle.js +207 -0
  127. package/dist/app/view-lifecycle.js.map +1 -0
  128. package/dist/app/widget-fixture.d.ts +10 -3
  129. package/dist/app/widget-fixture.d.ts.map +1 -1
  130. package/dist/app/widget-fixture.js +21 -8
  131. package/dist/app/widget-fixture.js.map +1 -1
  132. package/dist/app/widget-props-sheet.d.ts +6 -10
  133. package/dist/app/widget-props-sheet.d.ts.map +1 -1
  134. package/dist/app/widget-props-sheet.js +25 -20
  135. package/dist/app/widget-props-sheet.js.map +1 -1
  136. package/dist/app/widget-renderer.d.ts +41 -1
  137. package/dist/app/widget-renderer.d.ts.map +1 -1
  138. package/dist/app/widget-renderer.js +82 -31
  139. package/dist/app/widget-renderer.js.map +1 -1
  140. package/dist/components/DrillButton.d.ts +9 -6
  141. package/dist/components/DrillButton.d.ts.map +1 -1
  142. package/dist/components/DrillButton.js +9 -6
  143. package/dist/components/DrillButton.js.map +1 -1
  144. package/dist/components/EmptyState.d.ts +33 -0
  145. package/dist/components/EmptyState.d.ts.map +1 -0
  146. package/dist/components/EmptyState.js +19 -0
  147. package/dist/components/EmptyState.js.map +1 -0
  148. package/dist/components/ErrorState.d.ts +53 -0
  149. package/dist/components/ErrorState.d.ts.map +1 -0
  150. package/dist/components/ErrorState.js +77 -0
  151. package/dist/components/ErrorState.js.map +1 -0
  152. package/dist/components/FilterBar.d.ts +16 -5
  153. package/dist/components/FilterBar.d.ts.map +1 -1
  154. package/dist/components/FilterBar.js +13 -5
  155. package/dist/components/FilterBar.js.map +1 -1
  156. package/dist/components/GroupCard.d.ts.map +1 -1
  157. package/dist/components/GroupCard.js +2 -1
  158. package/dist/components/GroupCard.js.map +1 -1
  159. package/dist/components/KpiGrid.d.ts +70 -18
  160. package/dist/components/KpiGrid.d.ts.map +1 -1
  161. package/dist/components/KpiGrid.js +112 -23
  162. package/dist/components/KpiGrid.js.map +1 -1
  163. package/dist/components/ListFooter.d.ts +32 -4
  164. package/dist/components/ListFooter.d.ts.map +1 -1
  165. package/dist/components/ListFooter.js +35 -6
  166. package/dist/components/ListFooter.js.map +1 -1
  167. package/dist/components/PagedListFooter.d.ts +57 -0
  168. package/dist/components/PagedListFooter.d.ts.map +1 -0
  169. package/dist/components/PagedListFooter.js +27 -0
  170. package/dist/components/PagedListFooter.js.map +1 -0
  171. package/dist/components/Pills.d.ts +36 -12
  172. package/dist/components/Pills.d.ts.map +1 -1
  173. package/dist/components/Pills.js +29 -8
  174. package/dist/components/Pills.js.map +1 -1
  175. package/dist/components/SectionHeading.d.ts +3 -1
  176. package/dist/components/SectionHeading.d.ts.map +1 -1
  177. package/dist/components/SectionHeading.js +6 -2
  178. package/dist/components/SectionHeading.js.map +1 -1
  179. package/dist/components/Toast.d.ts +43 -0
  180. package/dist/components/Toast.d.ts.map +1 -0
  181. package/dist/components/Toast.js +106 -0
  182. package/dist/components/Toast.js.map +1 -0
  183. package/dist/components/WidgetHeader.d.ts +34 -10
  184. package/dist/components/WidgetHeader.d.ts.map +1 -1
  185. package/dist/components/WidgetHeader.js +11 -6
  186. package/dist/components/WidgetHeader.js.map +1 -1
  187. package/dist/hooks/index.d.ts +4 -4
  188. package/dist/hooks/index.d.ts.map +1 -1
  189. package/dist/hooks/index.js +1 -1
  190. package/dist/hooks/index.js.map +1 -1
  191. package/dist/hooks/tool-result-recovery-model.d.ts +155 -0
  192. package/dist/hooks/tool-result-recovery-model.d.ts.map +1 -0
  193. package/dist/hooks/tool-result-recovery-model.js +273 -0
  194. package/dist/hooks/tool-result-recovery-model.js.map +1 -0
  195. package/dist/hooks/use-tool-query.d.ts +56 -14
  196. package/dist/hooks/use-tool-query.d.ts.map +1 -1
  197. package/dist/hooks/use-tool-query.js +102 -25
  198. package/dist/hooks/use-tool-query.js.map +1 -1
  199. package/dist/hooks/use-tool-result-recovery.d.ts +61 -44
  200. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
  201. package/dist/hooks/use-tool-result-recovery.js +62 -110
  202. package/dist/hooks/use-tool-result-recovery.js.map +1 -1
  203. package/dist/hooks/use-view-data.d.ts +55 -7
  204. package/dist/hooks/use-view-data.d.ts.map +1 -1
  205. package/dist/hooks/use-view-data.js +81 -11
  206. package/dist/hooks/use-view-data.js.map +1 -1
  207. package/dist/hooks/use-view-tool-query.d.ts +12 -7
  208. package/dist/hooks/use-view-tool-query.d.ts.map +1 -1
  209. package/dist/hooks/use-view-tool-query.js +15 -14
  210. package/dist/hooks/use-view-tool-query.js.map +1 -1
  211. package/dist/index.d.ts +27 -13
  212. package/dist/index.d.ts.map +1 -1
  213. package/dist/index.js +29 -7
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/focus-return.d.ts +61 -0
  216. package/dist/lib/focus-return.d.ts.map +1 -0
  217. package/dist/lib/focus-return.js +46 -0
  218. package/dist/lib/focus-return.js.map +1 -0
  219. package/dist/lib/focus-ring.d.ts +18 -0
  220. package/dist/lib/focus-ring.d.ts.map +1 -0
  221. package/dist/lib/focus-ring.js +18 -0
  222. package/dist/lib/focus-ring.js.map +1 -0
  223. package/dist/lib/icon-stroke.d.ts +12 -0
  224. package/dist/lib/icon-stroke.d.ts.map +1 -0
  225. package/dist/lib/icon-stroke.js +12 -0
  226. package/dist/lib/icon-stroke.js.map +1 -0
  227. package/dist/lib/parse-tool-result.d.ts +23 -1
  228. package/dist/lib/parse-tool-result.d.ts.map +1 -1
  229. package/dist/lib/parse-tool-result.js +38 -2
  230. package/dist/lib/parse-tool-result.js.map +1 -1
  231. package/dist/lib/toast-model.d.ts +86 -0
  232. package/dist/lib/toast-model.d.ts.map +1 -0
  233. package/dist/lib/toast-model.js +102 -0
  234. package/dist/lib/toast-model.js.map +1 -0
  235. package/dist/lib/tone-utils.d.ts +95 -9
  236. package/dist/lib/tone-utils.d.ts.map +1 -1
  237. package/dist/lib/tone-utils.js +100 -9
  238. package/dist/lib/tone-utils.js.map +1 -1
  239. package/dist/lib/tool-query.d.ts +107 -0
  240. package/dist/lib/tool-query.d.ts.map +1 -0
  241. package/dist/lib/tool-query.js +182 -0
  242. package/dist/lib/tool-query.js.map +1 -0
  243. package/dist/lib/tool-result-observer.d.ts +108 -0
  244. package/dist/lib/tool-result-observer.d.ts.map +1 -0
  245. package/dist/lib/tool-result-observer.js +158 -0
  246. package/dist/lib/tool-result-observer.js.map +1 -0
  247. package/dist/lib/typography.d.ts +37 -0
  248. package/dist/lib/typography.d.ts.map +1 -0
  249. package/dist/lib/typography.js +41 -0
  250. package/dist/lib/typography.js.map +1 -0
  251. package/dist/lib/utils.d.ts.map +1 -1
  252. package/dist/lib/utils.js +11 -1
  253. package/dist/lib/utils.js.map +1 -1
  254. package/dist/locale/index.d.ts +3 -1
  255. package/dist/locale/index.d.ts.map +1 -1
  256. package/dist/locale/index.js +2 -1
  257. package/dist/locale/index.js.map +1 -1
  258. package/dist/locale/locale-provider.d.ts +42 -1
  259. package/dist/locale/locale-provider.d.ts.map +1 -1
  260. package/dist/locale/locale-provider.js +44 -3
  261. package/dist/locale/locale-provider.js.map +1 -1
  262. package/dist/locale/message-types.d.ts +336 -0
  263. package/dist/locale/message-types.d.ts.map +1 -0
  264. package/dist/locale/message-types.js +7 -0
  265. package/dist/locale/message-types.js.map +1 -0
  266. package/dist/locale/messages-de.d.ts +9 -0
  267. package/dist/locale/messages-de.d.ts.map +1 -0
  268. package/dist/locale/messages-de.js +185 -0
  269. package/dist/locale/messages-de.js.map +1 -0
  270. package/dist/locale/messages-en.d.ts +8 -0
  271. package/dist/locale/messages-en.d.ts.map +1 -0
  272. package/dist/locale/messages-en.js +184 -0
  273. package/dist/locale/messages-en.js.map +1 -0
  274. package/dist/locale/toolkit-messages.d.ts +57 -0
  275. package/dist/locale/toolkit-messages.d.ts.map +1 -0
  276. package/dist/locale/toolkit-messages.js +103 -0
  277. package/dist/locale/toolkit-messages.js.map +1 -0
  278. package/dist/primitives/alert.d.ts +1 -1
  279. package/dist/primitives/alert.d.ts.map +1 -1
  280. package/dist/primitives/alert.js +6 -2
  281. package/dist/primitives/alert.js.map +1 -1
  282. package/dist/primitives/badge.d.ts +1 -1
  283. package/dist/primitives/badge.d.ts.map +1 -1
  284. package/dist/primitives/badge.js +19 -6
  285. package/dist/primitives/badge.js.map +1 -1
  286. package/dist/primitives/button.d.ts +1 -1
  287. package/dist/primitives/button.d.ts.map +1 -1
  288. package/dist/primitives/button.js +12 -8
  289. package/dist/primitives/button.js.map +1 -1
  290. package/dist/primitives/card.js +4 -4
  291. package/dist/primitives/card.js.map +1 -1
  292. package/dist/primitives/dialog.d.ts +7 -3
  293. package/dist/primitives/dialog.d.ts.map +1 -1
  294. package/dist/primitives/dialog.js +22 -8
  295. package/dist/primitives/dialog.js.map +1 -1
  296. package/dist/primitives/input.d.ts.map +1 -1
  297. package/dist/primitives/input.js +4 -1
  298. package/dist/primitives/input.js.map +1 -1
  299. package/dist/primitives/scroll-area.js +1 -1
  300. package/dist/primitives/scroll-area.js.map +1 -1
  301. package/dist/primitives/select.d.ts.map +1 -1
  302. package/dist/primitives/select.js +6 -3
  303. package/dist/primitives/select.js.map +1 -1
  304. package/dist/primitives/sheet.d.ts +3 -1
  305. package/dist/primitives/sheet.d.ts.map +1 -1
  306. package/dist/primitives/sheet.js +17 -7
  307. package/dist/primitives/sheet.js.map +1 -1
  308. package/dist/primitives/skeleton.js +1 -1
  309. package/dist/primitives/skeleton.js.map +1 -1
  310. package/dist/primitives/switch.d.ts.map +1 -1
  311. package/dist/primitives/switch.js +2 -1
  312. package/dist/primitives/switch.js.map +1 -1
  313. package/dist/primitives/tabs.d.ts.map +1 -1
  314. package/dist/primitives/tabs.js +5 -3
  315. package/dist/primitives/tabs.js.map +1 -1
  316. package/dist/providers/query-provider.d.ts +32 -1
  317. package/dist/providers/query-provider.d.ts.map +1 -1
  318. package/dist/providers/query-provider.js +53 -4
  319. package/dist/providers/query-provider.js.map +1 -1
  320. package/dist/render-cases-primitives.js +1 -1
  321. package/dist/render-cases-primitives.js.map +1 -1
  322. package/dist/render-cases.d.ts.map +1 -1
  323. package/dist/render-cases.js +48 -6
  324. package/dist/render-cases.js.map +1 -1
  325. package/dist/render-effects.d.ts +42 -0
  326. package/dist/render-effects.d.ts.map +1 -0
  327. package/dist/render-effects.js +81 -0
  328. package/dist/render-effects.js.map +1 -0
  329. package/dist/render-scan.d.ts +25 -0
  330. package/dist/render-scan.d.ts.map +1 -0
  331. package/dist/render-scan.js +66 -0
  332. package/dist/render-scan.js.map +1 -0
  333. package/dist/theme/cd-tokens.d.ts +66 -0
  334. package/dist/theme/cd-tokens.d.ts.map +1 -0
  335. package/dist/theme/cd-tokens.js +64 -0
  336. package/dist/theme/cd-tokens.js.map +1 -0
  337. package/dist/theme/create-theme.d.ts +214 -39
  338. package/dist/theme/create-theme.d.ts.map +1 -1
  339. package/dist/theme/create-theme.js +204 -13
  340. package/dist/theme/create-theme.js.map +1 -1
  341. package/dist/theme/fonts.d.ts +38 -0
  342. package/dist/theme/fonts.d.ts.map +1 -0
  343. package/dist/theme/fonts.js +56 -0
  344. package/dist/theme/fonts.js.map +1 -0
  345. package/dist/theme/host-fonts.d.ts +39 -0
  346. package/dist/theme/host-fonts.d.ts.map +1 -0
  347. package/dist/theme/host-fonts.js +44 -0
  348. package/dist/theme/host-fonts.js.map +1 -0
  349. package/dist/theme/index.d.ts +8 -1
  350. package/dist/theme/index.d.ts.map +1 -1
  351. package/dist/theme/index.js +5 -1
  352. package/dist/theme/index.js.map +1 -1
  353. package/dist/theme/portal-container.d.ts +16 -0
  354. package/dist/theme/portal-container.d.ts.map +1 -0
  355. package/dist/theme/portal-container.js +19 -0
  356. package/dist/theme/portal-container.js.map +1 -0
  357. package/dist/theme/presets.d.ts +24 -9
  358. package/dist/theme/presets.d.ts.map +1 -1
  359. package/dist/theme/presets.js +44 -11
  360. package/dist/theme/presets.js.map +1 -1
  361. package/dist/theme/theme-provider.d.ts +27 -9
  362. package/dist/theme/theme-provider.d.ts.map +1 -1
  363. package/dist/theme/theme-provider.js +42 -20
  364. package/dist/theme/theme-provider.js.map +1 -1
  365. package/package.json +11 -5
  366. package/src/app/adapt-data-widget.tsx +2 -42
  367. package/src/app/builder/BuilderBanners.tsx +26 -13
  368. package/src/app/builder/CanvasRow.tsx +79 -32
  369. package/src/app/builder/CatalogueSheet.tsx +58 -35
  370. package/src/app/builder/CellPropsSheet.tsx +17 -2
  371. package/src/app/builder/DiscardDialog.tsx +59 -0
  372. package/src/app/builder/EmptyHint.tsx +16 -11
  373. package/src/app/builder/LayoutSurface.tsx +207 -67
  374. package/src/app/builder/PipelineStrip.tsx +265 -156
  375. package/src/app/builder/SaveDialog.tsx +25 -14
  376. package/src/app/builder/Toolbar.tsx +154 -57
  377. package/src/app/builder/Workspace.tsx +226 -56
  378. package/src/app/builder/builder-feedback.test.tsx +75 -0
  379. package/src/app/builder/builder-model.test.ts +5 -0
  380. package/src/app/builder/builder-model.ts +346 -6
  381. package/src/app/builder/builder-model.ux.test.ts +456 -0
  382. package/src/app/builder/builder-ux.test.tsx +844 -0
  383. package/src/app/builder/labels.ts +15 -121
  384. package/src/app/builder/reveal.ts +10 -0
  385. package/src/app/builder/status-chip.tsx +8 -4
  386. package/src/app/builder/use-builder-catalogue.ts +62 -19
  387. package/src/app/builder/use-builder-session.ts +68 -0
  388. package/src/app/builder/use-display-resolver.ts +24 -0
  389. package/src/app/builder/use-save-dashboard.ts +5 -0
  390. package/src/app/index.ts +20 -3
  391. package/src/app/layout-builder.tsx +193 -186
  392. package/src/app/mcp-app-view-chrome.test.tsx +487 -14
  393. package/src/app/mcp-app-view-chrome.tsx +328 -100
  394. package/src/app/mcp-app-view-states.tsx +140 -0
  395. package/src/app/mcp-app-view.effects.test.tsx +292 -0
  396. package/src/app/mcp-app-view.test.tsx +478 -0
  397. package/src/app/mcp-app-view.tsx +447 -190
  398. package/src/app/mcp-toolkit-app.test.tsx +40 -0
  399. package/src/app/mcp-toolkit-app.tsx +39 -8
  400. package/src/app/mcp-use-host-bridge.test.tsx +180 -0
  401. package/src/app/mcp-use-host-bridge.tsx +67 -24
  402. package/src/app/model-context.test.tsx +116 -0
  403. package/src/app/model-context.tsx +125 -0
  404. package/src/app/use-build-mode-focus.ts +62 -0
  405. package/src/app/use-view-actions.ts +89 -0
  406. package/src/app/use-view-lifecycle.ts +122 -0
  407. package/src/app/view-lifecycle.test.ts +443 -0
  408. package/src/app/view-lifecycle.ts +334 -0
  409. package/src/app/widget-fixture.effects.test.tsx +51 -0
  410. package/src/app/widget-fixture.render.test.tsx +81 -0
  411. package/src/app/widget-fixture.tsx +34 -11
  412. package/src/app/widget-props-sheet.tsx +67 -39
  413. package/src/app/widget-renderer.render.test.tsx +131 -0
  414. package/src/app/widget-renderer.tsx +136 -38
  415. package/src/components/DrillButton.tsx +24 -10
  416. package/src/components/EmptyState.tsx +83 -0
  417. package/src/components/ErrorState.tsx +204 -0
  418. package/src/components/FilterBar.test.tsx +82 -0
  419. package/src/components/FilterBar.tsx +56 -27
  420. package/src/components/GroupCard.tsx +3 -2
  421. package/src/components/KpiGrid.test.tsx +279 -0
  422. package/src/components/KpiGrid.tsx +329 -91
  423. package/src/components/ListFooter.test.tsx +116 -0
  424. package/src/components/ListFooter.tsx +110 -12
  425. package/src/components/PagedListFooter.test.tsx +140 -0
  426. package/src/components/PagedListFooter.tsx +107 -0
  427. package/src/components/Pills.test.tsx +77 -0
  428. package/src/components/Pills.tsx +78 -16
  429. package/src/components/SectionHeading.tsx +9 -3
  430. package/src/components/Toast.tsx +206 -0
  431. package/src/components/WidgetHeader.test.tsx +78 -0
  432. package/src/components/WidgetHeader.tsx +63 -16
  433. package/src/components/feedback-states.test.tsx +314 -0
  434. package/src/components/tone-figures.test.tsx +58 -0
  435. package/src/fonts.css +19 -0
  436. package/src/globals.css +9 -165
  437. package/src/hooks/index.ts +6 -3
  438. package/src/hooks/tool-result-recovery-model.test.ts +744 -0
  439. package/src/hooks/tool-result-recovery-model.ts +418 -0
  440. package/src/hooks/use-tool-query.effects.test.tsx +205 -0
  441. package/src/hooks/use-tool-query.ts +156 -33
  442. package/src/hooks/use-tool-result-recovery.effects.test.tsx +141 -0
  443. package/src/hooks/use-tool-result-recovery.test.ts +28 -5
  444. package/src/hooks/use-tool-result-recovery.ts +101 -131
  445. package/src/hooks/use-view-data.test.tsx +169 -0
  446. package/src/hooks/use-view-data.ts +131 -10
  447. package/src/hooks/use-view-tool-query.ts +25 -19
  448. package/src/i18n-coverage.test.tsx +425 -0
  449. package/src/index.ts +102 -13
  450. package/src/lib/focus-return.test.ts +288 -0
  451. package/src/lib/focus-return.ts +96 -0
  452. package/src/lib/focus-ring.ts +20 -0
  453. package/src/lib/icon-stroke.test.ts +59 -0
  454. package/src/lib/icon-stroke.ts +13 -0
  455. package/src/lib/parse-tool-result.test.ts +46 -1
  456. package/src/lib/parse-tool-result.ts +40 -2
  457. package/src/lib/toast-model.test.ts +233 -0
  458. package/src/lib/toast-model.ts +165 -0
  459. package/src/lib/tone-utils.test.ts +125 -16
  460. package/src/lib/tone-utils.ts +144 -15
  461. package/src/lib/tool-query.test.ts +569 -0
  462. package/src/lib/tool-query.ts +253 -0
  463. package/src/lib/tool-result-observer.test.ts +296 -0
  464. package/src/lib/tool-result-observer.ts +211 -0
  465. package/src/lib/typography.test.ts +45 -0
  466. package/src/lib/typography.ts +47 -0
  467. package/src/lib/utils.ts +12 -1
  468. package/src/locale/index.ts +14 -1
  469. package/src/locale/locale-provider.test.tsx +87 -0
  470. package/src/locale/locale-provider.tsx +90 -3
  471. package/src/locale/message-types.ts +337 -0
  472. package/src/locale/messages-de.ts +198 -0
  473. package/src/locale/messages-en.ts +196 -0
  474. package/src/locale/toolkit-messages.test.ts +349 -0
  475. package/src/locale/toolkit-messages.ts +143 -0
  476. package/src/no-hardcoded-copy.test.ts +432 -0
  477. package/src/primitives/alert.tsx +7 -2
  478. package/src/primitives/badge.tsx +19 -7
  479. package/src/primitives/button.tsx +13 -8
  480. package/src/primitives/card.tsx +4 -4
  481. package/src/primitives/dialog.tsx +42 -8
  482. package/src/primitives/input.tsx +5 -3
  483. package/src/primitives/scroll-area.tsx +1 -1
  484. package/src/primitives/select.tsx +7 -4
  485. package/src/primitives/sheet.tsx +33 -6
  486. package/src/primitives/skeleton.tsx +1 -1
  487. package/src/primitives/switch.tsx +4 -2
  488. package/src/primitives/tabs.tsx +9 -6
  489. package/src/providers/query-provider.test.tsx +154 -0
  490. package/src/providers/query-provider.tsx +66 -5
  491. package/src/render-cases-primitives.tsx +1 -1
  492. package/src/render-cases.tsx +82 -6
  493. package/src/render-effects.tsx +96 -0
  494. package/src/render-scan.ts +75 -0
  495. package/src/theme/cd-tokens.generated.css +48 -0
  496. package/src/theme/cd-tokens.test.ts +109 -0
  497. package/src/theme/cd-tokens.ts +67 -0
  498. package/src/theme/create-theme.test.ts +253 -5
  499. package/src/theme/create-theme.ts +341 -41
  500. package/src/theme/fonts.test.ts +85 -0
  501. package/src/theme/fonts.ts +72 -0
  502. package/src/theme/host-fonts.test.ts +135 -0
  503. package/src/theme/host-fonts.ts +52 -0
  504. package/src/theme/index.ts +15 -1
  505. package/src/theme/portal-container.ts +20 -0
  506. package/src/theme/presets.ts +44 -11
  507. package/src/theme/theme-css.test.ts +619 -0
  508. package/src/theme/theme-provider.test.tsx +82 -0
  509. package/src/theme/theme-provider.tsx +60 -24
  510. package/src/theme.css +344 -0
  511. package/ui-catalog.json +783 -49
@@ -0,0 +1,443 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import {
3
+ decodeViewResult,
4
+ DEFAULT_SLOW_HINT_MS,
5
+ describeViewForModel,
6
+ errorMessage,
7
+ reloadedAtOf,
8
+ resolveViewLifecycle,
9
+ RETRY_IDLE,
10
+ retryFocusTarget,
11
+ retryReducer,
12
+ waitKey,
13
+ withSlowHint,
14
+ type RetryState,
15
+ type ViewLifecycleInput,
16
+ } from "./view-lifecycle.js"
17
+
18
+ /** The baseline: a rendering call still open on a host that knows the tool. */
19
+ const waiting: ViewLifecycleInput = {
20
+ hasViewData: false,
21
+ callStatus: "pending",
22
+ cancelled: false,
23
+ callErrorReason: undefined,
24
+ recoveryFailed: false,
25
+ retryState: RETRY_IDLE,
26
+ slow: false,
27
+ canRetry: true,
28
+ }
29
+
30
+ const at = (over: Partial<ViewLifecycleInput>) => resolveViewLifecycle({ ...waiting, ...over })
31
+
32
+ describe("resolveViewLifecycle (toolkit#176 state machine)", () => {
33
+ it("waits while the rendering call is open, slow once the hint delay passed", () => {
34
+ expect(at({})).toEqual({ phase: "waiting", slow: false })
35
+ expect(at({ slow: true })).toEqual({ phase: "waiting", slow: true })
36
+ // A latched-ready call without a complete payload (recovery in flight) still waits.
37
+ expect(at({ callStatus: "ready" })).toEqual({ phase: "waiting", slow: false })
38
+ })
39
+
40
+ it("an isError result is an error with its reason — never an endless wait (K14)", () => {
41
+ expect(at({ callStatus: "error", callErrorReason: "[503] engine down" })).toEqual({
42
+ phase: "error",
43
+ reason: "[503] engine down",
44
+ canRetry: true,
45
+ })
46
+ })
47
+
48
+ it("a failed stripped-result recovery is an error too (N22: a terminal state)", () => {
49
+ expect(at({ recoveryFailed: true })).toEqual({
50
+ phase: "error",
51
+ reason: undefined,
52
+ canRetry: true,
53
+ })
54
+ })
55
+
56
+ it("a blocked recovery is its own error kind: the tool ran, the view must not re-run it", () => {
57
+ // A tool the host reports as writing: no retry, one click would write twice.
58
+ expect(at({ recoveryBlocked: true, toolWrites: true })).toEqual({
59
+ phase: "error",
60
+ reason: undefined,
61
+ canRetry: false,
62
+ kind: "blocked",
63
+ })
64
+ // Any other tool (a stricter policy blocked it): loading it is the user's call.
65
+ expect(at({ recoveryBlocked: true, toolWrites: false })).toEqual({
66
+ phase: "error",
67
+ reason: undefined,
68
+ canRetry: true,
69
+ kind: "blocked",
70
+ })
71
+ expect(at({ recoveryBlocked: true })).toMatchObject({ canRetry: true, kind: "blocked" })
72
+ expect(at({ recoveryBlocked: true, canRetry: false })).toMatchObject({ canRetry: false })
73
+ // toolWrites alone changes nothing: a failure keeps its retry.
74
+ expect(at({ recoveryFailed: true, toolWrites: true })).toEqual({
75
+ phase: "error",
76
+ reason: undefined,
77
+ canRetry: true,
78
+ })
79
+ expect(at({ callStatus: "error", callErrorReason: "x", toolWrites: true })).toMatchObject({
80
+ canRetry: true,
81
+ })
82
+ })
83
+
84
+ it("a block yields to a payload, a retry, a cancellation and a call error, and wins over a failure", () => {
85
+ const blocked = { recoveryBlocked: true, toolWrites: true }
86
+ expect(at({ ...blocked, hasViewData: true })).toEqual({ phase: "result" })
87
+ expect(at({ ...blocked, retryState: { status: "running", attempt: 1 } })).toEqual({
88
+ phase: "waiting",
89
+ slow: false,
90
+ })
91
+ expect(
92
+ at({ ...blocked, retryState: { status: "failed", attempt: 1, reason: "down" } }),
93
+ ).toEqual({ phase: "error", reason: "down", canRetry: true })
94
+ expect(at({ ...blocked, cancelled: true })).toEqual({ phase: "cancelled" })
95
+ expect(at({ ...blocked, callStatus: "error", callErrorReason: "x" })).toEqual({
96
+ phase: "error",
97
+ reason: "x",
98
+ canRetry: true,
99
+ })
100
+ expect(at({ ...blocked, recoveryFailed: true })).toMatchObject({ kind: "blocked" })
101
+ })
102
+
103
+ it("offers a retry only when the originating tool is known", () => {
104
+ expect(at({ callStatus: "error", canRetry: false })).toMatchObject({
105
+ phase: "error",
106
+ canRetry: false,
107
+ })
108
+ })
109
+
110
+ it("a latched non-view result nothing can recover is an error, not an endless wait", () => {
111
+ // No toolInfo: the recovery cannot re-run the tool, so the wait would never end.
112
+ expect(at({ callStatus: "ready", canRetry: false })).toEqual({
113
+ phase: "error",
114
+ reason: undefined,
115
+ canRetry: false,
116
+ })
117
+ // Still pending on such a host: the result may yet arrive.
118
+ expect(at({ callStatus: "pending", canRetry: false })).toEqual({
119
+ phase: "waiting",
120
+ slow: false,
121
+ })
122
+ // A running retry or a payload in hand still win.
123
+ expect(
124
+ at({ callStatus: "ready", canRetry: false, retryState: { status: "running", attempt: 1 } }),
125
+ ).toEqual({ phase: "waiting", slow: false })
126
+ expect(at({ callStatus: "ready", canRetry: false, hasViewData: true })).toEqual({
127
+ phase: "result",
128
+ })
129
+ })
130
+
131
+ it("a host cancellation is terminal and wins over the error status it latches as", () => {
132
+ expect(at({ cancelled: true, callStatus: "error", callErrorReason: "cancelled" })).toEqual({
133
+ phase: "cancelled",
134
+ })
135
+ })
136
+
137
+ it("a running retry waits (with its own slow flag), a failed retry shows ITS reason", () => {
138
+ const running: RetryState = { status: "running", attempt: 1 }
139
+ expect(at({ callStatus: "error", retryState: running })).toEqual({
140
+ phase: "waiting",
141
+ slow: false,
142
+ })
143
+ expect(at({ callStatus: "error", retryState: running, slow: true })).toEqual({
144
+ phase: "waiting",
145
+ slow: true,
146
+ })
147
+ const failed: RetryState = { status: "failed", attempt: 1, reason: "still down" }
148
+ expect(at({ callStatus: "error", callErrorReason: "first", retryState: failed })).toEqual({
149
+ phase: "error",
150
+ reason: "still down",
151
+ canRetry: true,
152
+ })
153
+ expect(at({ cancelled: true, retryState: failed, canRetry: false })).toEqual({
154
+ phase: "error",
155
+ reason: "still down",
156
+ canRetry: false,
157
+ })
158
+ })
159
+
160
+ it("a payload in hand always wins — the view renders", () => {
161
+ expect(at({ hasViewData: true, callStatus: "error", recoveryFailed: true })).toEqual({
162
+ phase: "result",
163
+ })
164
+ expect(at({ hasViewData: true, retryState: { status: "running", attempt: 2 } })).toEqual({
165
+ phase: "result",
166
+ })
167
+ expect(at({ hasViewData: true, cancelled: true })).toEqual({ phase: "result" })
168
+ })
169
+ })
170
+
171
+ describe("retryReducer", () => {
172
+ it("counts attempts: start runs the next one, failure keeps the count and the reason", () => {
173
+ const first = retryReducer(RETRY_IDLE, { type: "start" })
174
+ expect(first).toEqual({ status: "running", attempt: 1 })
175
+ const failed = retryReducer(first, { type: "failed", reason: "503" })
176
+ expect(failed).toEqual({ status: "failed", attempt: 1, reason: "503" })
177
+ const second = retryReducer(failed, { type: "start" })
178
+ expect(second).toEqual({ status: "running", attempt: 2 })
179
+ expect(retryReducer(second, { type: "succeeded" })).toEqual({ status: "idle", attempt: 2 })
180
+ expect(retryReducer(second, { type: "failed", reason: undefined })).toEqual({
181
+ status: "failed",
182
+ attempt: 2,
183
+ reason: undefined,
184
+ })
185
+ })
186
+ })
187
+
188
+ describe("waitKey", () => {
189
+ it("names each wait so the slow hint restarts per attempt and stops when not waiting", () => {
190
+ expect(waitKey({ phase: "waiting", slow: false }, RETRY_IDLE)).toBe("idle:0")
191
+ expect(waitKey({ phase: "waiting", slow: false }, { status: "running", attempt: 2 })).toBe(
192
+ "running:2",
193
+ )
194
+ expect(waitKey({ phase: "error", reason: undefined, canRetry: true }, RETRY_IDLE)).toBeNull()
195
+ expect(waitKey({ phase: "result" }, RETRY_IDLE)).toBeNull()
196
+ expect(waitKey({ phase: "cancelled" }, RETRY_IDLE)).toBeNull()
197
+ })
198
+
199
+ it("waits 8 s before the slow hint by default", () => {
200
+ expect(DEFAULT_SLOW_HINT_MS).toBe(8000)
201
+ })
202
+ })
203
+
204
+ describe("withSlowHint", () => {
205
+ const waiting = { phase: "waiting", slow: false } as const
206
+
207
+ it("turns the current wait slow once ITS delay passed", () => {
208
+ expect(withSlowHint(waiting, "idle:0", "idle:0")).toEqual({ phase: "waiting", slow: true })
209
+ })
210
+
211
+ it("keeps a new attempt and an unslowed wait without the hint", () => {
212
+ expect(withSlowHint(waiting, "idle:0", "running:1")).toBe(waiting)
213
+ expect(withSlowHint(waiting, null, "idle:0")).toBe(waiting)
214
+ expect(withSlowHint(waiting, null, null)).toBe(waiting)
215
+ })
216
+
217
+ it("never touches a phase that is not waiting", () => {
218
+ const error = { phase: "error", reason: "x", canRetry: true } as const
219
+ expect(withSlowHint(error, "idle:0", "idle:0")).toBe(error)
220
+ expect(withSlowHint({ phase: "result" }, "idle:0", "idle:0")).toEqual({ phase: "result" })
221
+ })
222
+ })
223
+
224
+ describe("retryFocusTarget", () => {
225
+ it("holds focus on the frame while the retry runs, then hands it on and settles", () => {
226
+ expect(retryFocusTarget("waiting")).toEqual({ target: "frame", settled: false })
227
+ expect(retryFocusTarget("error")).toEqual({ target: "retry", settled: true })
228
+ expect(retryFocusTarget("result")).toEqual({ target: "frame", settled: true })
229
+ expect(retryFocusTarget("cancelled")).toEqual({ target: "frame", settled: true })
230
+ })
231
+ })
232
+
233
+ describe("decodeViewResult", () => {
234
+ const isView = (v: unknown): v is { layout: unknown } =>
235
+ typeof v === "object" && v !== null && "layout" in v
236
+
237
+ it("returns the payload of a complete view", () => {
238
+ expect(decodeViewResult({ structuredContent: { layout: { rows: [] } } }, isView)).toEqual({
239
+ ok: true,
240
+ data: { layout: { rows: [] } },
241
+ })
242
+ })
243
+
244
+ it("types a tool error as such, with the isError text as the raw reason", () => {
245
+ expect(decodeViewResult({ isError: true, content: [{ text: "[503] down" }] }, isView)).toEqual({
246
+ ok: false,
247
+ isError: true,
248
+ reason: "[503] down",
249
+ })
250
+ // An isError result without text is still a tool error.
251
+ expect(decodeViewResult({ isError: true, content: [{ text: "" }] }, isView)).toEqual({
252
+ ok: false,
253
+ isError: true,
254
+ reason: undefined,
255
+ })
256
+ })
257
+
258
+ it("types a payload the shell cannot render as no view, without a reason", () => {
259
+ expect(decodeViewResult({ structuredContent: { other: 1 } }, isView)).toEqual({
260
+ ok: false,
261
+ isError: false,
262
+ reason: undefined,
263
+ })
264
+ })
265
+ })
266
+
267
+ describe("errorMessage", () => {
268
+ it("reads Error messages and strings, nothing else", () => {
269
+ expect(errorMessage(new Error("boom"))).toBe("boom")
270
+ expect(errorMessage(new Error(""))).toBeUndefined()
271
+ expect(errorMessage("plain")).toBe("plain")
272
+ expect(errorMessage("")).toBeUndefined()
273
+ expect(errorMessage({ message: "not an error" })).toBeUndefined()
274
+ expect(errorMessage(undefined)).toBeUndefined()
275
+ })
276
+ })
277
+
278
+ describe("describeViewForModel (the view's model context, toolkit#176 K20)", () => {
279
+ const error = (reason: string | undefined, canRetry = true) =>
280
+ ({ phase: "error", reason, canRetry }) as const
281
+
282
+ it("an error says what failed and that the user sees an error instead of the view", () => {
283
+ expect(
284
+ describeViewForModel({ lifecycle: error("[503] engine down"), toolName: "render-view" }),
285
+ ).toBe(
286
+ "The view of render-view could not be shown (reason: [503] engine down). " +
287
+ "The user sees an error message with a retry button instead of the view.",
288
+ )
289
+ expect(describeViewForModel({ lifecycle: error(undefined, false), toolName: undefined })).toBe(
290
+ "The view could not be shown. The user sees an error message instead of the view.",
291
+ )
292
+ })
293
+
294
+ it("a block says the tool ran, nothing failed, and the model must not re-run it to show the view", () => {
295
+ const blocked = (canRetry: boolean) =>
296
+ ({ phase: "error", reason: undefined, canRetry, kind: "blocked" }) as const
297
+ expect(describeViewForModel({ lifecycle: blocked(false), toolName: "start_and_show" })).toBe(
298
+ "The view of start_and_show is not shown: start_and_show ran, but the host did not pass its " +
299
+ "result to the view, and the view did not re-run it. The user sees a note to ask in the chat " +
300
+ "for the current state instead of the view. Do not re-run start_and_show just to show the view.",
301
+ )
302
+ expect(describeViewForModel({ lifecycle: blocked(true), toolName: undefined })).toBe(
303
+ "The view is not shown: the tool ran, but the host did not pass its result to the view, " +
304
+ "and the view did not re-run it. The user sees a note with a button that loads the view " +
305
+ "instead of the view. Do not re-run the tool just to show the view.",
306
+ )
307
+ })
308
+
309
+ it("a cancellation says so", () => {
310
+ expect(describeViewForModel({ lifecycle: { phase: "cancelled" }, toolName: "show_x" })).toBe(
311
+ "The view of show_x was cancelled before it rendered. The user sees a note that the call was cancelled.",
312
+ )
313
+ })
314
+
315
+ it("nothing while waiting, nor for a plain host-delivered view", () => {
316
+ expect(
317
+ describeViewForModel({ lifecycle: { phase: "waiting", slow: true }, toolName: "t" }),
318
+ ).toBeNull()
319
+ // A waiting view shows no data, so a stamp or step errors of an older payload say nothing.
320
+ expect(
321
+ describeViewForModel({
322
+ lifecycle: { phase: "waiting", slow: false },
323
+ toolName: "t",
324
+ reloadedAt: Date.UTC(2026, 9, 11),
325
+ stepErrors: [{ stepId: "kpi", reason: "timeout" }],
326
+ }),
327
+ ).toBeNull()
328
+ expect(describeViewForModel({ lifecycle: { phase: "result" }, toolName: "t" })).toBeNull()
329
+ expect(
330
+ describeViewForModel({
331
+ lifecycle: { phase: "result" },
332
+ toolName: "t",
333
+ stepErrors: [],
334
+ reloadedAt: null,
335
+ }),
336
+ ).toBeNull()
337
+ })
338
+
339
+ it("failed data steps of a shown view, with or without the alert's retry", () => {
340
+ const stepErrors = [
341
+ { stepId: "kpi", reason: "timeout" },
342
+ { stepId: "list", reason: "[404] gone" },
343
+ ]
344
+ expect(
345
+ describeViewForModel({
346
+ lifecycle: { phase: "result" },
347
+ toolName: "render-view",
348
+ stepErrors,
349
+ stepsRetryable: true,
350
+ }),
351
+ ).toBe(
352
+ "The view of render-view is shown, but some of its data could not be loaded " +
353
+ "(kpi: timeout; list: [404] gone). The user sees an alert with a retry button.",
354
+ )
355
+ expect(
356
+ describeViewForModel({ lifecycle: { phase: "result" }, toolName: undefined, stepErrors }),
357
+ ).toBe(
358
+ "The view is shown, but some of its data could not be loaded " +
359
+ "(kpi: timeout; list: [404] gone). The user sees an alert.",
360
+ )
361
+ })
362
+
363
+ it("a payload the view re-loaded itself is live as of then (N23)", () => {
364
+ const at = Date.UTC(2026, 9, 11, 14, 32, 5)
365
+ const recovered =
366
+ "The view of t re-ran the tool at 2026-10-11T14:32:05.000Z because the host did not pass " +
367
+ "the original result to the view; its figures are live as of then and can differ from the original result."
368
+ expect(
369
+ describeViewForModel({ lifecycle: { phase: "result" }, toolName: "t", reloadedAt: at }),
370
+ ).toBe(recovered)
371
+ expect(
372
+ describeViewForModel({
373
+ lifecycle: { phase: "result" },
374
+ toolName: "t",
375
+ reloadedAt: at,
376
+ reloadedBy: "recovery",
377
+ }),
378
+ ).toBe(recovered)
379
+ expect(
380
+ describeViewForModel({
381
+ lifecycle: { phase: "result" },
382
+ toolName: "t",
383
+ reloadedAt: at,
384
+ stepErrors: [{ stepId: "kpi", reason: "timeout" }],
385
+ }),
386
+ ).toMatch(
387
+ /original result\. Some of its data could not be loaded \(kpi: timeout\)\. The user sees an alert\.$/,
388
+ )
389
+ })
390
+
391
+ it("a refreshed payload says the user refreshed it, and when", () => {
392
+ expect(
393
+ describeViewForModel({
394
+ lifecycle: { phase: "result" },
395
+ toolName: "t",
396
+ reloadedAt: Date.UTC(2026, 9, 11, 15, 10),
397
+ reloadedBy: "refresh",
398
+ }),
399
+ ).toBe(
400
+ "The view of t was refreshed by the user at 2026-10-11T15:10:00.000Z; its figures are live " +
401
+ "as of then and can differ from the original result.",
402
+ )
403
+ })
404
+
405
+ it("a payload applied from the view builder says the user edited it there, and when it loaded", () => {
406
+ expect(
407
+ describeViewForModel({
408
+ lifecycle: { phase: "result" },
409
+ toolName: "show_orders_dashboard",
410
+ reloadedAt: Date.UTC(2026, 9, 11, 10, 5),
411
+ reloadedBy: "builder",
412
+ }),
413
+ ).toBe(
414
+ "The view of show_orders_dashboard was edited by the user in the view builder, which loaded " +
415
+ "its data again at 2026-10-11T10:05:00.000Z; its figures are live as of then and can " +
416
+ "differ from the original result.",
417
+ )
418
+ })
419
+ })
420
+
421
+ describe("reloadedAtOf (which payload carries the data-as-of stamp, N23)", () => {
422
+ const now = 42_000
423
+
424
+ it("a recovery, a refresh and a builder Apply always: the view loaded the figures itself", () => {
425
+ expect(reloadedAtOf("recovered", now, false)).toBe(now)
426
+ expect(reloadedAtOf("refresh", now, false)).toBe(now)
427
+ expect(reloadedAtOf("refresh", now, true)).toBe(now)
428
+ expect(reloadedAtOf("builder", now, false)).toBe(now)
429
+ expect(reloadedAtOf("builder", now, true)).toBe(now)
430
+ })
431
+
432
+ it("a retry only when the original result reached the chat", () => {
433
+ // After a blocked or failed recovery: the host answered, the chat has figures.
434
+ expect(reloadedAtOf("retry", now, false)).toBe(now)
435
+ // After a failed or cancelled call: the model never had figures.
436
+ expect(reloadedAtOf("retry", now, true)).toBeNull()
437
+ })
438
+
439
+ it("never the host's own payload", () => {
440
+ expect(reloadedAtOf("host", now, false)).toBeNull()
441
+ expect(reloadedAtOf("host", now, true)).toBeNull()
442
+ })
443
+ })