@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
@@ -1,63 +1,186 @@
1
- import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
2
- import { useCallTool } from "../providers/query-provider.js"
1
+ import { useCallback, useEffect, useRef } from "react"
2
+ import {
3
+ hashKey,
4
+ useMutation,
5
+ useQuery,
6
+ useQueryClient,
7
+ type MutationFunctionContext,
8
+ type UseMutationOptions,
9
+ type UseMutationResult,
10
+ type UseQueryResult,
11
+ } from "@tanstack/react-query"
12
+ import { useCallTool, useToolNameInQueryKey } from "../providers/query-provider.js"
3
13
  import { parseToolResult } from "../lib/parse-tool-result.js"
14
+ import {
15
+ claimToolQueryKey,
16
+ invalidateToolQueries,
17
+ NoToolTransportError,
18
+ settleEachCall,
19
+ toolKeyCollisionMessage,
20
+ toolQueryKey,
21
+ toolQueryOptions,
22
+ type PerCallCallbacks,
23
+ type UseToolQueryOptions,
24
+ } from "../lib/tool-query.js"
4
25
 
5
- export interface UseToolQueryOptions<TData = unknown, TSelected = TData> {
6
- enabled?: boolean
7
- select?: (data: TData) => TSelected
26
+ export type { UseToolQueryOptions } from "../lib/tool-query.js"
27
+
28
+ /**
29
+ * The exact cache key `useToolQuery`, `useViewToolQuery` and `useViewData`
30
+ * use for this tool and args under the nearest `AppQueryProvider`:
31
+ * `toolQueryKey` with the provider's `toolNameInQueryKey` setting. For
32
+ * `getQueryData`, `setQueryData` or `cancelQueries({ exact: true })`; prefix
33
+ * invalidation needs only the caller's `queryKey`. A new array per render.
34
+ */
35
+ export function useToolQueryKey(
36
+ queryKey: readonly unknown[],
37
+ toolName: string,
38
+ args: object,
39
+ ): unknown[] {
40
+ return toolQueryKey(queryKey, toolName, args, useToolNameInQueryKey())
41
+ }
42
+
43
+ /**
44
+ * Warns when two tools use one cache key (toolkit#176 K18): their answers
45
+ * would overwrite each other. Only the 2.x key can collide: with the tool
46
+ * name in the key (`toolNameInQueryKey`), two tools never share one.
47
+ */
48
+ export function useToolKeyGuard(
49
+ queryKey: readonly unknown[],
50
+ toolName: string,
51
+ args: object,
52
+ ): void {
53
+ const client = useQueryClient()
54
+ const keyId = hashKey(useToolQueryKey(queryKey, toolName, args))
55
+ useEffect(() => {
56
+ const previous = claimToolQueryKey(client, keyId, toolName)
57
+ if (previous !== null) console.warn(toolKeyCollisionMessage(keyId, toolName, previous))
58
+ }, [client, keyId, toolName])
59
+ }
60
+
61
+ /**
62
+ * Fetches a query again once a tool transport arrives, when it failed for
63
+ * want of one ({@link NoToolTransportError}): a `callTool` that resolves
64
+ * after the first render. TanStack refetches on a new key or `enabled`,
65
+ * never on a new fetcher, so the query would keep the error until a remount.
66
+ */
67
+ export function useRefetchOnTransport(
68
+ callTool: unknown,
69
+ query: Pick<UseQueryResult<unknown, Error>, "error" | "refetch">,
70
+ ): void {
71
+ const { error, refetch } = query
72
+ useEffect(() => {
73
+ if (callTool && error instanceof NoToolTransportError) void refetch()
74
+ }, [callTool, error, refetch])
8
75
  }
9
76
 
10
77
  /**
11
78
  * Loads data from a widget tool via TanStack Query.
12
79
  *
13
- * `args` is appended to the effective query key so two callers that share
14
- * the supplied `queryKey` but pass different `args` get separate cache
15
- * entries (otherwise the second caller silently reads stale data from the
16
- * first — the classic queryKey-vs-args trap). `select` transforms data on
17
- * each read without mutating the cache.
80
+ * The effective cache key is `[...queryKey, args]` (`toolQueryKey`): two
81
+ * callers that share `queryKey` but pass different `args` get separate
82
+ * entries. Two tools must not share a key and args; the hook warns when they
83
+ * do. Under an `AppQueryProvider` with `toolNameInQueryKey` (opt-in in 2.x,
84
+ * the default from the next major) the key is `[...queryKey, toolName,
85
+ * args]` and two tools never collide; `useToolQueryKey` returns the key in
86
+ * force. `opts` passes TanStack's query options through (`retry`,
87
+ * `retryDelay`, `staleTime`, `placeholderData` / `keepPreviousData`,
88
+ * `initialData` + `initialDataUpdatedAt`, `enabled`, `select`, …), and the
89
+ * result exposes `refetch`, `failureCount` and `failureReason`. `retry`
90
+ * defaults to the client's; `retry: shouldRetryToolCall` stops retrying a
91
+ * tool's own `isError` answer.
92
+ *
93
+ * The tool goes through `AppQueryProvider`'s `callTool`, else the nearest
94
+ * `HostBridgeProvider`'s bridge; with neither, the query fails with a
95
+ * "no tool transport" error instead of idling, and fetches once one arrives.
18
96
  */
19
97
  export function useToolQuery<TData = unknown, TSelected = TData>(
20
- queryKey: unknown[],
98
+ queryKey: readonly unknown[],
21
99
  toolName: string,
22
100
  args: object,
23
101
  opts?: UseToolQueryOptions<TData, TSelected>,
24
- ) {
102
+ ): UseQueryResult<TSelected, Error> {
25
103
  const callTool = useCallTool()
104
+ const toolNameInKey = useToolNameInQueryKey()
105
+ useToolKeyGuard(queryKey, toolName, args)
106
+ const query = useQuery<TData, Error, TSelected>(
107
+ toolQueryOptions<TData, TSelected>(queryKey, toolName, args, callTool, opts, {
108
+ toolNameInKey,
109
+ }),
110
+ )
111
+ useRefetchOnTransport(callTool, query)
112
+ return query
113
+ }
26
114
 
27
- return useQuery<TData, Error, TSelected>({
28
- queryKey: [...queryKey, args],
29
- queryFn: async () => {
30
- if (!callTool) throw new Error("callTool not available")
31
- const result = await callTool(toolName, args)
32
- return parseToolResult<TData>(result)
33
- },
34
- select: opts?.select,
35
- enabled: !!callTool && (opts?.enabled ?? true),
36
- })
115
+ /** Options of {@link useToolMutation}: TanStack's mutation options plus `invalidateKeys`. */
116
+ export type UseToolMutationOptions<TData = unknown> = Omit<
117
+ UseMutationOptions<TData, Error, Record<string, unknown>, unknown>,
118
+ "mutationFn" | "onMutate"
119
+ > & {
120
+ /** Query keys (prefixes) to invalidate after a successful call. */
121
+ invalidateKeys?: unknown[][]
37
122
  }
38
123
 
39
124
  /**
40
- * Executes a mutation via a widget tool and invalidates configured query keys.
125
+ * Executes a mutation via a widget tool and invalidates `invalidateKeys`
126
+ * after each successful call (`invalidateQueries`: active queries refetch,
127
+ * inactive ones are marked stale and refetch when they are next used).
128
+ *
129
+ * `mutate(args, { onSuccess, onError, onSettled })` fires its per-call
130
+ * callbacks for EVERY call while the component is mounted, not only for the
131
+ * latest one (TanStack's own rule), so two quick calls each get their
132
+ * confirmation. The hook-level callbacks in `opts` run first; the
133
+ * invalidation runs before the hook-level `onSuccess`.
41
134
  */
42
135
  export function useToolMutation<TData = unknown>(
43
136
  toolName: string,
44
- opts?: { invalidateKeys?: unknown[][] },
45
- ) {
137
+ opts?: UseToolMutationOptions<TData>,
138
+ ): UseMutationResult<TData, Error, Record<string, unknown>> {
46
139
  const callTool = useCallTool()
47
140
  const queryClient = useQueryClient()
141
+ const { invalidateKeys, onSuccess, ...options } = opts ?? {}
48
142
 
49
- return useMutation<TData, Error, Record<string, unknown>>({
143
+ const mutation = useMutation<TData, Error, Record<string, unknown>>({
144
+ ...options,
50
145
  mutationFn: async (args: Record<string, unknown>) => {
51
- if (!callTool) throw new Error("callTool not available")
146
+ if (!callTool) throw new NoToolTransportError(toolName)
52
147
  const result = await callTool(toolName, args)
53
148
  return parseToolResult<TData>(result)
54
149
  },
55
- onSuccess: async () => {
56
- if (opts?.invalidateKeys) {
57
- await Promise.all(
58
- opts.invalidateKeys.map((key) => queryClient.refetchQueries({ queryKey: key })),
59
- )
60
- }
150
+ onSuccess: async (data, variables, onMutateResult, context) => {
151
+ await invalidateToolQueries(queryClient, invalidateKeys)
152
+ await onSuccess?.(data, variables, onMutateResult, context)
61
153
  },
62
154
  })
155
+
156
+ // Per-call callbacks fire only while mounted (TanStack's rule, kept), with
157
+ // the latest meta/mutationKey. Both refs are written in effects and read
158
+ // only when a call settles, so `mutate` stays stable across renders.
159
+ const mounted = useRef(false)
160
+ const scope = useRef<Pick<MutationFunctionContext, "meta" | "mutationKey">>({
161
+ meta: undefined,
162
+ mutationKey: undefined,
163
+ })
164
+ useEffect(() => {
165
+ scope.current = { meta: options.meta, mutationKey: options.mutationKey }
166
+ })
167
+ useEffect(() => {
168
+ mounted.current = true
169
+ return () => {
170
+ mounted.current = false
171
+ }
172
+ }, [])
173
+ const { mutateAsync } = mutation
174
+ const mutate = useCallback(
175
+ (
176
+ variables: Record<string, unknown>,
177
+ callbacks?: PerCallCallbacks<TData, Record<string, unknown>>,
178
+ ) => {
179
+ settleEachCall(mutateAsync(variables), variables, callbacks, () =>
180
+ mounted.current ? { client: queryClient, ...scope.current } : null,
181
+ )
182
+ },
183
+ [mutateAsync, queryClient],
184
+ )
185
+ return { ...mutation, mutate }
63
186
  }
@@ -0,0 +1,141 @@
1
+ // @vitest-environment happy-dom
2
+ import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from "vitest"
3
+ import type { ToolResultObserver } from "../lib/tool-result-observer.js"
4
+ import { createToolResultObserver } from "../lib/tool-result-observer.js"
5
+ import { advanceTimers, hookProbe, inAct, renderWithEffects } from "../render-effects.js"
6
+ import {
7
+ DEFAULT_ASSUME_STRIPPED_AFTER_MS,
8
+ useToolResultRecovery,
9
+ type ToolResultRecoveryOptions,
10
+ } from "./use-tool-result-recovery.js"
11
+
12
+ /**
13
+ * The hook's wiring with its effects running (toolkit#176 K13): the default
14
+ * observer is the document's (`installToolResultObserver`), the controller
15
+ * subscribes to it on mount, and a stripped result re-runs the tool from the
16
+ * observer's notification alone, while a slow tool on a compliant host is
17
+ * never re-run. The decision itself is pinned in the recovery model's tests.
18
+ */
19
+
20
+ const installed = vi.hoisted(() => ({ observer: null as ToolResultObserver | null }))
21
+
22
+ // The document's observer, as a framed view would install it; the tests drive
23
+ // a fake host channel instead of window.parent.
24
+ vi.mock(import("../lib/tool-result-observer.js"), async (importOriginal) => {
25
+ const actual = await importOriginal()
26
+ return { ...actual, installToolResultObserver: () => installed.observer }
27
+ })
28
+
29
+ /** A host channel the observer listens on, with the SEP-1865 messages a host sends. */
30
+ function fakeHost() {
31
+ const channel = new EventTarget()
32
+ const observer = createToolResultObserver(channel)
33
+ const send = (data: unknown) =>
34
+ inAct(() => channel.dispatchEvent(new MessageEvent("message", { data })))
35
+ const notify = (method: string, params: Record<string, unknown>) =>
36
+ send({ jsonrpc: "2.0", method, params })
37
+ return {
38
+ observer,
39
+ handshake: () =>
40
+ send({
41
+ jsonrpc: "2.0",
42
+ id: 1,
43
+ result: { protocolVersion: "2026-01-26", hostCapabilities: {} },
44
+ }),
45
+ toolInput: (args: Record<string, unknown>) =>
46
+ notify("ui/notifications/tool-input", { arguments: args }),
47
+ strippedResult: () =>
48
+ notify("ui/notifications/tool-result", { content: [{ type: "text", text: "Rendered." }] }),
49
+ structuredResult: (structuredContent: unknown) =>
50
+ notify("ui/notifications/tool-result", { content: [], structuredContent }),
51
+ }
52
+ }
53
+
54
+ type CallTool = ToolResultRecoveryOptions["callTool"]
55
+
56
+ const PAYLOAD = { ok: true, rows: 3 }
57
+ const isValid = (value: unknown) => (value as { ok?: unknown } | null)?.ok === true
58
+
59
+ let callTool: Mock<CallTool>
60
+
61
+ /** Mount the hook with the view's inputs; the probe keeps its latest state. */
62
+ async function mount(over: Partial<ToolResultRecoveryOptions> = {}) {
63
+ const options: ToolResultRecoveryOptions = {
64
+ resultReady: false,
65
+ props: undefined,
66
+ isValid,
67
+ // The view runtime's input: a partial stream may have merged into it.
68
+ toolInput: { id: 7 },
69
+ hostContext: { toolInfo: { tool: { name: "show_list" } } },
70
+ callTool,
71
+ ...over,
72
+ }
73
+ const probe = hookProbe(() => useToolResultRecovery<typeof PAYLOAD>(options))
74
+ const view = await renderWithEffects(<probe.Probe />)
75
+ return { ...view, state: probe.current }
76
+ }
77
+
78
+ beforeEach(() => {
79
+ vi.useFakeTimers()
80
+ callTool = vi.fn<CallTool>(() => Promise.resolve({ structuredContent: PAYLOAD }))
81
+ })
82
+
83
+ afterEach(() => {
84
+ vi.useRealTimers()
85
+ installed.observer = null
86
+ })
87
+
88
+ describe("useToolResultRecovery with its effects (K13)", () => {
89
+ it("keep: a structured result the latch delivered is never re-run", async () => {
90
+ const host = fakeHost()
91
+ installed.observer = host.observer
92
+ await host.handshake()
93
+ await host.toolInput({ id: 7 })
94
+ await host.structuredResult(PAYLOAD)
95
+ const view = await mount({ resultReady: true, props: PAYLOAD })
96
+ await advanceTimers(vi.advanceTimersByTime, DEFAULT_ASSUME_STRIPPED_AFTER_MS * 4)
97
+ expect(callTool).not.toHaveBeenCalled()
98
+ expect(view.state().data).toBeNull()
99
+ await view.unmount()
100
+ })
101
+
102
+ it("slow: a compliant host still answering is never re-run, however long it takes", async () => {
103
+ const host = fakeHost()
104
+ installed.observer = host.observer
105
+ await host.handshake()
106
+ await host.toolInput({ id: 7 })
107
+ const view = await mount()
108
+ await advanceTimers(vi.advanceTimersByTime, DEFAULT_ASSUME_STRIPPED_AFTER_MS + 1)
109
+ await advanceTimers(vi.advanceTimersByTime, 10_000)
110
+ expect(callTool).not.toHaveBeenCalled()
111
+ expect(view.state()).toMatchObject({ data: null, failed: false, blocked: null })
112
+ await view.unmount()
113
+ })
114
+
115
+ it("strip: the observer's notification alone re-runs the tool once, with the complete input", async () => {
116
+ const host = fakeHost()
117
+ installed.observer = host.observer
118
+ await host.handshake()
119
+ const view = await mount()
120
+ await host.toolInput({ id: 7, page: 2 })
121
+ expect(callTool).not.toHaveBeenCalled()
122
+ // Nothing re-renders the view here: only the subscription can react.
123
+ await host.strippedResult()
124
+ expect(callTool).toHaveBeenCalledTimes(1)
125
+ expect(callTool).toHaveBeenCalledWith("show_list", { id: 7, page: 2 })
126
+ expect(view.state()).toMatchObject({ data: PAYLOAD, failed: false, args: { id: 7, page: 2 } })
127
+ await advanceTimers(vi.advanceTimersByTime, 10_000)
128
+ expect(callTool, "a single flight").toHaveBeenCalledTimes(1)
129
+ await view.unmount()
130
+ })
131
+
132
+ it("without an observer (observer: null) the 2.x grace timer re-runs a silent call", async () => {
133
+ const view = await mount({ observer: null })
134
+ await advanceTimers(vi.advanceTimersByTime, DEFAULT_ASSUME_STRIPPED_AFTER_MS - 1)
135
+ expect(callTool).not.toHaveBeenCalled()
136
+ await advanceTimers(vi.advanceTimersByTime, 1)
137
+ expect(callTool).toHaveBeenCalledTimes(1)
138
+ expect(callTool).toHaveBeenCalledWith("show_list", { id: 7 })
139
+ await view.unmount()
140
+ })
141
+ })
@@ -1,10 +1,13 @@
1
1
  import { describe, expect, it } from "vitest"
2
- import { decodeRecoveredResult, resolveRecoveryToolName } from "./use-tool-result-recovery.js"
2
+ import {
3
+ decodeRecoveredResult,
4
+ isRecoveryReady,
5
+ resolveRecoveryToolName,
6
+ } from "./use-tool-result-recovery.js"
3
7
 
4
- // The hook's effect wiring (single-flight, StrictMode re-run, host-payload
5
- // precedence) is exercised end-to-end by the consumer's Playwright host
6
- // simulation (structuredContent keep/strip scenarios); these tests pin the
7
- // pure decision logic.
8
+ // The hook's effect wiring (default observer, subscription, keep/strip/slow)
9
+ // runs in use-tool-result-recovery.effects.test.tsx and end-to-end in the
10
+ // consumer's Playwright host simulation; these tests pin the pure helpers.
8
11
 
9
12
  describe("resolveRecoveryToolName", () => {
10
13
  it("reads the SEP-1865 shape toolInfo.tool.name first", () => {
@@ -63,3 +66,23 @@ describe("decodeRecoveredResult", () => {
63
66
  expect(decodeRecoveredResult({ content: [] }, isView)).toBeNull()
64
67
  })
65
68
  })
69
+
70
+ describe("isRecoveryReady (toolkit#176)", () => {
71
+ const base = { resultReady: false, assumedReady: false, cancelled: false, errored: false }
72
+
73
+ it("fires once a result latched or the stripped-result grace period passed", () => {
74
+ expect(isRecoveryReady(base)).toBe(false)
75
+ expect(isRecoveryReady({ ...base, resultReady: true })).toBe(true)
76
+ expect(isRecoveryReady({ ...base, assumedReady: true })).toBe(true)
77
+ })
78
+
79
+ it("never re-executes a call that failed (K14: isError renders the error instead)", () => {
80
+ expect(isRecoveryReady({ ...base, resultReady: true, errored: true })).toBe(false)
81
+ expect(isRecoveryReady({ ...base, assumedReady: true, errored: true })).toBe(false)
82
+ })
83
+
84
+ it("never re-executes a call the host cancelled", () => {
85
+ expect(isRecoveryReady({ ...base, resultReady: true, cancelled: true })).toBe(false)
86
+ expect(isRecoveryReady({ ...base, assumedReady: true, cancelled: true })).toBe(false)
87
+ })
88
+ })