basalt-ui 1.28.0 → 1.29.1

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 (457) hide show
  1. package/AGENTS.md +12 -19
  2. package/CHANGELOG.md +23 -0
  3. package/MIGRATING.md +171 -7
  4. package/README.md +66 -105
  5. package/agent/rules/basalt-batteries.md +60 -183
  6. package/agent/rules/basalt-charts.md +56 -75
  7. package/agent/rules/basalt-controls.md +38 -69
  8. package/agent/rules/basalt-mantine.md +58 -103
  9. package/agent/rules/basalt-state.md +42 -60
  10. package/agent/rules/basalt-tokens.md +46 -67
  11. package/agent/skills/basalt-app/SKILL.md +2 -0
  12. package/agent/templates/CLAUDE-block.md.tpl +14 -14
  13. package/agent/templates/query-client.ts.tpl +1 -1
  14. package/configs/oxlint-plugin.js +84 -254
  15. package/configs/oxlint-plugin.test.ts +23 -266
  16. package/configs/oxlint.json +0 -2
  17. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  18. package/dist/agent/ai-sdk-transport.js +7 -4
  19. package/dist/agent/coalesce.js +1 -1
  20. package/dist/agent/merge.js +1 -1
  21. package/dist/agent/transport.d.ts +13 -2
  22. package/dist/agent/transport.d.ts.map +1 -1
  23. package/dist/agent/use-agent-thread-runs.d.ts +12 -1
  24. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  25. package/dist/agent/use-agent-thread-runs.js +20 -6
  26. package/dist/agent-chat/index.d.ts +1 -1
  27. package/dist/agent-chat/index.d.ts.map +1 -1
  28. package/dist/agent-chat/relative-time.js +1 -1
  29. package/dist/agent-chat/thread-detail-panel.js +1 -1
  30. package/dist/agent-chat/thread-feed-row.d.ts +32 -7
  31. package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
  32. package/dist/agent-chat/thread-feed-row.js +121 -86
  33. package/dist/agent-chat/thread-feed.d.ts +11 -1
  34. package/dist/agent-chat/thread-feed.d.ts.map +1 -1
  35. package/dist/agent-chat/thread-feed.js +10 -6
  36. package/dist/agent-chat/thread-message.js +1 -1
  37. package/dist/agent-chat/virtualize.d.ts +39 -0
  38. package/dist/agent-chat/virtualize.d.ts.map +1 -1
  39. package/dist/agent-chat/virtualize.js +15 -0
  40. package/dist/charts/index.d.ts +3 -3
  41. package/dist/charts/index.d.ts.map +1 -1
  42. package/dist/charts/index.js +8 -4
  43. package/dist/charts/kinds/BandStrip.js +1 -1
  44. package/dist/charts/kinds/Bars.d.ts +1 -3
  45. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  46. package/dist/charts/kinds/Bars.js +1 -1
  47. package/dist/charts/kinds/DualPanel.d.ts +3 -1
  48. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  49. package/dist/charts/kinds/DualPanel.js +1 -1
  50. package/dist/charts/kinds/StackedArea.js +1 -1
  51. package/dist/charts/kinds/ZonedLine.d.ts +1 -3
  52. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  53. package/dist/charts/primitives/Axes.d.ts +21 -2
  54. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  55. package/dist/charts/primitives/Axes.js +34 -2
  56. package/dist/charts/primitives/ChartCard.d.ts +20 -4
  57. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  58. package/dist/charts/primitives/ChartCard.js +62 -33
  59. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  60. package/dist/charts/primitives/ChartFrame.js +8 -1
  61. package/dist/charts/primitives/ChartPending.d.ts +12 -6
  62. package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
  63. package/dist/charts/primitives/ChartPending.js +1 -1
  64. package/dist/charts/utils/format.d.ts +5 -92
  65. package/dist/charts/utils/format.d.ts.map +1 -1
  66. package/dist/charts/utils/format.js +10 -80
  67. package/dist/cli/audit-allows.d.ts +24 -0
  68. package/dist/cli/audit-allows.d.ts.map +1 -0
  69. package/dist/cli/audit-allows.js +240 -0
  70. package/dist/cli/check-theme.d.ts +10 -0
  71. package/dist/cli/check-theme.d.ts.map +1 -0
  72. package/dist/cli/check-theme.js +139 -0
  73. package/dist/cli/doctor.d.ts +47 -0
  74. package/dist/cli/doctor.d.ts.map +1 -0
  75. package/dist/cli/doctor.js +197 -0
  76. package/dist/cli/guard-hook.d.ts +9 -0
  77. package/dist/cli/guard-hook.d.ts.map +1 -0
  78. package/dist/cli/guard-hook.js +93 -0
  79. package/dist/cli/help.d.ts +9 -0
  80. package/dist/cli/help.d.ts.map +1 -0
  81. package/dist/cli/help.js +53 -0
  82. package/dist/cli/index.d.ts +360 -270
  83. package/dist/cli/index.d.ts.map +1 -1
  84. package/dist/cli/index.js +85 -1901
  85. package/dist/cli/init.d.ts +4 -0
  86. package/dist/cli/init.d.ts.map +1 -0
  87. package/dist/cli/init.js +165 -0
  88. package/dist/cli/sync.d.ts +43 -0
  89. package/dist/cli/sync.d.ts.map +1 -0
  90. package/dist/cli/sync.js +206 -0
  91. package/dist/cli/tokens-css.d.ts +51 -0
  92. package/dist/cli/tokens-css.d.ts.map +1 -0
  93. package/dist/cli/tokens-css.js +165 -0
  94. package/dist/commands/define-commands.js +1 -1
  95. package/dist/commands/define-overlays.js +1 -1
  96. package/dist/commands/index.d.ts +2 -1
  97. package/dist/commands/index.d.ts.map +1 -1
  98. package/dist/commands/index.js +6 -0
  99. package/dist/commands/overlays-mount.d.ts +7 -9
  100. package/dist/commands/overlays-mount.d.ts.map +1 -1
  101. package/dist/commands/shell-bridge.d.ts +57 -0
  102. package/dist/commands/shell-bridge.d.ts.map +1 -0
  103. package/dist/commands/shell-bridge.js +32 -0
  104. package/dist/common/errors.d.ts +35 -0
  105. package/dist/common/errors.d.ts.map +1 -1
  106. package/dist/common/errors.js +29 -1
  107. package/dist/common/index.d.ts +2 -0
  108. package/dist/common/index.d.ts.map +1 -1
  109. package/dist/common/index.js +6 -0
  110. package/dist/common/is-dev.d.ts.map +1 -0
  111. package/dist/{motion/index.d.ts → common/motion.d.ts} +1 -1
  112. package/dist/common/motion.d.ts.map +1 -0
  113. package/dist/common/props.d.ts +1 -1
  114. package/dist/common/props.d.ts.map +1 -1
  115. package/dist/common/validate.js +1 -1
  116. package/dist/content/markdown.d.ts +3 -4
  117. package/dist/content/markdown.d.ts.map +1 -1
  118. package/dist/content/markdown.js +1 -4
  119. package/dist/controls/actions.d.ts +1 -1
  120. package/dist/controls/actions.d.ts.map +1 -1
  121. package/dist/controls/panel-row.js +1 -1
  122. package/dist/controls/range-filter.js +1 -1
  123. package/dist/dashboard/delta-badge.d.ts +18 -10
  124. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  125. package/dist/dashboard/delta-badge.js +33 -1
  126. package/dist/dashboard/empty-state.d.ts +1 -4
  127. package/dist/dashboard/empty-state.d.ts.map +1 -1
  128. package/dist/dashboard/empty-state.js +1 -2
  129. package/dist/dashboard/index.d.ts +4 -2
  130. package/dist/dashboard/index.d.ts.map +1 -1
  131. package/dist/dashboard/index.js +7 -1
  132. package/dist/dashboard/query-state.d.ts +4 -13
  133. package/dist/dashboard/query-state.d.ts.map +1 -1
  134. package/dist/dashboard/query-state.js +4 -7
  135. package/dist/{section → dashboard}/section.d.ts +2 -2
  136. package/dist/{section → dashboard}/section.d.ts.map +1 -1
  137. package/dist/{section → dashboard}/section.js +2 -2
  138. package/dist/dashboard/settings-section.d.ts +1 -1
  139. package/dist/dashboard/settings-section.d.ts.map +1 -1
  140. package/dist/dashboard/settings-section.js +1 -1
  141. package/dist/dashboard/stat-card.d.ts +1 -1
  142. package/dist/dashboard/stat-card.d.ts.map +1 -1
  143. package/dist/dashboard/stat-card.js +1 -1
  144. package/dist/dashboard/widget-header.d.ts.map +1 -0
  145. package/dist/data/data-table.js +2 -2
  146. package/dist/data/table.d.ts +1 -1
  147. package/dist/data/table.d.ts.map +1 -1
  148. package/dist/data/table.js +2 -16
  149. package/dist/data/virtual.d.ts +3 -3
  150. package/dist/dev-dock.d.ts +26 -0
  151. package/dist/dev-dock.d.ts.map +1 -0
  152. package/dist/dev-dock.js +85 -0
  153. package/dist/format/date.d.ts +62 -0
  154. package/dist/format/date.d.ts.map +1 -0
  155. package/dist/format/date.js +106 -0
  156. package/dist/format/index.d.ts +39 -0
  157. package/dist/format/index.d.ts.map +1 -0
  158. package/dist/format/index.js +54 -0
  159. package/dist/format/measure.d.ts +15 -0
  160. package/dist/format/measure.d.ts.map +1 -0
  161. package/dist/format/measure.js +16 -0
  162. package/dist/format/number.d.ts +69 -0
  163. package/dist/format/number.d.ts.map +1 -0
  164. package/dist/format/number.js +62 -0
  165. package/dist/format/shared.d.ts +27 -0
  166. package/dist/format/shared.d.ts.map +1 -0
  167. package/dist/format/shared.js +29 -0
  168. package/dist/forms/field.d.ts +4 -20
  169. package/dist/forms/field.d.ts.map +1 -1
  170. package/dist/forms/field.js +0 -4
  171. package/dist/forms/form-layout.d.ts +1 -1
  172. package/dist/forms/form-layout.d.ts.map +1 -1
  173. package/dist/forms/form-layout.js +1 -1
  174. package/dist/forms/index.d.ts +1 -1
  175. package/dist/forms/index.d.ts.map +1 -1
  176. package/dist/forms/index.js +1 -2
  177. package/dist/forms/use-form-submit.d.ts +1 -1
  178. package/dist/forms/use-form-submit.js +1 -1
  179. package/dist/guard/index.d.ts +2 -9
  180. package/dist/guard/index.d.ts.map +1 -1
  181. package/dist/guard/index.js +53 -32
  182. package/dist/guard/types.d.ts +8 -10
  183. package/dist/guard/types.d.ts.map +1 -1
  184. package/dist/index.d.ts +16 -11
  185. package/dist/index.d.ts.map +1 -1
  186. package/dist/index.js +23 -29
  187. package/dist/notifications/index.d.ts +5 -5
  188. package/dist/notifications/index.d.ts.map +1 -1
  189. package/dist/notifications/index.js +0 -2
  190. package/dist/notifications/mount-guard.d.ts.map +1 -1
  191. package/dist/notifications/mount-guard.js +2 -2
  192. package/dist/provider/connectivity-indicator.d.ts.map +1 -0
  193. package/dist/{connectivity → provider}/connectivity-provider.d.ts +1 -1
  194. package/dist/provider/connectivity-provider.d.ts.map +1 -0
  195. package/dist/{connectivity/types.d.ts → provider/connectivity-types.d.ts} +1 -1
  196. package/dist/provider/connectivity-types.d.ts.map +1 -0
  197. package/dist/provider/error-fallback.module.css +21 -0
  198. package/dist/provider/error-fallback.module.css.d.ts +9 -0
  199. package/dist/provider/index.d.ts +20 -36
  200. package/dist/provider/index.d.ts.map +1 -1
  201. package/dist/provider/index.js +42 -32
  202. package/dist/provider/lab-theme.js +1 -1
  203. package/dist/provider/use-connectivity.d.ts +3 -0
  204. package/dist/provider/use-connectivity.d.ts.map +1 -0
  205. package/dist/query-client.d.ts +67 -0
  206. package/dist/query-client.d.ts.map +1 -0
  207. package/dist/query-client.js +35 -0
  208. package/dist/{query/devtools.d.ts → query-devtools.d.ts} +2 -2
  209. package/dist/query-devtools.d.ts.map +1 -0
  210. package/dist/register.d.ts +17 -3
  211. package/dist/register.d.ts.map +1 -1
  212. package/dist/router-tanstack/index.d.ts +4 -7
  213. package/dist/router-tanstack/index.d.ts.map +1 -1
  214. package/dist/router-tanstack/index.js +1 -9
  215. package/dist/router-tanstack/nav.d.ts +1 -1
  216. package/dist/router-tanstack/nav.d.ts.map +1 -1
  217. package/dist/router-tanstack/search-store.d.ts +3 -6
  218. package/dist/router-tanstack/search-store.d.ts.map +1 -1
  219. package/dist/router-tanstack/use-nav.d.ts +1 -1
  220. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  221. package/dist/shell/app-breadcrumbs.d.ts +1 -1
  222. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  223. package/dist/shell/app-main.module.css +11 -0
  224. package/dist/shell/app-mobile-nav.d.ts +1 -1
  225. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  226. package/dist/shell/app-mobile-nav.js +24 -2
  227. package/dist/shell/app-sidebar.d.ts +1 -1
  228. package/dist/shell/app-sidebar.d.ts.map +1 -1
  229. package/dist/shell/app-sidebar.js +31 -2
  230. package/dist/shell/app-sidebar.module.css +10 -0
  231. package/dist/shell/index.d.ts +10 -4
  232. package/dist/shell/index.d.ts.map +1 -1
  233. package/dist/shell/index.js +13 -17
  234. package/dist/shell/mobile-nav-model.d.ts +1 -1
  235. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  236. package/dist/{nav/types.d.ts → shell/nav-types.d.ts} +1 -1
  237. package/dist/shell/nav-types.d.ts.map +1 -0
  238. package/dist/shell/page-aside.d.ts.map +1 -1
  239. package/dist/shell/page-aside.js +2 -29
  240. package/dist/shell/page-title.d.ts +14 -0
  241. package/dist/shell/page-title.d.ts.map +1 -0
  242. package/dist/shell/page-title.js +25 -0
  243. package/dist/shell/page-title.module.css +33 -0
  244. package/dist/shell/page-title.module.css.d.ts +11 -0
  245. package/dist/shell/sidebar-block-model.d.ts +1 -1
  246. package/dist/shell/sidebar-block-model.d.ts.map +1 -1
  247. package/dist/shell/sidebar-blocks.d.ts +1 -1
  248. package/dist/shell/sidebar-blocks.d.ts.map +1 -1
  249. package/dist/shell/use-breakpoint.d.ts +11 -0
  250. package/dist/shell/use-breakpoint.d.ts.map +1 -0
  251. package/dist/shell/use-breakpoint.js +33 -0
  252. package/dist/state/fields.d.ts +3 -19
  253. package/dist/state/fields.d.ts.map +1 -1
  254. package/dist/state/fields.js +6 -16
  255. package/dist/styles.css +36 -0
  256. package/dist/surfaces.d.ts +18 -41
  257. package/dist/surfaces.d.ts.map +1 -1
  258. package/dist/surfaces.js +48 -60
  259. package/dist/theme/index.d.ts.map +1 -1
  260. package/dist/{theme-toggle/index.d.ts → theme/theme-toggle.d.ts} +1 -1
  261. package/dist/theme/theme-toggle.d.ts.map +1 -0
  262. package/dist/{theme-toggle/index.js → theme/theme-toggle.js} +1 -1
  263. package/dist/tokens/index.d.ts +1 -0
  264. package/dist/tokens/index.d.ts.map +1 -1
  265. package/dist/tokens/index.js +10 -0
  266. package/dist/tokens/palette.d.ts +12 -0
  267. package/dist/tokens/palette.d.ts.map +1 -1
  268. package/dist/tokens/palette.js +13 -1
  269. package/dist/tokens.css +1 -0
  270. package/llms.txt +9 -24
  271. package/package.json +12 -16
  272. package/src/agent/ai-sdk-transport.ts +11 -4
  273. package/src/agent/coalesce.ts +1 -1
  274. package/src/agent/merge.ts +1 -1
  275. package/src/agent/transport.ts +15 -2
  276. package/src/agent/use-agent-thread-runs.ts +65 -15
  277. package/src/agent-chat/index.ts +6 -4
  278. package/src/agent-chat/relative-time.ts +1 -1
  279. package/src/agent-chat/thread-detail-panel.tsx +1 -1
  280. package/src/agent-chat/thread-feed-row.tsx +105 -30
  281. package/src/agent-chat/thread-feed.tsx +51 -25
  282. package/src/agent-chat/thread-message.tsx +1 -1
  283. package/src/agent-chat/virtualize.ts +48 -0
  284. package/src/charts/index.ts +7 -10
  285. package/src/charts/kinds/BandStrip.tsx +1 -1
  286. package/src/charts/kinds/Bars.tsx +2 -5
  287. package/src/charts/kinds/DualPanel.tsx +4 -3
  288. package/src/charts/kinds/StackedArea.tsx +1 -1
  289. package/src/charts/kinds/ZonedLine.tsx +1 -4
  290. package/src/charts/primitives/Axes.tsx +49 -0
  291. package/src/charts/primitives/ChartCard.tsx +49 -6
  292. package/src/charts/primitives/ChartFrame.tsx +5 -1
  293. package/src/charts/primitives/ChartPending.tsx +14 -8
  294. package/src/charts/utils/format.ts +17 -186
  295. package/src/cli/audit-allows.ts +371 -0
  296. package/src/cli/check-theme.ts +197 -0
  297. package/src/cli/doctor.ts +331 -0
  298. package/src/cli/guard-hook.ts +115 -0
  299. package/src/cli/help.ts +101 -0
  300. package/src/cli/index.ts +269 -3439
  301. package/src/cli/init.ts +247 -0
  302. package/src/cli/sync.ts +324 -0
  303. package/src/cli/tokens-css.ts +252 -0
  304. package/src/commands/define-commands.ts +1 -1
  305. package/src/commands/define-overlays.ts +1 -1
  306. package/src/commands/index.ts +10 -1
  307. package/src/commands/overlays-mount.tsx +8 -10
  308. package/src/commands/shell-bridge.ts +86 -0
  309. package/src/common/errors.ts +70 -0
  310. package/src/common/index.ts +2 -0
  311. package/src/common/props.ts +1 -1
  312. package/src/common/validate.ts +1 -1
  313. package/src/content/markdown.tsx +4 -8
  314. package/src/controls/actions.tsx +1 -1
  315. package/src/controls/panel-row.tsx +1 -1
  316. package/src/controls/range-filter.tsx +1 -1
  317. package/src/dashboard/delta-badge.tsx +74 -7
  318. package/src/dashboard/empty-state.tsx +2 -6
  319. package/src/dashboard/index.ts +11 -2
  320. package/src/dashboard/query-state.tsx +5 -18
  321. package/src/{section → dashboard}/section.tsx +4 -4
  322. package/src/dashboard/settings-section.tsx +2 -2
  323. package/src/dashboard/stat-card.tsx +2 -2
  324. package/src/data/data-table.tsx +3 -3
  325. package/src/data/table.ts +6 -25
  326. package/src/data/virtual-list.tsx +1 -1
  327. package/src/data/virtual.ts +3 -3
  328. package/src/dev-dock.tsx +162 -0
  329. package/src/format/date.ts +184 -0
  330. package/src/format/index.ts +69 -0
  331. package/src/format/measure.ts +31 -0
  332. package/src/format/number.ts +129 -0
  333. package/src/format/shared.ts +63 -0
  334. package/src/forms/field.ts +4 -24
  335. package/src/forms/form-layout.tsx +2 -2
  336. package/src/forms/index.ts +1 -2
  337. package/src/forms/use-form-submit.ts +2 -2
  338. package/src/guard/index.ts +80 -69
  339. package/src/guard/types.ts +7 -10
  340. package/src/index.ts +38 -29
  341. package/src/notifications/index.ts +5 -7
  342. package/src/notifications/mount-guard.ts +7 -9
  343. package/src/{connectivity → provider}/connectivity-indicator.tsx +1 -1
  344. package/src/{connectivity → provider}/connectivity-provider.tsx +1 -1
  345. package/src/provider/error-fallback.module.css +21 -0
  346. package/src/provider/error-fallback.module.css.d.ts +9 -0
  347. package/src/provider/index.tsx +71 -71
  348. package/src/provider/lab-theme.ts +1 -1
  349. package/src/{connectivity → provider}/use-connectivity.ts +3 -3
  350. package/src/query-client.ts +105 -0
  351. package/src/{query/devtools.tsx → query-devtools.tsx} +5 -4
  352. package/src/register.ts +20 -4
  353. package/src/router-tanstack/index.ts +4 -15
  354. package/src/router-tanstack/nav.ts +1 -1
  355. package/src/router-tanstack/search-store.ts +3 -4
  356. package/src/router-tanstack/use-nav.ts +1 -1
  357. package/src/shell/app-breadcrumbs.tsx +1 -1
  358. package/src/shell/app-main.module.css +11 -0
  359. package/src/shell/app-mobile-nav.tsx +28 -1
  360. package/src/shell/app-sidebar.module.css +10 -0
  361. package/src/shell/app-sidebar.tsx +31 -2
  362. package/src/shell/index.tsx +23 -28
  363. package/src/shell/mobile-nav-model.ts +1 -1
  364. package/src/shell/page-aside.tsx +2 -52
  365. package/src/shell/page-title.module.css +33 -0
  366. package/src/shell/page-title.module.css.d.ts +11 -0
  367. package/src/shell/page-title.tsx +57 -0
  368. package/src/shell/sidebar-block-model.ts +1 -1
  369. package/src/shell/sidebar-blocks.tsx +1 -1
  370. package/src/shell/use-breakpoint.ts +73 -0
  371. package/src/state/fields.ts +7 -43
  372. package/src/styles.css +36 -0
  373. package/src/surfaces.ts +47 -61
  374. package/src/theme/index.ts +7 -0
  375. package/src/{theme-toggle/index.tsx → theme/theme-toggle.tsx} +1 -1
  376. package/src/tokens/index.ts +10 -0
  377. package/src/tokens/palette.ts +12 -0
  378. package/dist/connectivity/connectivity-indicator.d.ts.map +0 -1
  379. package/dist/connectivity/connectivity-provider.d.ts.map +0 -1
  380. package/dist/connectivity/index.d.ts +0 -14
  381. package/dist/connectivity/index.d.ts.map +0 -1
  382. package/dist/connectivity/index.js +0 -8
  383. package/dist/connectivity/types.d.ts.map +0 -1
  384. package/dist/connectivity/use-connectivity.d.ts +0 -3
  385. package/dist/connectivity/use-connectivity.d.ts.map +0 -1
  386. package/dist/data/index.d.ts +0 -38
  387. package/dist/data/index.d.ts.map +0 -1
  388. package/dist/data/index.js +0 -24
  389. package/dist/motion/index.d.ts.map +0 -1
  390. package/dist/nav/types.d.ts.map +0 -1
  391. package/dist/notifications/overlay.d.ts +0 -20
  392. package/dist/notifications/overlay.d.ts.map +0 -1
  393. package/dist/notifications/overlay.js +0 -14
  394. package/dist/query/devtools.d.ts.map +0 -1
  395. package/dist/query/error-message.d.ts +0 -41
  396. package/dist/query/error-message.d.ts.map +0 -1
  397. package/dist/query/error-message.js +0 -30
  398. package/dist/query/index.d.ts +0 -47
  399. package/dist/query/index.d.ts.map +0 -1
  400. package/dist/query/index.js +0 -56
  401. package/dist/router-tanstack/field.d.ts +0 -14
  402. package/dist/router-tanstack/field.d.ts.map +0 -1
  403. package/dist/router-tanstack/field.js +0 -4
  404. package/dist/router-tanstack/multi-search-param-store.d.ts +0 -43
  405. package/dist/router-tanstack/multi-search-param-store.d.ts.map +0 -1
  406. package/dist/router-tanstack/multi-search-param-store.js +0 -30
  407. package/dist/router-tanstack/search-param-store.d.ts +0 -42
  408. package/dist/router-tanstack/search-param-store.d.ts.map +0 -1
  409. package/dist/router-tanstack/search-param-store.js +0 -25
  410. package/dist/section/index.d.ts +0 -2
  411. package/dist/section/index.d.ts.map +0 -1
  412. package/dist/section/index.js +0 -4
  413. package/dist/theme-toggle/index.d.ts.map +0 -1
  414. package/dist/utils/is-dev.d.ts.map +0 -1
  415. package/dist/widget-header/delta-badge.d.ts +0 -19
  416. package/dist/widget-header/delta-badge.d.ts.map +0 -1
  417. package/dist/widget-header/delta-badge.js +0 -36
  418. package/dist/widget-header/index.d.ts +0 -3
  419. package/dist/widget-header/index.d.ts.map +0 -1
  420. package/dist/widget-header/index.js +0 -8
  421. package/dist/widget-header/widget-header.d.ts.map +0 -1
  422. package/src/connectivity/index.ts +0 -19
  423. package/src/data/index.ts +0 -69
  424. package/src/notifications/overlay.tsx +0 -66
  425. package/src/query/error-message.ts +0 -70
  426. package/src/query/index.ts +0 -97
  427. package/src/router-tanstack/field.ts +0 -36
  428. package/src/router-tanstack/multi-search-param-store.ts +0 -106
  429. package/src/router-tanstack/search-param-store.ts +0 -100
  430. package/src/section/index.ts +0 -1
  431. package/src/widget-header/delta-badge.tsx +0 -77
  432. package/src/widget-header/index.ts +0 -10
  433. /package/dist/{utils → common}/is-dev.d.ts +0 -0
  434. /package/dist/{utils → common}/is-dev.js +0 -0
  435. /package/dist/{motion/index.js → common/motion.js} +0 -0
  436. /package/dist/{section → dashboard}/section.module.css +0 -0
  437. /package/dist/{section → dashboard}/section.module.css.d.ts +0 -0
  438. /package/dist/{widget-header → dashboard}/widget-header.d.ts +0 -0
  439. /package/dist/{widget-header → dashboard}/widget-header.js +0 -0
  440. /package/dist/{widget-header → dashboard}/widget-header.module.css +0 -0
  441. /package/dist/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
  442. /package/dist/{connectivity → provider}/connectivity-indicator.d.ts +0 -0
  443. /package/dist/{connectivity → provider}/connectivity-indicator.js +0 -0
  444. /package/dist/{connectivity → provider}/connectivity-provider.js +0 -0
  445. /package/dist/{connectivity/types.js → provider/connectivity-types.js} +0 -0
  446. /package/dist/{connectivity → provider}/use-connectivity.js +0 -0
  447. /package/dist/{query/devtools.js → query-devtools.js} +0 -0
  448. /package/dist/{nav/types.js → shell/nav-types.js} +0 -0
  449. /package/src/{utils → common}/is-dev.ts +0 -0
  450. /package/src/{motion/index.ts → common/motion.ts} +0 -0
  451. /package/src/{section → dashboard}/section.module.css +0 -0
  452. /package/src/{section → dashboard}/section.module.css.d.ts +0 -0
  453. /package/src/{widget-header → dashboard}/widget-header.module.css +0 -0
  454. /package/src/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
  455. /package/src/{widget-header → dashboard}/widget-header.tsx +0 -0
  456. /package/src/{connectivity/types.ts → provider/connectivity-types.ts} +0 -0
  457. /package/src/{nav/types.ts → shell/nav-types.ts} +0 -0
@@ -18,6 +18,7 @@ import basaltPlugin, {
18
18
  KNOWN_RULE_IDS,
19
19
  PLUGIN_RULE_ADVISORY,
20
20
  PLUGIN_RULE_GRACE,
21
+ RETIRED_RULE_IDS,
21
22
  } from './oxlint-plugin.js'
22
23
  import { CTL_THEME } from '../src/theme/ctl-theme'
23
24
  import { GUARD_RULES, PLUGIN_RULE_IDS } from '../src/guard/index.ts'
@@ -37,8 +38,6 @@ beforeEach(() => {
37
38
  jsPlugins: [PLUGIN_PATH],
38
39
  rules: {
39
40
  'basalt/provider-above-router': 'error',
40
- 'basalt/duplicate-notifications-mount': 'error',
41
- 'basalt/query-dual-import': 'error',
42
41
  'basalt/query-fn-unwrap': 'error',
43
42
  'basalt/deprecated-export': 'error',
44
43
  'basalt/forms-field-key': 'error',
@@ -1511,9 +1510,15 @@ describe('KNOWN_RULE_IDS', () => {
1511
1510
  // Deduped, because ONE id is deliberately in both registries: `in-body-page-title` is a plugin
1512
1511
  // rule AND a guard kind (law C8 has an AST half and a text half), so one `theme-allow
1513
1512
  // in-body-page-title — <why>` waives both lanes rather than needing a different word per lane.
1514
- it('contains nothing else — it is exactly the union of the two registries', () => {
1513
+ // `RETIRED_RULE_IDS` adds a third component: an id that used to be a guard kind or a plugin
1514
+ // rule, is in neither live registry, and must still parse as a (dead) waiver rather than a typo.
1515
+ it('contains nothing else — it is exactly the union of the two registries plus retired ids', () => {
1515
1516
  const expected = [
1516
- ...new Set([...Object.keys(basaltPlugin.rules), ...Object.keys(GUARD_RULES)]),
1517
+ ...new Set([
1518
+ ...Object.keys(basaltPlugin.rules),
1519
+ ...Object.keys(GUARD_RULES),
1520
+ ...RETIRED_RULE_IDS,
1521
+ ]),
1517
1522
  ].toSorted()
1518
1523
  expect([...KNOWN_RULE_IDS].toSorted()).toEqual(expected)
1519
1524
  })
@@ -1531,6 +1536,15 @@ describe('KNOWN_RULE_IDS', () => {
1531
1536
  expect([id, PLUGIN_RULE_IDS.has(id) || Object.hasOwn(GUARD_RULES, id)]).toEqual([id, true])
1532
1537
  }
1533
1538
  })
1539
+
1540
+ // A retired id has to be classifiable too, on both sides — `RETIRED_RULE_IDS` is this plugin's
1541
+ // copy, `PLUGIN_RULE_IDS` (src/guard) is the guard's. Drift between them would mean
1542
+ // `--audit-allows` classifies a retired id one way from `check-theme` and another from oxlint.
1543
+ it('every retired id is in the guard’s PLUGIN_RULE_IDS too', () => {
1544
+ for (const id of RETIRED_RULE_IDS) {
1545
+ expect([id, PLUGIN_RULE_IDS.has(id)]).toEqual([id, true])
1546
+ }
1547
+ })
1534
1548
  })
1535
1549
 
1536
1550
  // ── theme-allow fails closed on a typo ─────────────────────────────────────────────────────────
@@ -3035,194 +3049,9 @@ describe('basalt/provider-above-router', () => {
3035
3049
  })
3036
3050
  })
3037
3051
 
3038
- // ── duplicate-notifications-mount (F5) ───────────────────────────────────────
3039
-
3040
- describe('basalt/duplicate-notifications-mount', () => {
3041
- const IMPORTS =
3042
- `import { BasaltOverlays } from 'basalt-ui/commands'\n` +
3043
- `import { BasaltNotifications } from 'basalt-ui/notifications'\n`
3044
-
3045
- it('flags both mounts in one file', () => {
3046
- const { code, rules } = run(
3047
- `${IMPORTS}export const App = () => (\n <BasaltOverlays notifications>\n` +
3048
- ` <BasaltNotifications />\n </BasaltOverlays>\n)\n`,
3049
- )
3050
- expect(code).toBe(1)
3051
- expect(rules).toContain('duplicate-notifications-mount')
3052
- })
3053
-
3054
- // `notifications` defaults to TRUE, so the attribute being absent is the same defect.
3055
- it('flags when BasaltOverlays writes no notifications prop at all', () => {
3056
- const { code, rules } = run(
3057
- `${IMPORTS}export const App = () => (\n <BasaltOverlays>\n` +
3058
- ` <BasaltNotifications />\n </BasaltOverlays>\n)\n`,
3059
- )
3060
- expect(code).toBe(1)
3061
- expect(rules).toContain('duplicate-notifications-mount')
3062
- })
3063
-
3064
- it('does NOT flag when BasaltOverlays disables its notifications layer', () => {
3065
- const { code, rules } = run(
3066
- `${IMPORTS}export const App = () => (\n <BasaltOverlays notifications={false}>\n` +
3067
- ` <BasaltNotifications />\n </BasaltOverlays>\n)\n`,
3068
- )
3069
- expect(code).toBe(0)
3070
- expect(rules).not.toContain('duplicate-notifications-mount')
3071
- })
3072
-
3073
- it('does NOT flag a standalone BasaltNotifications on its own', () => {
3074
- const { code, rules } = run(
3075
- `import { BasaltNotifications } from 'basalt-ui/notifications'\n` +
3076
- `export const App = () => <BasaltNotifications />\n`,
3077
- )
3078
- expect(code).toBe(0)
3079
- expect(rules).not.toContain('duplicate-notifications-mount')
3080
- })
3081
-
3082
- it("does NOT flag a consumer's own components of the same names", () => {
3083
- const { code, rules } = run(
3084
- `import { BasaltOverlays } from './overlays'\n` +
3085
- `import { BasaltNotifications } from './notifications'\n` +
3086
- `export const App = () => (\n <BasaltOverlays>\n <BasaltNotifications />\n` +
3087
- ` </BasaltOverlays>\n)\n`,
3088
- )
3089
- expect(code).toBe(0)
3090
- expect(rules).not.toContain('duplicate-notifications-mount')
3091
- })
3092
-
3093
- it('honours a theme-allow naming the rule', () => {
3094
- const { code, rules } = run(
3095
- `${IMPORTS}export const App = () => (\n <BasaltOverlays notifications>\n` +
3096
- ` {/* theme-allow duplicate-notifications-mount — two roots, never both mounted */}\n` +
3097
- ` <BasaltNotifications />\n </BasaltOverlays>\n)\n`,
3098
- )
3099
- expect(code).toBe(0)
3100
- expect(rules).not.toContain('duplicate-notifications-mount')
3101
- })
3102
-
3103
- // Two mounts WRITTEN, one mount RENDERED — the ternary is the shape a consumer reaches for while
3104
- // migrating from the standalone layer to the composed one, and it was never a double mount.
3105
- it('does NOT flag the two branches of one ternary', () => {
3106
- const { code, rules } = run(
3107
- `${IMPORTS}export const App = ({ composed }) => (\n` +
3108
- ` <div>{composed ? <BasaltOverlays /> : <BasaltNotifications />}</div>\n)\n`,
3109
- )
3110
- expect(code).toBe(0)
3111
- expect(rules).not.toContain('duplicate-notifications-mount')
3112
- })
3113
-
3114
- it('does NOT flag the two operands of one logical expression', () => {
3115
- const { code, rules } = run(
3116
- `${IMPORTS}export const App = ({ composed }) => (\n` +
3117
- ` <div>{(composed && <BasaltOverlays />) || <BasaltNotifications />}</div>\n)\n`,
3118
- )
3119
- expect(code).toBe(0)
3120
- expect(rules).not.toContain('duplicate-notifications-mount')
3121
- })
3122
-
3123
- // The exemption is the NEAREST common ancestor only. Two separate `&&` guards can both hold, so
3124
- // this stays a finding — the pair is conditional, not exclusive.
3125
- it('DOES flag two independently-guarded mounts under one parent', () => {
3126
- const { code, rules } = run(
3127
- `${IMPORTS}export const App = ({ a, b }) => (\n` +
3128
- ` <div>{a && <BasaltOverlays />}{b && <BasaltNotifications />}</div>\n)\n`,
3129
- )
3130
- expect(code).toBe(1)
3131
- expect(rules).toContain('duplicate-notifications-mount')
3132
- })
3133
-
3134
- // One exclusive pair does not excuse a second, unconditional overlays mount.
3135
- it('DOES flag when a NON-exclusive overlays mount also exists', () => {
3136
- const { code, rules } = run(
3137
- `${IMPORTS}export const App = ({ composed }) => (\n` +
3138
- ` <BasaltOverlays>\n` +
3139
- ` {composed ? <BasaltOverlays /> : <BasaltNotifications />}\n` +
3140
- ` </BasaltOverlays>\n)\n`,
3141
- )
3142
- expect(code).toBe(1)
3143
- expect(rules).toContain('duplicate-notifications-mount')
3144
- })
3145
- })
3146
-
3147
- // ── query-dual-import (F5) ───────────────────────────────────────────────────
3148
-
3149
- describe('basalt/query-dual-import', () => {
3150
- it('flags a file importing from both @tanstack/react-query and basalt-ui/query', () => {
3151
- const { code, rules, output } = run(
3152
- `import { useQuery } from '@tanstack/react-query'\n` +
3153
- `import { unwrap } from 'basalt-ui/query'\n` +
3154
- `export const useThing = () => useQuery({ queryKey: ['t'], queryFn: () => unwrap(get()) })\n`,
3155
- )
3156
- expect(code).toBe(1)
3157
- expect(rules).toContain('query-dual-import')
3158
- expect(output).toContain('Dual query import')
3159
- })
3160
-
3161
- // The softer half — the one that actually catches the drift, and why the rule is `warn`.
3162
- it('flags a raw @tanstack/react-query import beside a basalt root import', () => {
3163
- const { code, rules, output } = run(
3164
- `import { useQuery } from '@tanstack/react-query'\n` +
3165
- `import { QueryState } from 'basalt-ui'\n` +
3166
- `export const useThing = () => useQuery({ queryKey: ['t'], queryFn: get })\n`,
3167
- )
3168
- expect(code).toBe(1)
3169
- expect(rules).toContain('query-dual-import')
3170
- expect(output).toContain('Raw @tanstack/react-query import')
3171
- })
3172
-
3173
- it('does NOT flag basalt-ui/query on its own', () => {
3174
- const { code, rules } = run(
3175
- `import { useQuery, unwrap } from 'basalt-ui/query'\n` +
3176
- `export const useThing = () => useQuery({ queryKey: ['t'], queryFn: () => unwrap(get()) })\n`,
3177
- )
3178
- expect(code).toBe(0)
3179
- expect(rules).not.toContain('query-dual-import')
3180
- })
3181
-
3182
- // `import type` is erased at compile time — there is no runtime import to route through the
3183
- // seam, and typing against the library's own result type is what `QueryStateLike` documents as
3184
- // legitimate. Same skip `agent-no-raw-usechat` and `ai-sdk-major` take.
3185
- it('does NOT flag a type-only @tanstack/react-query import beside basalt-ui/query', () => {
3186
- const { code, rules } = run(
3187
- `import type { UseQueryResult } from '@tanstack/react-query'\n` +
3188
- `import { unwrap } from 'basalt-ui/query'\n` +
3189
- `export const pick = (q: UseQueryResult<number>) => unwrap(q.data)\n`,
3190
- )
3191
- expect(code).toBe(0)
3192
- expect(rules).not.toContain('query-dual-import')
3193
- })
3194
-
3195
- it('does NOT flag @tanstack/react-query in a file with no basalt import', () => {
3196
- const { code, rules } = run(
3197
- `import { useQuery } from '@tanstack/react-query'\n` +
3198
- `export const useThing = () => useQuery({ queryKey: ['t'], queryFn: get })\n`,
3199
- )
3200
- expect(code).toBe(0)
3201
- expect(rules).not.toContain('query-dual-import')
3202
- })
3203
-
3204
- // The devtools package is a different package, not a subpath.
3205
- it('does NOT flag @tanstack/react-query-devtools', () => {
3206
- const { code, rules } = run(
3207
- `import { ReactQueryDevtools } from '@tanstack/react-query-devtools'\n` +
3208
- `import { unwrap } from 'basalt-ui/query'\n` +
3209
- `export const D = () => <ReactQueryDevtools />\n`,
3210
- )
3211
- expect(code).toBe(0)
3212
- expect(rules).not.toContain('query-dual-import')
3213
- })
3214
-
3215
- it('honours a theme-allow naming the rule', () => {
3216
- const { code, rules } = run(
3217
- `// theme-allow query-dual-import — the persister API has no basalt re-export yet\n` +
3218
- `import { useQuery } from '@tanstack/react-query'\n` +
3219
- `import { unwrap } from 'basalt-ui/query'\n` +
3220
- `export const useThing = () => useQuery({ queryKey: ['t'], queryFn: () => unwrap(g()) })\n`,
3221
- )
3222
- expect(code).toBe(0)
3223
- expect(rules).not.toContain('query-dual-import')
3224
- })
3225
- })
3052
+ // `basalt/query-dual-import` is RETIRED (`RETIRED_RULE_IDS`) — its own describe block was removed
3053
+ // along with the rule; see `KNOWN_RULE_IDS` below for the coverage that remains: it is still a
3054
+ // valid (dead) `theme-allow` id, never a typo.
3226
3055
 
3227
3056
  // ── query-fn-unwrap (F5) ─────────────────────────────────────────────────────
3228
3057
 
@@ -3295,80 +3124,8 @@ describe('basalt/query-fn-unwrap', () => {
3295
3124
 
3296
3125
  // ── deprecated-export (B4) ───────────────────────────────────────────────────
3297
3126
 
3298
- describe('basalt/deprecated-export', () => {
3299
- it('flags a deprecated named import', () => {
3300
- const { code, rules, output } = run(
3301
- `import { field } from 'basalt-ui/forms'\nexport const f = field\n`,
3302
- )
3303
- expect(code).toBe(1)
3304
- expect(rules).toContain('deprecated-export')
3305
- expect(output).toContain('inputProps')
3306
- })
3307
-
3308
- it('does NOT flag the replacement', () => {
3309
- const { code, rules } = run(
3310
- `import { inputProps } from 'basalt-ui/forms'\nexport const f = inputProps\n`,
3311
- )
3312
- expect(code).toBe(0)
3313
- expect(rules).not.toContain('deprecated-export')
3314
- })
3315
-
3316
- it('does NOT flag a same-named import from somewhere else', () => {
3317
- const { code, rules } = run(`import { field } from 'basalt-ui/state'\nexport const f = field\n`)
3318
- expect(code).toBe(0)
3319
- expect(rules).not.toContain('deprecated-export')
3320
- })
3321
-
3322
- it('flags each deprecated BasaltProvider connectivity prop', () => {
3323
- const { code, rules, output } = run(
3324
- `import { BasaltProvider } from 'basalt-ui'\n` +
3325
- `export const App = () => (\n` +
3326
- ` <BasaltProvider sseUrl="/e" healthUrl="/h" healthIntervalMs={5000}>{null}</BasaltProvider>\n)\n`,
3327
- )
3328
- expect(code).toBe(1)
3329
- expect(rules).toContain('deprecated-export')
3330
- expect(output).toContain('connectivity')
3331
- })
3332
-
3333
- it('does NOT flag the connectivity object prop', () => {
3334
- const { code, rules } = run(
3335
- `import { BasaltProvider } from 'basalt-ui'\n` +
3336
- `export const App = () => <BasaltProvider connectivity={{ sseUrl: '/e' }}>{null}</BasaltProvider>\n`,
3337
- )
3338
- expect(code).toBe(0)
3339
- expect(rules).not.toContain('deprecated-export')
3340
- })
3341
-
3342
- it("does NOT flag an sseUrl on a consumer's own component", () => {
3343
- const { code, rules } = run(
3344
- `import { BasaltProvider } from './provider'\n` +
3345
- `export const App = () => <BasaltProvider sseUrl="/e">{null}</BasaltProvider>\n`,
3346
- )
3347
- expect(code).toBe(0)
3348
- expect(rules).not.toContain('deprecated-export')
3349
- })
3350
-
3351
- it('honours a theme-allow naming the rule', () => {
3352
- const { code, rules } = run(
3353
- `// theme-allow deprecated-export — pinned to 1.27.0 until the forms port lands\n` +
3354
- `import { field } from 'basalt-ui/forms'\nexport const f = field\n`,
3355
- )
3356
- expect(code).toBe(0)
3357
- expect(rules).not.toContain('deprecated-export')
3358
- })
3359
-
3360
- // `field` → `inputProps` is NOT a drop-in rename any more (`inputProps` returns no `key`), so
3361
- // the row carries `fix: false`: the diagnostic still fires, but `--fix` must leave the import
3362
- // alone — the rewrite belongs to `basalt/forms-field-key`, which also inserts the key.
3363
- it('leaves a row with `fix: false` untouched under --fix while still reporting it', () => {
3364
- const source = `import { field } from 'basalt-ui/forms'\nexport const f = field\n`
3365
- writeFileSync(resolve(dir, 'fixture.tsx'), source)
3366
- Bun.spawnSync([OXLINT_BIN, '-c', '.oxlintrc.json', '--fix', 'fixture.tsx'], { cwd: dir })
3367
- expect(readFileSync(resolve(dir, 'fixture.tsx'), 'utf8')).toBe(source)
3368
- const { rules } = run(source)
3369
- expect(rules).toContain('deprecated-export')
3370
- })
3371
- })
3127
+ // deprecated-export's behaviour tests were removed with the last ledger row in 1.29.0; the
3128
+ // ledger-shape and barrel-scan assertions below keep the mechanism honest until the next row.
3372
3129
 
3373
3130
  // ── forms-field-key ──────────────────────────────────────────────────────────
3374
3131
 
@@ -42,8 +42,6 @@
42
42
  "basalt/search-literal-link": "error",
43
43
  "basalt/use-search-from-literal": "error",
44
44
  "basalt/provider-above-router": "warn",
45
- "basalt/duplicate-notifications-mount": "warn",
46
- "basalt/query-dual-import": "warn",
47
45
  "basalt/query-fn-unwrap": "warn",
48
46
  "basalt/deprecated-export": "warn",
49
47
  "basalt/forms-field-key": "warn",
@@ -1 +1 @@
1
- {"version":3,"file":"ai-sdk-transport.d.ts","sourceRoot":"","sources":["../../src/agent/ai-sdk-transport.ts"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,SAAS,CAAA;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAmB1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,kFAAkF;IAClF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAA;IACnD,oFAAoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAA;IACzC;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CACzC,CAAA;AAID,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,IAAI,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG;IACvF;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;CAC/E,CAAA;AAwRD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAG,SAAS,EAC9C,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAAC,KAAK,CAAC,CAkDvB"}
1
+ {"version":3,"file":"ai-sdk-transport.d.ts","sourceRoot":"","sources":["../../src/agent/ai-sdk-transport.ts"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,SAAS,CAAA;AACxD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAmB1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,kFAAkF;IAClF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAA;IACnD,oFAAoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAA;IACzC;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CACzC,CAAA;AAID,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,IAAI,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG;IACvF;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;CAC/E,CAAA;AAwRD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAG,SAAS,EAC9C,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAAC,KAAK,CAAC,CAyDvB"}
@@ -158,14 +158,17 @@ function aiSdkTransport(options) {
158
158
  // diffPart), so a replayed run rewrites in place instead of duplicating — the literal
159
159
  // condition this assertion stands for.
160
160
  idempotentReplay: true,
161
- async *stream(input, signal) {
161
+ async *stream(input, signal, ctx) {
162
162
  yield { id: `${chatId}#start`, type: "start", runId: chatId, resumeToken: chatId };
163
163
  const { httpTransport, readUIMessageStream: readStream } = await resolveAiSdk();
164
164
  const userMessage = {
165
165
  // This id is what AI SDK's own backend persistence/dedup keys turns hang off — the same
166
- // idempotency-key cost as basalt's own ChatMessage.id (see mintMessageId's doc), so
167
- // mintMessageId, not mintThreadId, is the right helper here.
168
- id: mintMessageId(),
166
+ // idempotency-key cost as basalt's own ChatMessage.id (see mintMessageId's doc). Prefer
167
+ // the caller's OWN idempotency key (ctx.messageId — the id useAgentThreadRuns.start()
168
+ // already minted for this turn's ChatMessage) so the two never diverge for one turn; only
169
+ // a caller that omits ctx (useAgentStream, a hand-rolled stream() call) falls back to
170
+ // minting a fresh one here.
171
+ id: ctx?.messageId ?? mintMessageId(),
169
172
  role: "user",
170
173
  parts: [{ type: "text", text: input }]
171
174
  };
@@ -1,4 +1,4 @@
1
- import { isDev } from "../utils/is-dev.js";
1
+ import { isDev } from "../common/is-dev.js";
2
2
  function isTextLikeType(type) {
3
3
  return type === "text" || type === "reasoning";
4
4
  }
@@ -1,4 +1,4 @@
1
- import { isDev } from "../utils/is-dev.js";
1
+ import { isDev } from "../common/is-dev.js";
2
2
  function isTextLikeType(type) {
3
3
  return type === "text" || type === "reasoning";
4
4
  }
@@ -31,7 +31,7 @@
31
31
  */
32
32
  import type { AgentPart, AgentPartDraft } from './parts.js';
33
33
  /**
34
- * The injected transport seam. `stream(input, signal)` returns an async generator of parts.
34
+ * The injected transport seam. `stream(input, signal, ctx)` returns an async generator of parts.
35
35
  * Implement this interface for any backend — Eden, fetch-based SSE, AI SDK, or a mock.
36
36
  *
37
37
  * The default `TPart` is `AgentPartDraft` (not `AgentPart`): transports yield DRAFTS — `id`
@@ -39,6 +39,15 @@ import type { AgentPart, AgentPartDraft } from './parts.js';
39
39
  * part before it is ever accumulated. Pass an explicit `AgentPart` generic when a transport mints
40
40
  * its own ids directly (as `aiSdkTransport` does).
41
41
  *
42
+ * `ctx.messageId`, when present, is the id `useAgentThreadRuns.start()` already minted for this
43
+ * turn's user `ChatMessage` — the SAME idempotency key `appendMessage`/the backend's dedup keys off
44
+ * of (see `mintMessageId`'s doc). A transport that mints its OWN wire-level message id independent
45
+ * of that (as `aiSdkTransport` used to) forces the consumer to reconcile two ids for one turn; a
46
+ * transport MAY use `ctx.messageId` as its outbound message id instead of minting a fresh one. The
47
+ * param is optional and appended last so an existing two-arg transport keeps compiling unchanged —
48
+ * only `useAgentThreadRuns.start()`/`retry()` pass it; a bare `stream(input, signal)` call (e.g.
49
+ * `useAgentStream`, a hand-rolled caller) omits it and a transport falls back to minting its own.
50
+ *
42
51
  * @example
43
52
  * // Minimal mock transport for tests / playground:
44
53
  * const mockTransport: AgentTransport = {
@@ -48,7 +57,9 @@ import type { AgentPart, AgentPartDraft } from './parts.js';
48
57
  * }
49
58
  */
50
59
  export type AgentTransport<TPart = AgentPartDraft, TInput = string> = {
51
- stream: (input: TInput, signal?: AbortSignal) => AsyncGenerator<TPart>;
60
+ stream: (input: TInput, signal?: AbortSignal, ctx?: {
61
+ readonly messageId: string;
62
+ }) => AsyncGenerator<TPart>;
52
63
  /** Optional: resume a previously-started run from its resumeToken (see StartPart). Transports that don't support resumption simply omit this. */
53
64
  resume?: (resumeToken: string, signal?: AbortSignal) => AsyncGenerator<TPart>;
54
65
  };
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/agent/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAIxD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI;IACpE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACtE,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;CAC9E,CAAA;AAID;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI,cAAc,CAC3F,KAAK,EACL,MAAM,CACP,GAAG;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACrF,QAAQ,CAAC,gBAAgB,EAAE,IAAI,CAAA;CAChC,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EACvC,CAAC,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC/B,CAAC,IAAI,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAK7C;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAC9D,IAAI,EAAE,CACJ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,UAAU,CAAC,EAAE,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAA;CAAE,GAC7C,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAsBxE"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/agent/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAIxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI;IACpE,MAAM,EAAE,CACN,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,EACpB,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KACjC,cAAc,CAAC,KAAK,CAAC,CAAA;IAC1B,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;CAC9E,CAAA;AAID;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI,cAAc,CAC3F,KAAK,EACL,MAAM,CACP,GAAG;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACrF,QAAQ,CAAC,gBAAgB,EAAE,IAAI,CAAA;CAChC,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EACvC,CAAC,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC/B,CAAC,IAAI,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAK7C;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAC9D,IAAI,EAAE,CACJ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,UAAU,CAAC,EAAE,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAA;CAAE,GAC7C,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAsBxE"}
@@ -42,6 +42,17 @@ export type UseAgentThreadRunsArgs<TPart = AgentPart> = {
42
42
  * Required when TPart is not the default AgentPart union.
43
43
  */
44
44
  readonly toUserParts?: (input: string) => TPart[];
45
+ /**
46
+ * Called when a turn's stream throws — never for an `AbortError` (an intentional stop()/
47
+ * supersede, not a failure worth surfacing). Fires from the SAME catch branch that sets
48
+ * `onFailureStatus` on the thread, so a caller wanting a toast/notification for a genuine
49
+ * transport error (a 409/503/401, argo's hermes-chat S19) no longer has to wrap every transport
50
+ * call in its own generator to intercept the throw — see `consumeAndFinalize`'s catch block.
51
+ */
52
+ readonly onError?: (info: {
53
+ readonly threadId: string;
54
+ readonly error: unknown;
55
+ }) => void;
45
56
  };
46
57
  export type UseAgentThreadRunsReturn<TPart = AgentPart> = {
47
58
  /** Live stream state per thread id, for threads with a run in progress or just completed. */
@@ -95,5 +106,5 @@ export type UseAgentThreadRunsReturn<TPart = AgentPart> = {
95
106
  * start(threadB, 'What changed in the last release?')
96
107
  * // both stream concurrently; runs.get(threadA) / runs.get(threadB) update independently
97
108
  */
98
- export declare function useAgentThreadRuns<TPart extends PartLike = AgentPart>({ transport, store, resolveOutcome, toUserParts, }: UseAgentThreadRunsArgs<TPart>): UseAgentThreadRunsReturn<TPart>;
109
+ export declare function useAgentThreadRuns<TPart extends PartLike = AgentPart>({ transport, store, resolveOutcome, toUserParts, onError, }: UseAgentThreadRunsArgs<TPart>): UseAgentThreadRunsReturn<TPart>;
99
110
  //# sourceMappingURL=use-agent-thread-runs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-thread-runs.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-thread-runs.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,WAAW,CAAA;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAA6B,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,IAAI;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;CACxB,CAAA;AAID,MAAM,MAAM,sBAAsB,CAAC,KAAK,GAAG,SAAS,IAAI;IACtD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,EACd,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC7B,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IACzD,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IACnC,uFAAuF;IACvF,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC,KAAK,CAAC,CAAA;IAC/C;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;CAClD,CAAA;AAID,MAAM,MAAM,wBAAwB,CAAC,KAAK,GAAG,SAAS,IAAI;IACxD,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;IACzD;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzD;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAC7B,CAAA;AAuRD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,QAAQ,GAAG,SAAS,EAAE,EACrE,SAAS,EACT,KAAK,EACL,cAAc,EACd,WAAW,GACZ,EAAE,sBAAsB,CAAC,KAAK,CAAC,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAgSjE"}
1
+ {"version":3,"file":"use-agent-thread-runs.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-thread-runs.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,WAAW,CAAA;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAA6B,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,IAAI;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;CACxB,CAAA;AAID,MAAM,MAAM,sBAAsB,CAAC,KAAK,GAAG,SAAS,IAAI;IACtD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,EACd,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC7B,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IACzD,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IACnC,uFAAuF;IACvF,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC,KAAK,CAAC,CAAA;IAC/C;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;IACjD;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAA;CAC1F,CAAA;AAID,MAAM,MAAM,wBAAwB,CAAC,KAAK,GAAG,SAAS,IAAI;IACxD,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;IACzD;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzD;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAC7B,CAAA;AA0RD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,QAAQ,GAAG,SAAS,EAAE,EACrE,SAAS,EACT,KAAK,EACL,cAAc,EACd,WAAW,EACX,OAAO,GACR,EAAE,sBAAsB,CAAC,KAAK,CAAC,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAsUjE"}
@@ -37,7 +37,8 @@ async function consumeAndFinalize(args) {
37
37
  storeRef,
38
38
  resolveOutcome,
39
39
  setRuns,
40
- onFailureStatus
40
+ onFailureStatus,
41
+ onError
41
42
  } = args;
42
43
  let parts = [];
43
44
  try {
@@ -99,6 +100,7 @@ async function consumeAndFinalize(args) {
99
100
  next.delete(threadId);
100
101
  return next;
101
102
  });
103
+ onError?.({ threadId, error: err });
102
104
  }
103
105
  }
104
106
  async function finalizeStop(args) {
@@ -149,11 +151,14 @@ function useAgentThreadRuns({
149
151
  transport,
150
152
  store,
151
153
  resolveOutcome,
152
- toUserParts
154
+ toUserParts,
155
+ onError
153
156
  }) {
154
157
  const [runs, setRuns] = useState(/* @__PURE__ */ new Map());
155
158
  const runsRef = useRef(runs);
156
159
  runsRef.current = runs;
160
+ const onErrorRef = useRef(onError);
161
+ onErrorRef.current = onError;
157
162
  const controllersRef = useRef(/* @__PURE__ */ new Map());
158
163
  const appendedRef = useRef(/* @__PURE__ */ new Map());
159
164
  const lastInputRef = useRef(/* @__PURE__ */ new Map());
@@ -194,6 +199,7 @@ function useAgentThreadRuns({
194
199
  []
195
200
  );
196
201
  useEffect(() => {
202
+ if (!store.hydrated) return;
197
203
  for (const thread of storeRef.current.threads) {
198
204
  const orphaned = (thread.status === "pending" || thread.status === "streaming") && !controllersRef.current.has(thread.id);
199
205
  if (!orphaned) continue;
@@ -229,10 +235,11 @@ function useAgentThreadRuns({
229
235
  storeRef,
230
236
  resolveOutcome,
231
237
  setRuns,
232
- onFailureStatus: "interrupted"
238
+ onFailureStatus: "interrupted",
239
+ onError: (info) => onErrorRef.current?.(info)
233
240
  });
234
241
  }
235
- }, []);
242
+ }, [store.hydrated]);
236
243
  const start = useCallback(
237
244
  (threadId, input) => {
238
245
  if (controllersRef.current.has(threadId)) return;
@@ -258,14 +265,21 @@ function useAgentThreadRuns({
258
265
  threadId,
259
266
  controller,
260
267
  // Namespaced by threadId — same choice, same caveat, as the mount-time resume call above.
261
- generator: withPartIds(threadId, resolvedTransport.stream(input, controller.signal)),
268
+ // ctx.messageId is this turn's OWN idempotency key (userMessage.id, just minted above) —
269
+ // a transport that accepts it (aiSdkTransport) mints its wire-level message with the SAME
270
+ // id instead of a second, unrelated one (R1 — see AgentTransport.stream's doc).
271
+ generator: withPartIds(
272
+ threadId,
273
+ resolvedTransport.stream(input, controller.signal, { messageId: userMessage.id })
274
+ ),
262
275
  userMessage,
263
276
  controllersRef,
264
277
  appendedRef,
265
278
  storeRef,
266
279
  resolveOutcome,
267
280
  setRuns,
268
- onFailureStatus: "error"
281
+ onFailureStatus: "error",
282
+ onError: (info) => onErrorRef.current?.(info)
269
283
  });
270
284
  },
271
285
  [resolveOutcome]
@@ -17,7 +17,7 @@ export type { ThreadFeedProps } from './thread-feed.js';
17
17
  export { ThreadFeedRow } from './thread-feed-row.js';
18
18
  export type { ThreadFeedRowProps } from './thread-feed-row.js';
19
19
  export type { MessageAffordances } from './message-affordances.js';
20
- export type { VirtualizeOptions, VirtualizeProps } from './virtualize.js';
20
+ export type { RowHeightProps, VirtualizeOptions, VirtualizeProps } from './virtualize.js';
21
21
  export { ThreadOutcomeCard } from './thread-outcome-card.js';
22
22
  export type { ThreadOutcomeCardProps } from './thread-outcome-card.js';
23
23
  export { ThreadDetailPanel } from './thread-detail-panel.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAG9D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAO3D,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAGtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAGnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAGnE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGnG,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAG7D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAG9D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAS3D,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAGtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAGnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAGnE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGnG,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAG7D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA"}
@@ -1,4 +1,4 @@
1
- import { isDev } from "../utils/is-dev.js";
1
+ import { isDev } from "../common/is-dev.js";
2
2
  const RELATIVE_TIME_FORMAT = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
3
3
  const RELATIVE_TIME_UNITS = [
4
4
  { unit: "year", ms: 31536e6 },
@@ -4,7 +4,7 @@ import { useReducedMotion } from "@mantine/hooks";
4
4
  import { motion } from "motion/react";
5
5
  import { BasaltStickToBottom } from "../agent/index.js";
6
6
  import { cx } from "../common/props.js";
7
- import { MOTION_SPRING } from "../motion/index.js";
7
+ import { MOTION_SPRING } from "../common/motion.js";
8
8
  import { Composer } from "./composer.js";
9
9
  import { ThreadTranscript } from "./thread-message.js";
10
10
  import { VX } from "../tokens/index.js";
@@ -1,9 +1,9 @@
1
- import type { JSX } from 'react';
2
- import type { AgentThread, ForeignPart, PartRenderer, PartRenderers, StreamStatus, TranscriptPart } from '../agent/index.js';
3
- import type { BasaltProps } from '../common/props.js';
1
+ import type { JSX, ReactNode } from 'react';
2
+ import type { AgentThread, ChatMessage, ForeignPart, PartRenderer, PartRenderers, StreamStatus, TranscriptPart } from '../agent/index.js';
3
+ import type { BasaltProps, SlotStylesProps } from '../common/props.js';
4
4
  import type { ComposerProps, ComposerSubmit } from './composer.js';
5
5
  import type { MessageAffordances } from './message-affordances.js';
6
- import type { VirtualizeProps } from './virtualize.js';
6
+ import type { RowHeightProps } from './virtualize.js';
7
7
  type ThreadFeedRowBase = {
8
8
  /**
9
9
  * The thread this row projects. Typed against `AgentThread<TranscriptPart>` — not the plain,
@@ -19,6 +19,30 @@ type ThreadFeedRowBase = {
19
19
  readonly expanded: boolean;
20
20
  /** Called with the thread's id when the header is clicked/activated. */
21
21
  readonly onToggle: (id: string) => void;
22
+ /**
23
+ * Header title override. Falls back to `thread.outcome?.title ?? 'Untitled thread'` (today's
24
+ * behavior, unchanged) when omitted. For a server-titled thread whose title is never distilled
25
+ * into a basalt `AgentOutcome` (argo's hermes-chat S4 — title/summary/type/pin are server-owned
26
+ * and never guessed client-side), pass it directly instead of forcing an outcome shape onto data
27
+ * that never had one.
28
+ */
29
+ readonly title?: string;
30
+ /** Header summary override, same fallback rule as `title` (`thread.outcome?.summary ?? ''`). */
31
+ readonly summary?: string;
32
+ /** Rendered at the START of the header row, before the title/summary stack — a pin icon, a type
33
+ * badge (argo's hermes-chat S5). Absent by default; renders nothing extra when omitted. */
34
+ readonly headerLeft?: ReactNode;
35
+ /** Rendered at the END of the header row, before the timestamp and chevron — a collapsed-only
36
+ * streaming indicator (argo's hermes-chat S5). Absent by default; renders nothing extra when
37
+ * omitted. */
38
+ readonly headerRight?: ReactNode;
39
+ /**
40
+ * Overrides the transcript source: when provided, the row renders THESE messages instead of
41
+ * reading `thread.messages` directly (argo's hermes-chat S6 — the confirmed transcript is an
42
+ * optimistic-overlay MERGE over the store's messages, not the store's messages verbatim).
43
+ * Defaults to `thread.messages`.
44
+ */
45
+ readonly messages?: readonly ChatMessage<TranscriptPart>[];
22
46
  /** The live (in-flight) assistant turn's parts, when a run is streaming for this thread. */
23
47
  readonly liveParts?: readonly TranscriptPart[];
24
48
  /** The live run's status — drives the in-progress indicator on the live block. */
@@ -49,10 +73,11 @@ type ThreadFeedRowBase = {
49
73
  * `virtualize`/`height` are forwarded straight to the row's `ThreadTranscript`, carrying the same
50
74
  * union guard: an inline row holding a very long thread can window it, and doing so REQUIRES a
51
75
  * `height` (the transcript then owns a fixed-height scroll node inside the expanded body, instead
52
- * of the body growing to the thread's full length). Omit both and the row's transcript is
53
- * content-sized, as before.
76
+ * of the body growing to the thread's full length). `height` alone (no `virtualize`) is B3's
77
+ * bounded-but-not-virtualized mode — see `RowHeightProps`'s own doc. Omit both and the row's
78
+ * transcript is content-sized, as before.
54
79
  */
55
- export type ThreadFeedRowProps = ThreadFeedRowBase & VirtualizeProps & BasaltProps;
80
+ export type ThreadFeedRowProps = ThreadFeedRowBase & RowHeightProps & SlotStylesProps<'root' | 'header' | 'body'> & BasaltProps;
56
81
  /**
57
82
  * One inline-expanding thread row: a header (title + relative timestamp) that toggles a lazily
58
83
  * mounted, kept-mounted transcript + composer body. See the module doc for the mount lifecycle