basalt-ui 1.41.0 → 2.0.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 (915) hide show
  1. package/AGENTS.md +46 -43
  2. package/CHANGELOG.md +542 -518
  3. package/MIGRATING.md +964 -37
  4. package/README.md +171 -366
  5. package/agent/rules/basalt-batteries.md +19 -74
  6. package/agent/rules/basalt-charts.md +21 -95
  7. package/agent/rules/basalt-controls.md +23 -131
  8. package/agent/rules/basalt-mantine.md +26 -99
  9. package/agent/rules/basalt-state.md +21 -103
  10. package/agent/rules/basalt-tokens.md +33 -114
  11. package/agent/skills/basalt-app/SKILL.md +229 -72
  12. package/agent/skills/basalt-charts/SKILL.md +23 -57
  13. package/agent/skills/basalt-design/SKILL.md +12 -70
  14. package/agent/templates/AGENTS-block.md.tpl +16 -26
  15. package/agent/templates/DESIGN.md.tpl +1 -1
  16. package/agent/templates/__root.tpl +9 -16
  17. package/agent/templates/main.tpl +21 -0
  18. package/agent/templates/nav.tpl +18 -0
  19. package/configs/oxlint-basalt.json +4 -10
  20. package/configs/oxlint-plugin.js +465 -844
  21. package/configs/oxlint-plugin.test.ts +661 -709
  22. package/configs/oxlint.json +10 -2
  23. package/dist/agent/ai-sdk-transport.d.ts +19 -7
  24. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  25. package/dist/agent/ai-sdk-transport.js +10 -7
  26. package/dist/{agent-chat → agent/chat}/composer.d.ts +16 -16
  27. package/dist/agent/chat/composer.d.ts.map +1 -0
  28. package/dist/{agent-chat → agent/chat}/composer.js +10 -9
  29. package/dist/{agent-chat → agent/chat}/message-affordances.d.ts +7 -6
  30. package/dist/agent/chat/message-affordances.d.ts.map +1 -0
  31. package/dist/agent/chat/relative-time.d.ts.map +1 -0
  32. package/dist/{agent-chat → agent/chat}/thread-feed-row.d.ts +18 -16
  33. package/dist/agent/chat/thread-feed-row.d.ts.map +1 -0
  34. package/dist/{agent-chat → agent/chat}/thread-feed-row.js +12 -11
  35. package/dist/{agent-chat → agent/chat}/thread-feed.d.ts +8 -8
  36. package/dist/agent/chat/thread-feed.d.ts.map +1 -0
  37. package/dist/{agent-chat → agent/chat}/thread-feed.js +12 -10
  38. package/dist/{agent-chat → agent/chat}/thread-message.d.ts +12 -9
  39. package/dist/agent/chat/thread-message.d.ts.map +1 -0
  40. package/dist/{agent-chat → agent/chat}/thread-message.js +15 -13
  41. package/dist/{agent-chat → agent/chat}/thread-outcome-card.d.ts +2 -2
  42. package/dist/agent/chat/thread-outcome-card.d.ts.map +1 -0
  43. package/dist/{agent-chat → agent/chat}/thread-outcome-card.js +3 -3
  44. package/dist/{agent-chat → agent/chat}/tool-chip.d.ts +5 -5
  45. package/dist/agent/chat/tool-chip.d.ts.map +1 -0
  46. package/dist/{agent-chat → agent/chat}/tool-chip.js +4 -4
  47. package/dist/agent/chat/use-keyboard-inset.d.ts.map +1 -0
  48. package/dist/{agent-chat → agent/chat}/use-keyboard-inset.js +1 -1
  49. package/dist/{agent-chat → agent/chat}/virtualize.d.ts +7 -7
  50. package/dist/agent/chat/virtualize.d.ts.map +1 -0
  51. package/dist/agent/error.d.ts +31 -0
  52. package/dist/agent/error.d.ts.map +1 -0
  53. package/dist/agent/error.js +18 -0
  54. package/dist/agent/history.d.ts +2 -36
  55. package/dist/agent/history.d.ts.map +1 -1
  56. package/dist/agent/history.js +0 -32
  57. package/dist/agent/id.d.ts +4 -5
  58. package/dist/agent/id.d.ts.map +1 -1
  59. package/dist/agent/index.d.ts +19 -52
  60. package/dist/agent/index.d.ts.map +1 -1
  61. package/dist/agent/index.js +10 -32
  62. package/dist/agent/outcome.d.ts +3 -14
  63. package/dist/agent/outcome.d.ts.map +1 -1
  64. package/dist/agent/outcome.js +0 -30
  65. package/dist/agent/part-list.d.ts +3 -3
  66. package/dist/agent/part-list.d.ts.map +1 -1
  67. package/dist/agent/parts.d.ts +23 -24
  68. package/dist/agent/parts.d.ts.map +1 -1
  69. package/dist/agent/run-committer.d.ts +23 -0
  70. package/dist/agent/run-committer.d.ts.map +1 -0
  71. package/dist/agent/run-committer.js +41 -0
  72. package/dist/agent/run-records.d.ts +77 -0
  73. package/dist/agent/run-records.d.ts.map +1 -0
  74. package/dist/agent/run-records.js +69 -0
  75. package/dist/agent/thread.d.ts +23 -47
  76. package/dist/agent/thread.d.ts.map +1 -1
  77. package/dist/agent/thread.js +3 -134
  78. package/dist/agent/transport.d.ts +6 -69
  79. package/dist/agent/transport.d.ts.map +1 -1
  80. package/dist/agent/transport.js +0 -18
  81. package/dist/agent/use-agent-thread-runs.d.ts +63 -26
  82. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  83. package/dist/agent/use-agent-thread-runs.js +202 -66
  84. package/dist/charts/BandStrip.d.ts +46 -0
  85. package/dist/charts/BandStrip.d.ts.map +1 -0
  86. package/dist/charts/BandStrip.js +151 -0
  87. package/dist/charts/Bars.d.ts +19 -0
  88. package/dist/charts/Bars.d.ts.map +1 -0
  89. package/dist/charts/Bars.js +77 -0
  90. package/dist/charts/CartesianChart.d.ts +57 -0
  91. package/dist/charts/CartesianChart.d.ts.map +1 -0
  92. package/dist/charts/CartesianChart.js +121 -0
  93. package/dist/charts/ChartCard.d.ts +34 -0
  94. package/dist/charts/ChartCard.d.ts.map +1 -0
  95. package/dist/charts/ChartCard.js +115 -0
  96. package/dist/charts/Donut.d.ts +25 -0
  97. package/dist/charts/Donut.d.ts.map +1 -0
  98. package/dist/charts/Donut.js +191 -0
  99. package/dist/charts/DualPanel.d.ts +47 -0
  100. package/dist/charts/DualPanel.d.ts.map +1 -0
  101. package/dist/charts/DualPanel.js +214 -0
  102. package/dist/charts/Heatmap.d.ts +41 -0
  103. package/dist/charts/Heatmap.d.ts.map +1 -0
  104. package/dist/charts/Heatmap.js +216 -0
  105. package/dist/charts/Lines.d.ts +11 -0
  106. package/dist/charts/Lines.d.ts.map +1 -0
  107. package/dist/charts/Lines.js +114 -0
  108. package/dist/charts/MirroredBars.d.ts +44 -0
  109. package/dist/charts/MirroredBars.d.ts.map +1 -0
  110. package/dist/charts/MirroredBars.js +190 -0
  111. package/dist/charts/Sparkline.d.ts +22 -0
  112. package/dist/charts/Sparkline.d.ts.map +1 -0
  113. package/dist/charts/Sparkline.js +129 -0
  114. package/dist/charts/StackedArea.d.ts +15 -0
  115. package/dist/charts/StackedArea.d.ts.map +1 -0
  116. package/dist/charts/StackedArea.js +46 -0
  117. package/dist/charts/box.d.ts +38 -0
  118. package/dist/charts/box.d.ts.map +1 -0
  119. package/dist/charts/box.js +72 -0
  120. package/dist/charts/card-context.d.ts +3 -0
  121. package/dist/charts/card-context.d.ts.map +1 -0
  122. package/dist/charts/card-context.js +7 -0
  123. package/dist/charts/chart-box.module.css +64 -0
  124. package/{src/content/article-card.module.css.d.ts → dist/charts/chart-box.module.css.d.ts} +5 -9
  125. package/dist/charts/chart-card.module.css +25 -0
  126. package/dist/{dashboard/widget-grid.module.css.d.ts → charts/chart-card.module.css.d.ts} +1 -3
  127. package/dist/charts/chart-legend.module.css +38 -0
  128. package/{src/dashboard/widget-grid.module.css.d.ts → dist/charts/chart-legend.module.css.d.ts} +3 -3
  129. package/dist/charts/compile.d.ts +62 -0
  130. package/dist/charts/compile.d.ts.map +1 -0
  131. package/dist/charts/compile.js +386 -0
  132. package/dist/charts/cursor.d.ts +50 -0
  133. package/dist/charts/cursor.d.ts.map +1 -0
  134. package/dist/charts/cursor.js +157 -0
  135. package/dist/charts/curves.d.ts +12 -0
  136. package/dist/charts/curves.d.ts.map +1 -0
  137. package/dist/charts/curves.js +86 -0
  138. package/dist/charts/donut.module.css +54 -0
  139. package/dist/{content/article-card.module.css.d.ts → charts/donut.module.css.d.ts} +2 -9
  140. package/dist/charts/host.d.ts +17 -0
  141. package/dist/charts/host.d.ts.map +1 -0
  142. package/dist/charts/host.js +35 -0
  143. package/dist/charts/index.d.ts +18 -53
  144. package/dist/charts/index.d.ts.map +1 -1
  145. package/dist/charts/index.js +17 -189
  146. package/dist/charts/legend.d.ts +23 -0
  147. package/dist/charts/legend.d.ts.map +1 -0
  148. package/dist/charts/legend.js +160 -0
  149. package/dist/charts/marks.d.ts +25 -0
  150. package/dist/charts/marks.d.ts.map +1 -0
  151. package/dist/charts/marks.js +128 -0
  152. package/dist/charts/panel.d.ts +56 -0
  153. package/dist/charts/panel.d.ts.map +1 -0
  154. package/dist/charts/panel.js +123 -0
  155. package/dist/charts/scales.d.ts +44 -0
  156. package/dist/charts/scales.d.ts.map +1 -0
  157. package/dist/charts/scales.js +163 -0
  158. package/dist/charts/sizing.d.ts +12 -0
  159. package/dist/charts/sizing.d.ts.map +1 -0
  160. package/dist/charts/sizing.js +33 -0
  161. package/dist/charts/state.d.ts +17 -0
  162. package/dist/charts/state.d.ts.map +1 -0
  163. package/dist/charts/state.js +53 -0
  164. package/dist/charts/theme.d.ts +38 -19
  165. package/dist/charts/theme.d.ts.map +1 -1
  166. package/dist/charts/theme.js +48 -26
  167. package/dist/charts/types.d.ts +227 -0
  168. package/dist/charts/types.d.ts.map +1 -0
  169. package/dist/charts/types.js +4 -0
  170. package/dist/cli/audit-allows.d.ts +4 -17
  171. package/dist/cli/audit-allows.d.ts.map +1 -1
  172. package/dist/cli/config.d.ts +18 -43
  173. package/dist/cli/config.d.ts.map +1 -1
  174. package/dist/cli/config.js +6 -0
  175. package/dist/cli/doctor-upgrade.d.ts +43 -0
  176. package/dist/cli/doctor-upgrade.d.ts.map +1 -0
  177. package/dist/cli/doctor-upgrade.js +314 -0
  178. package/dist/cli/doctor.d.ts +14 -29
  179. package/dist/cli/doctor.d.ts.map +1 -1
  180. package/dist/cli/doctor.js +58 -9
  181. package/dist/cli/help.d.ts.map +1 -1
  182. package/dist/cli/help.js +2 -4
  183. package/dist/cli/index.d.ts +48 -172
  184. package/dist/cli/index.d.ts.map +1 -1
  185. package/dist/cli/index.js +39 -132
  186. package/dist/cli/init.d.ts.map +1 -1
  187. package/dist/cli/init.js +43 -37
  188. package/dist/cli/sync.d.ts +5 -21
  189. package/dist/cli/sync.d.ts.map +1 -1
  190. package/dist/cli/sync.js +4 -23
  191. package/dist/cli/tokens-css.d.ts +7 -28
  192. package/dist/cli/tokens-css.d.ts.map +1 -1
  193. package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
  194. package/dist/commands/ShortcutsHelp.js +4 -5
  195. package/dist/commands/define-commands.d.ts +15 -31
  196. package/dist/commands/define-commands.d.ts.map +1 -1
  197. package/dist/commands/define-commands.js +1 -11
  198. package/dist/commands/define-overlays.d.ts +5 -19
  199. package/dist/commands/define-overlays.d.ts.map +1 -1
  200. package/dist/commands/define-overlays.js +3 -11
  201. package/dist/commands/index.d.ts +10 -40
  202. package/dist/commands/index.d.ts.map +1 -1
  203. package/dist/commands/index.js +9 -46
  204. package/dist/{notifications → commands/notifications}/bell.d.ts +1 -1
  205. package/dist/commands/notifications/bell.d.ts.map +1 -0
  206. package/dist/{notifications → commands/notifications}/center.d.ts +2 -2
  207. package/dist/commands/notifications/center.d.ts.map +1 -0
  208. package/dist/{notifications → commands/notifications}/center.js +2 -2
  209. package/dist/{notifications → commands/notifications}/define-notifications.d.ts +3 -3
  210. package/dist/commands/notifications/define-notifications.d.ts.map +1 -0
  211. package/dist/commands/notifications/notify.d.ts +44 -0
  212. package/dist/commands/notifications/notify.d.ts.map +1 -0
  213. package/dist/commands/notifications/notify.js +79 -0
  214. package/dist/{notifications → commands/notifications}/store.d.ts +2 -0
  215. package/dist/commands/notifications/store.d.ts.map +1 -0
  216. package/dist/{notifications → commands/notifications}/store.js +4 -0
  217. package/dist/commands/projectors.d.ts +1 -131
  218. package/dist/commands/projectors.d.ts.map +1 -1
  219. package/dist/commands/projectors.js +2 -73
  220. package/dist/commands/shell-bridge.d.ts +3 -3
  221. package/dist/commands/shortcut-format.d.ts +5 -4
  222. package/dist/commands/shortcut-format.d.ts.map +1 -1
  223. package/dist/common/errors.d.ts +3 -3
  224. package/dist/common/use-measured-widths.d.ts.map +1 -1
  225. package/dist/common/use-measured-widths.js +5 -3
  226. package/dist/common/validate.d.ts +2 -2
  227. package/dist/content/article-layout.d.ts.map +1 -1
  228. package/dist/content/article-layout.js +3 -3
  229. package/dist/content/article-model.d.ts +1 -46
  230. package/dist/content/article-model.d.ts.map +1 -1
  231. package/dist/content/article-model.js +1 -29
  232. package/dist/content/index.d.ts +11 -41
  233. package/dist/content/index.d.ts.map +1 -1
  234. package/dist/content/index.js +3 -35
  235. package/dist/content/markdown.d.ts +1 -1
  236. package/dist/content/markdown.d.ts.map +1 -1
  237. package/dist/content/markdown.js +1 -1
  238. package/dist/content/mermaid.d.ts.map +1 -1
  239. package/dist/content/mermaid.js +7 -7
  240. package/dist/content/slug.d.ts +1 -8
  241. package/dist/content/slug.d.ts.map +1 -1
  242. package/dist/content/slug.js +1 -8
  243. package/dist/content/url-hardening.d.ts +2 -2
  244. package/dist/content/url-hardening.d.ts.map +1 -1
  245. package/dist/content/url-hardening.js +2 -0
  246. package/dist/controls/actions.d.ts +16 -1
  247. package/dist/controls/actions.d.ts.map +1 -1
  248. package/dist/controls/enum-filter.d.ts +1 -1
  249. package/dist/controls/enum-filter.d.ts.map +1 -1
  250. package/dist/controls/filter-context.d.ts.map +1 -1
  251. package/dist/controls/filter-context.js +3 -1
  252. package/dist/controls/filter-set.js +4 -6
  253. package/dist/controls/form-layout.d.ts +1 -1
  254. package/dist/controls/form-layout.d.ts.map +1 -1
  255. package/dist/controls/form-layout.js +1 -1
  256. package/dist/controls/glyphs.d.ts +0 -2
  257. package/dist/controls/glyphs.d.ts.map +1 -1
  258. package/dist/controls/glyphs.js +0 -7
  259. package/dist/controls/index.d.ts +12 -17
  260. package/dist/controls/index.d.ts.map +1 -1
  261. package/dist/controls/index.js +5 -39
  262. package/dist/controls/multi-select-filter.d.ts +4 -3
  263. package/dist/controls/multi-select-filter.d.ts.map +1 -1
  264. package/dist/controls/number-filter.d.ts +4 -3
  265. package/dist/controls/number-filter.d.ts.map +1 -1
  266. package/dist/controls/number-filter.js +3 -1
  267. package/dist/controls/range-filter.d.ts +4 -3
  268. package/dist/controls/range-filter.d.ts.map +1 -1
  269. package/dist/controls/search-filter.d.ts +4 -3
  270. package/dist/controls/search-filter.d.ts.map +1 -1
  271. package/dist/controls/search-filter.js +19 -14
  272. package/dist/controls/select-filter.d.ts +4 -3
  273. package/dist/controls/select-filter.d.ts.map +1 -1
  274. package/dist/controls/sync-button.d.ts.map +1 -1
  275. package/dist/controls/sync-button.js +12 -15
  276. package/dist/controls/toggle-filter.d.ts +4 -3
  277. package/dist/controls/toggle-filter.d.ts.map +1 -1
  278. package/dist/controls/uncontrolled.d.ts +12 -0
  279. package/dist/controls/uncontrolled.d.ts.map +1 -0
  280. package/dist/controls/view-tabs.d.ts +2 -2
  281. package/dist/controls/view-tabs.d.ts.map +1 -1
  282. package/dist/controls-dates/index.d.ts +0 -1
  283. package/dist/controls-dates/index.d.ts.map +1 -1
  284. package/dist/dashboard/index.d.ts +7 -9
  285. package/dist/dashboard/index.d.ts.map +1 -1
  286. package/dist/dashboard/index.js +3 -23
  287. package/dist/dashboard/section.d.ts +4 -6
  288. package/dist/dashboard/section.d.ts.map +1 -1
  289. package/dist/dashboard/section.js +4 -10
  290. package/dist/dashboard/stat-card.d.ts +4 -4
  291. package/dist/dashboard/stat-card.d.ts.map +1 -1
  292. package/dist/dashboard/stat-card.js +3 -3
  293. package/dist/dashboard/widget-header.module.css +9 -7
  294. package/dist/dev-dock.d.ts.map +1 -1
  295. package/dist/dev-dock.js +6 -1
  296. package/dist/format/date.d.ts +0 -34
  297. package/dist/format/date.d.ts.map +1 -1
  298. package/dist/format/date.js +0 -54
  299. package/dist/format/index.d.ts +7 -22
  300. package/dist/format/index.d.ts.map +1 -1
  301. package/dist/format/index.js +2 -43
  302. package/dist/format/number.d.ts +3 -14
  303. package/dist/format/number.d.ts.map +1 -1
  304. package/dist/format/number.js +0 -13
  305. package/dist/format/shared.d.ts +2 -3
  306. package/dist/format/shared.d.ts.map +1 -1
  307. package/dist/forms/index.d.ts +0 -11
  308. package/dist/forms/index.d.ts.map +1 -1
  309. package/dist/forms/index.js +1 -14
  310. package/dist/guard/index.d.ts +6 -21
  311. package/dist/guard/index.d.ts.map +1 -1
  312. package/dist/guard/index.js +27 -115
  313. package/dist/guard/types.d.ts +7 -18
  314. package/dist/guard/types.d.ts.map +1 -1
  315. package/dist/index.d.ts +16 -48
  316. package/dist/index.d.ts.map +1 -1
  317. package/dist/index.js +21 -117
  318. package/dist/provider/create-router.d.ts +44 -0
  319. package/dist/provider/create-router.d.ts.map +1 -0
  320. package/dist/provider/create-router.js +62 -0
  321. package/dist/provider/hotkeys.d.ts +8 -0
  322. package/dist/provider/hotkeys.d.ts.map +1 -0
  323. package/dist/provider/hotkeys.js +50 -0
  324. package/dist/provider/index.d.ts +33 -110
  325. package/dist/provider/index.d.ts.map +1 -1
  326. package/dist/provider/index.js +182 -82
  327. package/dist/provider/lab-theme.d.ts.map +1 -1
  328. package/dist/provider/route-transition.d.ts +76 -0
  329. package/dist/provider/route-transition.d.ts.map +1 -0
  330. package/dist/provider/route-transition.js +98 -0
  331. package/dist/provider/spotlight-actions.d.ts +10 -0
  332. package/dist/provider/spotlight-actions.d.ts.map +1 -0
  333. package/dist/provider/spotlight-actions.js +55 -0
  334. package/dist/provider/test-router.d.ts +6 -0
  335. package/dist/provider/test-router.d.ts.map +1 -0
  336. package/dist/provider/test-router.js +18 -0
  337. package/dist/query-client.d.ts +4 -20
  338. package/dist/query-client.d.ts.map +1 -1
  339. package/dist/query-client.js +2 -2
  340. package/dist/router-tanstack/index.d.ts +18 -64
  341. package/dist/router-tanstack/index.d.ts.map +1 -1
  342. package/dist/router-tanstack/index.js +2 -16
  343. package/dist/router-tanstack/nav.d.ts +12 -1
  344. package/dist/router-tanstack/nav.d.ts.map +1 -1
  345. package/dist/router-tanstack/nav.js +8 -1
  346. package/dist/router-tanstack/search-store.d.ts +1 -1
  347. package/dist/router-tanstack/search-store.d.ts.map +1 -1
  348. package/dist/router-tanstack/use-nav.d.ts +8 -9
  349. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  350. package/dist/router-tanstack/use-nav.js +20 -1
  351. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  352. package/dist/shell/app-sidebar.js +1 -1
  353. package/dist/shell/index.d.ts +31 -10
  354. package/dist/shell/index.d.ts.map +1 -1
  355. package/dist/shell/index.js +14 -3
  356. package/dist/shell/page-aside.d.ts +2 -2
  357. package/dist/shell/page-aside.d.ts.map +1 -1
  358. package/dist/shell/page-aside.js +31 -12
  359. package/dist/shell/page-bar.d.ts +2 -2
  360. package/dist/shell/page-bar.d.ts.map +1 -1
  361. package/dist/shell/page-bar.js +1 -1
  362. package/dist/state.d.ts +10 -12
  363. package/dist/state.d.ts.map +1 -1
  364. package/dist/state.js +4 -6
  365. package/dist/styles.css +107 -7
  366. package/dist/surfaces.d.ts +28 -103
  367. package/dist/surfaces.d.ts.map +1 -1
  368. package/dist/surfaces.js +54 -143
  369. package/dist/{data → table}/data-table-state.d.ts +1 -1
  370. package/dist/table/data-table-state.d.ts.map +1 -0
  371. package/dist/{data → table}/data-table-state.js +4 -3
  372. package/dist/{data → table}/data-table.d.ts +28 -49
  373. package/dist/table/data-table.d.ts.map +1 -0
  374. package/dist/{data → table}/data-table.js +74 -60
  375. package/dist/table/index.d.ts +18 -0
  376. package/dist/table/index.d.ts.map +1 -0
  377. package/dist/{data/table.js → table/index.js} +1 -1
  378. package/dist/table/query-branch.d.ts.map +1 -0
  379. package/dist/table/table-features.d.ts +69 -0
  380. package/dist/table/table-features.d.ts.map +1 -0
  381. package/dist/table/table-features.js +49 -0
  382. package/dist/telemetry.d.ts +77 -0
  383. package/dist/telemetry.d.ts.map +1 -0
  384. package/dist/telemetry.js +33 -0
  385. package/dist/theme/index.d.ts +10 -21
  386. package/dist/theme/index.d.ts.map +1 -1
  387. package/dist/theme/index.js +3 -14
  388. package/dist/theme/theme-toggle.d.ts.map +1 -1
  389. package/dist/theme/theme-toggle.js +7 -33
  390. package/dist/theme/theme-toggle.module.css +30 -0
  391. package/dist/theme/theme-toggle.module.css.d.ts +10 -0
  392. package/dist/theme-lab/derive-controls.d.ts.map +1 -1
  393. package/dist/theme-lab/derive-controls.js +5 -3
  394. package/dist/theme-lab/derive-state.d.ts.map +1 -1
  395. package/dist/theme-lab/derive-state.js +2 -1
  396. package/dist/tokens/core.d.ts +360 -0
  397. package/dist/tokens/core.d.ts.map +1 -0
  398. package/dist/tokens/core.js +708 -0
  399. package/dist/tokens/derive.d.ts +1 -1
  400. package/dist/tokens/derive.d.ts.map +1 -1
  401. package/dist/tokens/index.d.ts +5 -373
  402. package/dist/tokens/index.d.ts.map +1 -1
  403. package/dist/tokens/index.js +8 -700
  404. package/dist/tokens/palette.d.ts +2 -6
  405. package/dist/tokens/palette.d.ts.map +1 -1
  406. package/dist/tokens/palette.js +1 -4
  407. package/dist/vite.d.ts +4 -3
  408. package/dist/vite.d.ts.map +1 -1
  409. package/llms.txt +43 -85
  410. package/package.json +80 -173
  411. package/src/agent/ai-sdk-transport.ts +57 -37
  412. package/src/{agent-chat → agent/chat}/composer.tsx +29 -30
  413. package/src/{agent-chat → agent/chat}/message-affordances.ts +8 -5
  414. package/src/{agent-chat → agent/chat}/thread-feed-row.tsx +30 -35
  415. package/src/{agent-chat → agent/chat}/thread-feed.tsx +40 -35
  416. package/src/{agent-chat → agent/chat}/thread-message.tsx +37 -35
  417. package/src/{agent-chat → agent/chat}/thread-outcome-card.tsx +5 -5
  418. package/src/{agent-chat → agent/chat}/tool-chip.tsx +12 -12
  419. package/src/{agent-chat → agent/chat}/use-keyboard-inset.ts +1 -1
  420. package/src/{agent-chat → agent/chat}/virtualize.ts +5 -5
  421. package/src/agent/error.ts +43 -0
  422. package/src/agent/history.ts +2 -107
  423. package/src/agent/id.ts +4 -5
  424. package/src/agent/index.ts +23 -112
  425. package/src/agent/outcome.ts +3 -56
  426. package/src/agent/part-list.tsx +3 -3
  427. package/src/agent/parts.ts +23 -22
  428. package/src/agent/run-committer.ts +64 -0
  429. package/src/agent/run-records.ts +147 -0
  430. package/src/agent/thread.ts +24 -231
  431. package/src/agent/transport.ts +6 -96
  432. package/src/agent/use-agent-thread-runs.ts +364 -199
  433. package/src/charts/BandStrip.tsx +276 -0
  434. package/src/charts/Bars.tsx +141 -0
  435. package/src/charts/CartesianChart.tsx +206 -0
  436. package/src/charts/ChartCard.tsx +178 -0
  437. package/src/charts/Donut.tsx +256 -0
  438. package/src/charts/DualPanel.tsx +326 -0
  439. package/src/charts/Heatmap.tsx +282 -0
  440. package/src/charts/Lines.tsx +151 -0
  441. package/src/charts/MirroredBars.tsx +303 -0
  442. package/src/charts/Sparkline.tsx +176 -0
  443. package/src/charts/StackedArea.tsx +80 -0
  444. package/src/charts/box.tsx +96 -0
  445. package/src/charts/card-context.ts +11 -0
  446. package/src/charts/chart-box.module.css +64 -0
  447. package/src/charts/chart-box.module.css.d.ts +12 -0
  448. package/src/charts/chart-card.module.css +25 -0
  449. package/src/charts/chart-card.module.css.d.ts +8 -0
  450. package/src/charts/chart-legend.module.css +38 -0
  451. package/src/charts/chart-legend.module.css.d.ts +10 -0
  452. package/src/charts/compile.ts +585 -0
  453. package/src/charts/cursor.ts +282 -0
  454. package/src/charts/curves.ts +111 -0
  455. package/src/charts/donut.module.css +54 -0
  456. package/src/charts/donut.module.css.d.ts +9 -0
  457. package/src/charts/host.tsx +60 -0
  458. package/src/charts/index.ts +41 -178
  459. package/src/charts/legend.tsx +204 -0
  460. package/src/charts/marks.ts +201 -0
  461. package/src/charts/panel.tsx +227 -0
  462. package/src/charts/scales.ts +237 -0
  463. package/src/charts/sizing.ts +47 -0
  464. package/src/charts/state.tsx +80 -0
  465. package/src/charts/theme.ts +65 -0
  466. package/src/charts/types.ts +242 -0
  467. package/src/cli/audit-allows.ts +8 -30
  468. package/src/cli/config.ts +26 -43
  469. package/src/cli/doctor-upgrade.ts +457 -0
  470. package/src/cli/doctor.ts +89 -72
  471. package/src/cli/help.ts +2 -4
  472. package/src/cli/index.ts +100 -384
  473. package/src/cli/init.ts +60 -51
  474. package/src/cli/sync.ts +13 -68
  475. package/src/cli/tokens-css.ts +7 -28
  476. package/src/commands/ShortcutsHelp.tsx +8 -9
  477. package/src/commands/define-commands.ts +16 -43
  478. package/src/commands/define-overlays.ts +12 -36
  479. package/src/commands/index.ts +11 -94
  480. package/src/{notifications → commands/notifications}/bell.tsx +2 -2
  481. package/src/{notifications → commands/notifications}/center.tsx +7 -5
  482. package/src/{notifications → commands/notifications}/define-notifications.ts +3 -3
  483. package/src/commands/notifications/notify.tsx +158 -0
  484. package/src/{notifications → commands/notifications}/store.ts +10 -2
  485. package/src/commands/projectors.ts +5 -225
  486. package/src/commands/shell-bridge.ts +5 -5
  487. package/src/commands/shortcut-format.ts +5 -4
  488. package/src/common/errors.ts +3 -3
  489. package/src/common/use-measured-widths.ts +6 -3
  490. package/src/common/validate.ts +2 -2
  491. package/src/content/article-layout.tsx +37 -38
  492. package/src/content/article-model.ts +1 -84
  493. package/src/content/index.ts +12 -83
  494. package/src/content/markdown.tsx +4 -3
  495. package/src/content/mermaid.tsx +9 -7
  496. package/src/content/slug.ts +1 -16
  497. package/src/content/url-hardening.ts +8 -2
  498. package/src/controls/actions.tsx +24 -1
  499. package/src/controls/enum-filter.tsx +1 -1
  500. package/src/controls/filter-context.tsx +3 -1
  501. package/src/controls/filter-set.tsx +5 -7
  502. package/src/controls/form-layout.tsx +2 -2
  503. package/src/controls/glyphs.tsx +0 -10
  504. package/src/controls/index.ts +13 -64
  505. package/src/controls/multi-select-filter.tsx +5 -4
  506. package/src/controls/number-filter.tsx +8 -4
  507. package/src/controls/range-filter.tsx +5 -4
  508. package/src/controls/search-filter.tsx +25 -19
  509. package/src/controls/select-filter.tsx +5 -4
  510. package/src/controls/sync-button.tsx +18 -20
  511. package/src/controls/toggle-filter.tsx +5 -4
  512. package/src/controls/uncontrolled.ts +12 -0
  513. package/src/controls/view-tabs.tsx +3 -3
  514. package/src/controls-dates/date-range-picker.tsx +1 -1
  515. package/src/controls-dates/index.ts +0 -1
  516. package/src/dashboard/index.ts +7 -48
  517. package/src/dashboard/section.tsx +11 -40
  518. package/src/dashboard/stat-card.tsx +14 -16
  519. package/src/dashboard/widget-header.module.css +9 -7
  520. package/src/dev-dock.tsx +14 -27
  521. package/src/format/date.ts +2 -92
  522. package/src/format/index.ts +7 -53
  523. package/src/format/number.ts +3 -26
  524. package/src/format/shared.ts +2 -3
  525. package/src/forms/index.ts +0 -23
  526. package/src/guard/index.ts +36 -191
  527. package/src/guard/types.ts +6 -19
  528. package/src/index.ts +30 -282
  529. package/src/provider/create-router.tsx +138 -0
  530. package/src/provider/hotkeys.ts +70 -0
  531. package/src/provider/index.tsx +260 -230
  532. package/src/provider/lab-theme.ts +3 -2
  533. package/src/provider/route-transition.ts +199 -0
  534. package/src/provider/spotlight-actions.tsx +69 -0
  535. package/src/provider/test-router.tsx +25 -0
  536. package/src/query-client.ts +4 -22
  537. package/src/router-tanstack/index.ts +18 -112
  538. package/src/router-tanstack/nav.ts +28 -2
  539. package/src/router-tanstack/search-store.ts +9 -2
  540. package/src/router-tanstack/use-nav.ts +47 -13
  541. package/src/shell/app-mobile-nav.tsx +3 -2
  542. package/src/shell/app-sidebar.tsx +1 -1
  543. package/src/shell/aside-docking.ts +1 -1
  544. package/src/shell/index.tsx +61 -20
  545. package/src/shell/page-aside.tsx +47 -23
  546. package/src/shell/page-bar.tsx +3 -3
  547. package/src/shell/page-title.tsx +1 -1
  548. package/src/state.ts +11 -37
  549. package/src/styles.css +107 -7
  550. package/src/surfaces.ts +56 -151
  551. package/src/{data → table}/data-table-state.ts +5 -4
  552. package/src/{data → table}/data-table.tsx +170 -161
  553. package/src/table/index.ts +18 -0
  554. package/src/table/table-features.ts +102 -0
  555. package/src/telemetry.ts +110 -0
  556. package/src/theme/index.ts +22 -47
  557. package/src/theme/theme-toggle.module.css +30 -0
  558. package/src/theme/theme-toggle.module.css.d.ts +10 -0
  559. package/src/theme/theme-toggle.tsx +15 -43
  560. package/src/theme-lab/derive-controls.tsx +5 -3
  561. package/src/theme-lab/derive-state.ts +2 -1
  562. package/src/tokens/core.ts +1163 -0
  563. package/src/tokens/derive.ts +1 -1
  564. package/src/tokens/index.ts +14 -1181
  565. package/src/tokens/palette.ts +3 -7
  566. package/src/vite.ts +3 -3
  567. package/agent/templates/query-client.ts.tpl +0 -26
  568. package/dist/agent/adapter.d.ts +0 -150
  569. package/dist/agent/adapter.d.ts.map +0 -1
  570. package/dist/agent/adapter.js +0 -482
  571. package/dist/agent/use-agent-stream.d.ts +0 -35
  572. package/dist/agent/use-agent-stream.d.ts.map +0 -1
  573. package/dist/agent/use-agent-stream.js +0 -59
  574. package/dist/agent-chat/composer.d.ts.map +0 -1
  575. package/dist/agent-chat/index.d.ts +0 -31
  576. package/dist/agent-chat/index.d.ts.map +0 -1
  577. package/dist/agent-chat/index.js +0 -19
  578. package/dist/agent-chat/message-affordances.d.ts.map +0 -1
  579. package/dist/agent-chat/relative-time.d.ts.map +0 -1
  580. package/dist/agent-chat/thread-detail-panel.d.ts +0 -28
  581. package/dist/agent-chat/thread-detail-panel.d.ts.map +0 -1
  582. package/dist/agent-chat/thread-detail-panel.js +0 -115
  583. package/dist/agent-chat/thread-feed-row.d.ts.map +0 -1
  584. package/dist/agent-chat/thread-feed.d.ts.map +0 -1
  585. package/dist/agent-chat/thread-message.d.ts.map +0 -1
  586. package/dist/agent-chat/thread-outcome-card.d.ts.map +0 -1
  587. package/dist/agent-chat/thread-workspace.d.ts +0 -44
  588. package/dist/agent-chat/thread-workspace.d.ts.map +0 -1
  589. package/dist/agent-chat/thread-workspace.js +0 -153
  590. package/dist/agent-chat/tool-chip.d.ts.map +0 -1
  591. package/dist/agent-chat/use-keyboard-inset.d.ts.map +0 -1
  592. package/dist/agent-chat/virtualize.d.ts.map +0 -1
  593. package/dist/charts/cursor/resolve.d.ts +0 -75
  594. package/dist/charts/cursor/resolve.d.ts.map +0 -1
  595. package/dist/charts/cursor/resolve.js +0 -91
  596. package/dist/charts/cursor/scope.d.ts +0 -20
  597. package/dist/charts/cursor/scope.d.ts.map +0 -1
  598. package/dist/charts/cursor/scope.js +0 -20
  599. package/dist/charts/cursor/store.d.ts +0 -35
  600. package/dist/charts/cursor/store.d.ts.map +0 -1
  601. package/dist/charts/cursor/store.js +0 -25
  602. package/dist/charts/cursor/touch-pin.d.ts +0 -45
  603. package/dist/charts/cursor/touch-pin.d.ts.map +0 -1
  604. package/dist/charts/cursor/touch-pin.js +0 -79
  605. package/dist/charts/hooks/use-discrete-cursor.d.ts +0 -66
  606. package/dist/charts/hooks/use-discrete-cursor.d.ts.map +0 -1
  607. package/dist/charts/hooks/use-discrete-cursor.js +0 -198
  608. package/dist/charts/hooks/use-hysteresis.d.ts +0 -18
  609. package/dist/charts/hooks/use-hysteresis.d.ts.map +0 -1
  610. package/dist/charts/hooks/use-hysteresis.js +0 -12
  611. package/dist/charts/hooks/useBandPlot.d.ts +0 -145
  612. package/dist/charts/hooks/useBandPlot.d.ts.map +0 -1
  613. package/dist/charts/hooks/useBandPlot.js +0 -178
  614. package/dist/charts/hooks/useChartCursor.d.ts +0 -75
  615. package/dist/charts/hooks/useChartCursor.d.ts.map +0 -1
  616. package/dist/charts/hooks/useChartCursor.js +0 -200
  617. package/dist/charts/hooks/useChartSize.d.ts +0 -29
  618. package/dist/charts/hooks/useChartSize.d.ts.map +0 -1
  619. package/dist/charts/hooks/useChartSize.js +0 -8
  620. package/dist/charts/kinds/BandStrip.d.ts +0 -132
  621. package/dist/charts/kinds/BandStrip.d.ts.map +0 -1
  622. package/dist/charts/kinds/BandStrip.js +0 -241
  623. package/dist/charts/kinds/Bars.d.ts +0 -141
  624. package/dist/charts/kinds/Bars.d.ts.map +0 -1
  625. package/dist/charts/kinds/Bars.js +0 -343
  626. package/dist/charts/kinds/Donut.d.ts +0 -77
  627. package/dist/charts/kinds/Donut.d.ts.map +0 -1
  628. package/dist/charts/kinds/Donut.js +0 -245
  629. package/dist/charts/kinds/DualPanel.d.ts +0 -111
  630. package/dist/charts/kinds/DualPanel.d.ts.map +0 -1
  631. package/dist/charts/kinds/DualPanel.js +0 -471
  632. package/dist/charts/kinds/Heatmap.d.ts +0 -85
  633. package/dist/charts/kinds/Heatmap.d.ts.map +0 -1
  634. package/dist/charts/kinds/Heatmap.js +0 -243
  635. package/dist/charts/kinds/MirroredBars.d.ts +0 -98
  636. package/dist/charts/kinds/MirroredBars.d.ts.map +0 -1
  637. package/dist/charts/kinds/MirroredBars.js +0 -292
  638. package/dist/charts/kinds/MultiLine.d.ts +0 -87
  639. package/dist/charts/kinds/MultiLine.d.ts.map +0 -1
  640. package/dist/charts/kinds/MultiLine.js +0 -176
  641. package/dist/charts/kinds/StackedArea.d.ts +0 -73
  642. package/dist/charts/kinds/StackedArea.d.ts.map +0 -1
  643. package/dist/charts/kinds/StackedArea.js +0 -135
  644. package/dist/charts/kinds/ZonedLine.d.ts +0 -97
  645. package/dist/charts/kinds/ZonedLine.d.ts.map +0 -1
  646. package/dist/charts/kinds/ZonedLine.js +0 -155
  647. package/dist/charts/kinds/band-chrome.d.ts +0 -31
  648. package/dist/charts/kinds/band-chrome.d.ts.map +0 -1
  649. package/dist/charts/kinds/band-chrome.js +0 -71
  650. package/dist/charts/kinds/donut-layout.d.ts +0 -47
  651. package/dist/charts/kinds/donut-layout.d.ts.map +0 -1
  652. package/dist/charts/kinds/donut-layout.js +0 -52
  653. package/dist/charts/layout/auto-margin.d.ts +0 -122
  654. package/dist/charts/layout/auto-margin.d.ts.map +0 -1
  655. package/dist/charts/layout/auto-margin.js +0 -81
  656. package/dist/charts/layout/end-labels.d.ts +0 -64
  657. package/dist/charts/layout/end-labels.d.ts.map +0 -1
  658. package/dist/charts/layout/end-labels.js +0 -76
  659. package/dist/charts/layout/log-ticks.d.ts +0 -27
  660. package/dist/charts/layout/log-ticks.d.ts.map +0 -1
  661. package/dist/charts/layout/log-ticks.js +0 -42
  662. package/dist/charts/primitives/AreaGradient.d.ts +0 -18
  663. package/dist/charts/primitives/AreaGradient.d.ts.map +0 -1
  664. package/dist/charts/primitives/AreaGradient.js +0 -13
  665. package/dist/charts/primitives/Axes.d.ts +0 -83
  666. package/dist/charts/primitives/Axes.d.ts.map +0 -1
  667. package/dist/charts/primitives/Axes.js +0 -168
  668. package/dist/charts/primitives/CartesianChart.d.ts +0 -257
  669. package/dist/charts/primitives/CartesianChart.d.ts.map +0 -1
  670. package/dist/charts/primitives/CartesianChart.js +0 -549
  671. package/dist/charts/primitives/ChartCard.d.ts +0 -66
  672. package/dist/charts/primitives/ChartCard.d.ts.map +0 -1
  673. package/dist/charts/primitives/ChartCard.js +0 -143
  674. package/dist/charts/primitives/ChartFrame.d.ts +0 -156
  675. package/dist/charts/primitives/ChartFrame.d.ts.map +0 -1
  676. package/dist/charts/primitives/ChartFrame.js +0 -241
  677. package/dist/charts/primitives/ChartLegend.d.ts +0 -67
  678. package/dist/charts/primitives/ChartLegend.d.ts.map +0 -1
  679. package/dist/charts/primitives/ChartLegend.js +0 -292
  680. package/dist/charts/primitives/ChartPending.d.ts +0 -113
  681. package/dist/charts/primitives/ChartPending.d.ts.map +0 -1
  682. package/dist/charts/primitives/ChartPending.js +0 -117
  683. package/dist/charts/primitives/ChartTooltip.d.ts +0 -59
  684. package/dist/charts/primitives/ChartTooltip.d.ts.map +0 -1
  685. package/dist/charts/primitives/ChartTooltip.js +0 -197
  686. package/dist/charts/primitives/Crosshair.d.ts +0 -26
  687. package/dist/charts/primitives/Crosshair.d.ts.map +0 -1
  688. package/dist/charts/primitives/Crosshair.js +0 -23
  689. package/dist/charts/primitives/HatchPattern.d.ts +0 -31
  690. package/dist/charts/primitives/HatchPattern.d.ts.map +0 -1
  691. package/dist/charts/primitives/HatchPattern.js +0 -31
  692. package/dist/charts/primitives/HoverOverlay.d.ts +0 -45
  693. package/dist/charts/primitives/HoverOverlay.d.ts.map +0 -1
  694. package/dist/charts/primitives/HoverOverlay.js +0 -72
  695. package/dist/charts/primitives/HoverOverlay.module.css +0 -13
  696. package/dist/charts/primitives/HoverOverlay.module.css.d.ts +0 -8
  697. package/dist/charts/primitives/XZoneRects.d.ts +0 -54
  698. package/dist/charts/primitives/XZoneRects.d.ts.map +0 -1
  699. package/dist/charts/primitives/XZoneRects.js +0 -65
  700. package/dist/charts/primitives/ZoneRects.d.ts +0 -38
  701. package/dist/charts/primitives/ZoneRects.d.ts.map +0 -1
  702. package/dist/charts/primitives/ZoneRects.js +0 -30
  703. package/dist/charts/primitives/chart-card-context.d.ts +0 -33
  704. package/dist/charts/primitives/chart-card-context.d.ts.map +0 -1
  705. package/dist/charts/primitives/chart-card-context.js +0 -18
  706. package/dist/charts/primitives/chart-frame-layout.d.ts +0 -185
  707. package/dist/charts/primitives/chart-frame-layout.d.ts.map +0 -1
  708. package/dist/charts/primitives/chart-frame-layout.js +0 -175
  709. package/dist/charts/primitives/chart-layout.d.ts +0 -112
  710. package/dist/charts/primitives/chart-layout.d.ts.map +0 -1
  711. package/dist/charts/primitives/chart-layout.js +0 -175
  712. package/dist/charts/primitives/chart-tier.d.ts +0 -16
  713. package/dist/charts/primitives/chart-tier.d.ts.map +0 -1
  714. package/dist/charts/primitives/chart-tier.js +0 -44
  715. package/dist/charts/primitives/legend-disclosure.d.ts +0 -27
  716. package/dist/charts/primitives/legend-disclosure.d.ts.map +0 -1
  717. package/dist/charts/primitives/legend-disclosure.js +0 -153
  718. package/dist/charts/series.d.ts +0 -189
  719. package/dist/charts/series.d.ts.map +0 -1
  720. package/dist/charts/series.js +0 -84
  721. package/dist/charts/sparklines/BarSparkline.d.ts +0 -37
  722. package/dist/charts/sparklines/BarSparkline.d.ts.map +0 -1
  723. package/dist/charts/sparklines/BarSparkline.js +0 -53
  724. package/dist/charts/sparklines/LineSparkline.d.ts +0 -15
  725. package/dist/charts/sparklines/LineSparkline.d.ts.map +0 -1
  726. package/dist/charts/sparklines/LineSparkline.js +0 -51
  727. package/dist/charts/sparklines/index.d.ts +0 -3
  728. package/dist/charts/sparklines/index.d.ts.map +0 -1
  729. package/dist/charts/sparklines/index.js +0 -6
  730. package/dist/charts/utils/domain.d.ts +0 -35
  731. package/dist/charts/utils/domain.d.ts.map +0 -1
  732. package/dist/charts/utils/domain.js +0 -12
  733. package/dist/charts/utils/format.d.ts +0 -8
  734. package/dist/charts/utils/format.d.ts.map +0 -1
  735. package/dist/charts/utils/format.js +0 -20
  736. package/dist/charts/utils/measure-text.d.ts +0 -25
  737. package/dist/charts/utils/measure-text.d.ts.map +0 -1
  738. package/dist/charts/utils/measure-text.js +0 -64
  739. package/dist/charts/utils/ticks.d.ts +0 -103
  740. package/dist/charts/utils/ticks.d.ts.map +0 -1
  741. package/dist/charts/utils/ticks.js +0 -92
  742. package/dist/commands/overlays-mount.d.ts +0 -122
  743. package/dist/commands/overlays-mount.d.ts.map +0 -1
  744. package/dist/commands/overlays-mount.js +0 -125
  745. package/dist/commands/useCommandHotkeys.d.ts +0 -14
  746. package/dist/commands/useCommandHotkeys.d.ts.map +0 -1
  747. package/dist/commands/useCommandHotkeys.js +0 -45
  748. package/dist/content/article-actions.d.ts +0 -39
  749. package/dist/content/article-actions.d.ts.map +0 -1
  750. package/dist/content/article-actions.js +0 -28
  751. package/dist/content/article-card.d.ts +0 -58
  752. package/dist/content/article-card.d.ts.map +0 -1
  753. package/dist/content/article-card.js +0 -79
  754. package/dist/content/article-card.module.css +0 -111
  755. package/dist/content/mdx.d.ts +0 -20
  756. package/dist/content/mdx.d.ts.map +0 -1
  757. package/dist/content/mdx.js +0 -64
  758. package/dist/controls/compare-filter.d.ts +0 -42
  759. package/dist/controls/compare-filter.d.ts.map +0 -1
  760. package/dist/controls/compare-filter.js +0 -38
  761. package/dist/controls/slider-control.d.ts +0 -36
  762. package/dist/controls/slider-control.d.ts.map +0 -1
  763. package/dist/controls/slider-control.js +0 -64
  764. package/dist/dashboard/widget-grid.d.ts +0 -27
  765. package/dist/dashboard/widget-grid.d.ts.map +0 -1
  766. package/dist/dashboard/widget-grid.js +0 -40
  767. package/dist/dashboard/widget-grid.module.css +0 -69
  768. package/dist/data/data-table-state.d.ts.map +0 -1
  769. package/dist/data/data-table.d.ts.map +0 -1
  770. package/dist/data/query-branch.d.ts.map +0 -1
  771. package/dist/data/table.d.ts +0 -24
  772. package/dist/data/table.d.ts.map +0 -1
  773. package/dist/data/virtual-list.d.ts +0 -133
  774. package/dist/data/virtual-list.d.ts.map +0 -1
  775. package/dist/data/virtual-list.js +0 -157
  776. package/dist/data/virtual.d.ts +0 -21
  777. package/dist/data/virtual.d.ts.map +0 -1
  778. package/dist/data/virtual.js +0 -4
  779. package/dist/forms/FormErrorSummary.d.ts +0 -30
  780. package/dist/forms/FormErrorSummary.d.ts.map +0 -1
  781. package/dist/forms/FormErrorSummary.js +0 -30
  782. package/dist/forms/use-field-array.d.ts +0 -60
  783. package/dist/forms/use-field-array.d.ts.map +0 -1
  784. package/dist/forms/use-field-array.js +0 -20
  785. package/dist/forms/use-form-draft.d.ts +0 -76
  786. package/dist/forms/use-form-draft.d.ts.map +0 -1
  787. package/dist/forms/use-form-draft.js +0 -92
  788. package/dist/forms/use-form-submit.d.ts +0 -88
  789. package/dist/forms/use-form-submit.d.ts.map +0 -1
  790. package/dist/forms/use-form-submit.js +0 -63
  791. package/dist/notifications/bell.d.ts.map +0 -1
  792. package/dist/notifications/center.d.ts.map +0 -1
  793. package/dist/notifications/define-notifications.d.ts.map +0 -1
  794. package/dist/notifications/index.d.ts +0 -40
  795. package/dist/notifications/index.d.ts.map +0 -1
  796. package/dist/notifications/index.js +0 -54
  797. package/dist/notifications/mount-guard.d.ts +0 -8
  798. package/dist/notifications/mount-guard.d.ts.map +0 -1
  799. package/dist/notifications/mount-guard.js +0 -18
  800. package/dist/notifications/notify.d.ts +0 -149
  801. package/dist/notifications/notify.d.ts.map +0 -1
  802. package/dist/notifications/notify.js +0 -171
  803. package/dist/notifications/store.d.ts.map +0 -1
  804. package/dist/provider/connectivity-indicator.d.ts +0 -13
  805. package/dist/provider/connectivity-indicator.d.ts.map +0 -1
  806. package/dist/provider/connectivity-indicator.js +0 -205
  807. package/dist/provider/connectivity-provider.d.ts +0 -7
  808. package/dist/provider/connectivity-provider.d.ts.map +0 -1
  809. package/dist/provider/connectivity-provider.js +0 -128
  810. package/dist/provider/connectivity-types.d.ts +0 -36
  811. package/dist/provider/connectivity-types.d.ts.map +0 -1
  812. package/dist/provider/use-connectivity.d.ts +0 -3
  813. package/dist/provider/use-connectivity.d.ts.map +0 -1
  814. package/dist/provider/use-connectivity.js +0 -14
  815. package/dist/query-devtools.d.ts +0 -21
  816. package/dist/query-devtools.d.ts.map +0 -1
  817. package/dist/query-devtools.js +0 -13
  818. package/src/agent/adapter.ts +0 -945
  819. package/src/agent/use-agent-stream.ts +0 -154
  820. package/src/agent-chat/index.ts +0 -54
  821. package/src/agent-chat/thread-detail-panel.tsx +0 -201
  822. package/src/agent-chat/thread-workspace.tsx +0 -279
  823. package/src/charts/cursor/resolve.ts +0 -198
  824. package/src/charts/cursor/scope.tsx +0 -35
  825. package/src/charts/cursor/store.ts +0 -58
  826. package/src/charts/cursor/touch-pin.ts +0 -138
  827. package/src/charts/hooks/use-discrete-cursor.ts +0 -335
  828. package/src/charts/hooks/use-hysteresis.ts +0 -26
  829. package/src/charts/hooks/useBandPlot.ts +0 -383
  830. package/src/charts/hooks/useChartCursor.ts +0 -367
  831. package/src/charts/hooks/useChartSize.ts +0 -35
  832. package/src/charts/kinds/BandStrip.tsx +0 -449
  833. package/src/charts/kinds/Bars.tsx +0 -589
  834. package/src/charts/kinds/Donut.tsx +0 -368
  835. package/src/charts/kinds/DualPanel.tsx +0 -712
  836. package/src/charts/kinds/Heatmap.tsx +0 -396
  837. package/src/charts/kinds/MirroredBars.tsx +0 -469
  838. package/src/charts/kinds/MultiLine.tsx +0 -322
  839. package/src/charts/kinds/StackedArea.tsx +0 -267
  840. package/src/charts/kinds/ZonedLine.tsx +0 -301
  841. package/src/charts/kinds/band-chrome.tsx +0 -91
  842. package/src/charts/kinds/donut-layout.ts +0 -82
  843. package/src/charts/layout/auto-margin.ts +0 -242
  844. package/src/charts/layout/end-labels.ts +0 -135
  845. package/src/charts/layout/log-ticks.ts +0 -90
  846. package/src/charts/primitives/AreaGradient.tsx +0 -26
  847. package/src/charts/primitives/Axes.tsx +0 -271
  848. package/src/charts/primitives/CartesianChart.tsx +0 -1002
  849. package/src/charts/primitives/ChartCard.tsx +0 -280
  850. package/src/charts/primitives/ChartFrame.tsx +0 -493
  851. package/src/charts/primitives/ChartLegend.tsx +0 -422
  852. package/src/charts/primitives/ChartPending.tsx +0 -243
  853. package/src/charts/primitives/ChartTooltip.tsx +0 -265
  854. package/src/charts/primitives/Crosshair.tsx +0 -43
  855. package/src/charts/primitives/HatchPattern.tsx +0 -55
  856. package/src/charts/primitives/HoverOverlay.module.css +0 -13
  857. package/src/charts/primitives/HoverOverlay.module.css.d.ts +0 -8
  858. package/src/charts/primitives/HoverOverlay.tsx +0 -109
  859. package/src/charts/primitives/XZoneRects.tsx +0 -132
  860. package/src/charts/primitives/ZoneRects.tsx +0 -67
  861. package/src/charts/primitives/chart-card-context.ts +0 -46
  862. package/src/charts/primitives/chart-frame-layout.ts +0 -430
  863. package/src/charts/primitives/chart-layout.ts +0 -318
  864. package/src/charts/primitives/chart-tier.tsx +0 -76
  865. package/src/charts/primitives/legend-disclosure.tsx +0 -178
  866. package/src/charts/series.ts +0 -276
  867. package/src/charts/sparklines/BarSparkline.tsx +0 -94
  868. package/src/charts/sparklines/LineSparkline.tsx +0 -71
  869. package/src/charts/sparklines/index.ts +0 -2
  870. package/src/charts/theme.tsx +0 -53
  871. package/src/charts/utils/domain.ts +0 -41
  872. package/src/charts/utils/format.ts +0 -19
  873. package/src/charts/utils/measure-text.ts +0 -109
  874. package/src/charts/utils/ticks.ts +0 -239
  875. package/src/commands/overlays-mount.tsx +0 -333
  876. package/src/commands/useCommandHotkeys.ts +0 -114
  877. package/src/content/article-actions.ts +0 -72
  878. package/src/content/article-card.module.css +0 -111
  879. package/src/content/article-card.tsx +0 -174
  880. package/src/content/mdx.tsx +0 -127
  881. package/src/controls/compare-filter.tsx +0 -81
  882. package/src/controls/slider-control.tsx +0 -159
  883. package/src/dashboard/widget-grid.module.css +0 -69
  884. package/src/dashboard/widget-grid.tsx +0 -146
  885. package/src/data/table.ts +0 -39
  886. package/src/data/virtual-list.tsx +0 -356
  887. package/src/data/virtual.ts +0 -26
  888. package/src/forms/FormErrorSummary.tsx +0 -71
  889. package/src/forms/use-field-array.ts +0 -94
  890. package/src/forms/use-form-draft.ts +0 -262
  891. package/src/forms/use-form-submit.ts +0 -199
  892. package/src/notifications/index.ts +0 -88
  893. package/src/notifications/mount-guard.ts +0 -38
  894. package/src/notifications/notify.tsx +0 -379
  895. package/src/provider/connectivity-indicator.tsx +0 -267
  896. package/src/provider/connectivity-provider.tsx +0 -189
  897. package/src/provider/connectivity-types.ts +0 -42
  898. package/src/provider/use-connectivity.ts +0 -26
  899. package/src/query-devtools.tsx +0 -40
  900. /package/dist/{agent-chat → agent/chat}/message-affordances.js +0 -0
  901. /package/dist/{agent-chat → agent/chat}/relative-time.d.ts +0 -0
  902. /package/dist/{agent-chat → agent/chat}/relative-time.js +0 -0
  903. /package/dist/{agent-chat → agent/chat}/use-keyboard-inset.d.ts +0 -0
  904. /package/dist/{agent-chat → agent/chat}/virtualize.js +0 -0
  905. /package/dist/{notifications → commands/notifications}/bell.js +0 -0
  906. /package/dist/{notifications → commands/notifications}/define-notifications.js +0 -0
  907. /package/dist/{provider/connectivity-types.js → controls/uncontrolled.js} +0 -0
  908. /package/dist/{data → table}/data-table.module.css +0 -0
  909. /package/dist/{data → table}/data-table.module.css.d.ts +0 -0
  910. /package/dist/{data → table}/query-branch.d.ts +0 -0
  911. /package/dist/{data → table}/query-branch.js +0 -0
  912. /package/src/{agent-chat → agent/chat}/relative-time.ts +0 -0
  913. /package/src/{data → table}/data-table.module.css +0 -0
  914. /package/src/{data → table}/data-table.module.css.d.ts +0 -0
  915. /package/src/{data → table}/query-branch.ts +0 -0
@@ -1,945 +0,0 @@
1
- /**
2
- * ThreadsStoreAdapter — the async, server-backed seam behind ThreadsStore.
3
- *
4
- * `createThreadsStore` (./thread) is localStorage with silent ring-buffer caps. That is the right
5
- * default for a demo and the wrong one for a real transcript: a consumer whose threads live in
6
- * Postgres has, until now, had to fork the store outright. This module opens that seam.
7
- *
8
- * Three pieces:
9
- * 1. `ThreadsStoreAdapter` — the async contract a consumer implements against its own backend.
10
- * 2. `createAdapterThreadsStore` — wraps one into the SYNCHRONOUS `ThreadsStore` that every
11
- * component in `basalt-ui/agent-chat` already takes. Optimistic local state, revalidate on
12
- * success, roll back on rejection.
13
- * 3. `threadsStoreAdapterContract` — a test-runner-agnostic conformance suite the consumer runs
14
- * against its own adapter. It ships in the published package, so it imports NO test framework.
15
- *
16
- * @example
17
- * import { createAdapterThreadsStore } from 'basalt-ui/agent'
18
- *
19
- * // Call once at module scope, exactly like createThreadsStore:
20
- * export const useThreads = createAdapterThreadsStore(postgresThreadsAdapter(api))
21
- *
22
- * // In a component — same shape as the localStorage store, plus hydrated/error:
23
- * const { threads, hydrated, error, create, appendMessage } = useThreads()
24
- */
25
- import { useEffect, useSyncExternalStore } from 'react'
26
- import type { ChatMessage } from './history'
27
- import { mintThreadId } from './id'
28
- import type { AgentOutcome } from './outcome'
29
- import type { AgentPart } from './parts'
30
- import type { AgentThread, ThreadStatus, ThreadsStore } from './thread'
31
-
32
- // ── ThreadsStoreAdapter ───────────────────────────────────────────────────────
33
-
34
- /**
35
- * The async persistence contract behind a `ThreadsStore`. Every method may reject; the wrapping
36
- * store treats a rejection as "the optimistic edit did not happen" and rolls it back.
37
- *
38
- * Implement this against whatever owns the transcript (Postgres, an HTTP API, IndexedDB) and
39
- * verify it with `threadsStoreAdapterContract` — the suite pins the parts of the contract that
40
- * prose cannot enforce, chiefly `appendMessage`'s idempotency.
41
- *
42
- * ## What `createAdapterThreadsStore` guarantees you
43
- *
44
- * **Per-thread write ordering.** Writes carrying the same thread id reach this adapter strictly in
45
- * issue order, each one awaiting the previous, so a write that depends on a row existing is never
46
- * issued before that row's `createThread` has RESOLVED. This matters because the primary send path
47
- * is one synchronous block — `create()`, `select()`, `markRead()`, `appendMessage()`,
48
- * `setStatus()` — which without ordering would hand you three dependent writes against a row you
49
- * have not created yet. You therefore never have to tolerate (or invent an upsert for) an
50
- * out-of-order write. Writes for DIFFERENT thread ids stay concurrent and carry no ordering
51
- * guarantee relative to each other.
52
- *
53
- * ## What it requires of you
54
- *
55
- * **Read-after-write consistency on `listThreads`.** A `listThreads` STARTED after a write has
56
- * resolved must reflect that write. The store keeps a write's optimistic patch applied until such
57
- * a list lands, then drops it; an eventually-consistent backend (a stale read replica, a cached
58
- * list) will therefore flash the pre-write state back into the UI. `threadsStoreAdapterContract`
59
- * pins this.
60
- *
61
- * @example
62
- * const postgresThreadsAdapter = (api: Api): ThreadsStoreAdapter => ({
63
- * listThreads: (signal) => api.threads.get({ fetch: { signal } }),
64
- * loadThread: (id, signal) => api.threads({ id }).get({ fetch: { signal } }),
65
- * createThread: ({ id, meta }) => api.threads.post({ id, meta }),
66
- * // ...
67
- * })
68
- */
69
- export type ThreadsStoreAdapter<TPart = AgentPart> = {
70
- /** All threads, NEWEST-FIRST (same ordering contract as `ThreadsStore.threads`). */
71
- readonly listThreads: (signal?: AbortSignal) => Promise<readonly AgentThread<TPart>[]>
72
- /** One thread by id, or `null` when it does not exist. Must not throw on an unknown id. */
73
- readonly loadThread: (id: string, signal?: AbortSignal) => Promise<AgentThread<TPart> | null>
74
- /**
75
- * Materialize a new thread under the CLIENT-MINTED `id`. The id is minted client-side because
76
- * `ThreadsStore.create()` returns synchronously — there is no await to hand a server id back
77
- * through. The created thread starts empty: `messages: []`, `outcome: null`,
78
- * `status: 'pending'`, `read: false`, with `createdAt`/`updatedAt` set.
79
- */
80
- readonly createThread: (i: {
81
- readonly id: string
82
- readonly meta?: Record<string, unknown>
83
- }) => Promise<void>
84
- /**
85
- * CONTRACT: idempotent on `message.id`. The id is client-minted and is the ONLY idempotency
86
- * key — a retried or double-fired write with the same id must be a no-op, not a second row.
87
- */
88
- readonly appendMessage: (i: {
89
- readonly threadId: string
90
- readonly message: ChatMessage<TPart>
91
- }) => Promise<void>
92
- readonly setStatus: (i: {
93
- readonly threadId: string
94
- readonly status: ThreadStatus
95
- }) => Promise<void>
96
- readonly setOutcome: (i: {
97
- readonly threadId: string
98
- readonly outcome: AgentOutcome
99
- }) => Promise<void>
100
- /** `token: undefined` CLEARS the token — the stored thread must then read `resumeToken` as absent. */
101
- readonly setResumeToken: (i: {
102
- readonly threadId: string
103
- readonly token: string | undefined
104
- }) => Promise<void>
105
- readonly markRead: (threadId: string) => Promise<void>
106
- /** Removing an unknown id is a no-op, not an error. */
107
- readonly removeThread: (threadId: string) => Promise<void>
108
- }
109
-
110
- // ── createAdapterThreadsStore ─────────────────────────────────────────────────
111
-
112
- export type AdapterThreadsStoreOptions = {
113
- /**
114
- * Re-list threads whenever the window regains focus. Off by default — a background tab that
115
- * refocuses should not surprise a consumer with a network round-trip it did not ask for.
116
- * @default false
117
- */
118
- readonly revalidateOnFocus?: boolean
119
- }
120
-
121
- /**
122
- * An optimistic patch over the last-confirmed server list.
123
- *
124
- * Every patch MUST be idempotent: on a successful write the patch stays applied while the
125
- * follow-up `listThreads` lands, so it is briefly re-applied on top of a server list that already
126
- * contains its effect. `appendMessage`'s patch therefore appends-if-absent, `create`'s
127
- * prepends-if-absent, and the setters are plain assignments. Without that property the store
128
- * would double-render a message for one frame after every successful append.
129
- *
130
- * Idempotent means TIME-INDEPENDENT too: a patch is re-applied on every store event, including
131
- * ones it has nothing to do with (a `select()` while a write is in flight), so it must never read
132
- * the clock inside `apply`. Each action captures `Date.now()` once, at issue time, and closes over
133
- * it — otherwise an unrelated `select()` would re-stamp `updatedAt`. `apply` is additionally
134
- * memoized on input identity (see `memoizeApply`) so re-application is referentially stable and
135
- * cannot defeat a consumer's `React.memo`/`useMemo` over `threads`.
136
- */
137
- type ThreadPatch<TPart> = {
138
- readonly apply: (threads: readonly AgentThread<TPart>[]) => AgentThread<TPart>[]
139
- /**
140
- * The patch's effect on the LOCAL selection, when it has one (`remove`/`clear`). Kept in the
141
- * patch rather than assigned eagerly so it rolls back with the rows on a rejection.
142
- */
143
- readonly applyActive?: (activeId: string | null) => string | null
144
- /**
145
- * The load sequence number that must land in `base` before this patch may be dropped, or `null`
146
- * while the write is still in flight. See `prune` — this is the whole "no gap" invariant.
147
- */
148
- confirmSeq: number | null
149
- }
150
-
151
- /**
152
- * Wraps an async `ThreadsStoreAdapter` into the synchronous `ThreadsStore` every component takes.
153
- *
154
- * Call this ONCE per module (not inside a component), exactly like `createThreadsStore`. All
155
- * components calling the returned hook share one registry, backed by `useSyncExternalStore`.
156
- *
157
- * Semantics:
158
- * - **Optimistic.** Every mutation applies locally first, so `create()` can return an id
159
- * synchronously and the UI never waits on a round-trip.
160
- * - **Ordered per thread.** Writes for one thread id reach the adapter in issue order, each
161
- * awaiting the previous — so the dependent writes of a `create()`-then-send land after
162
- * `createThread` has resolved. Different threads stay concurrent. See `ThreadsStoreAdapter`.
163
- * - **Revalidate on success, coalesced.** When a write resolves the store re-lists; the patch is
164
- * dropped only once a list has actually refreshed the confirmed base, so server-side derivations
165
- * win over the local guess with no gap in between. Writes that resolve while a list is already in
166
- * flight share ONE follow-up instead of each restarting it, so a burst of writes costs at most
167
- * two round trips (the in-flight one, plus one follow-up), not one per write — see `revalidate`.
168
- * - **Roll back on rejection.** A rejected write's patch is discarded, the rejection is surfaced
169
- * on `error`, and the store re-lists to converge on whatever the server really did. Patches are
170
- * tracked individually, so a failing write rolls back only itself and not whatever else was in
171
- * flight beside it. A rejected `create()` additionally drops every later single-thread write for
172
- * that id (`markRead`/`appendMessage`/`setStatus`/… — both the ones chained directly behind it and
173
- * the run's completion writes at stream end) instead of letting each fail independently against a
174
- * row that no longer exists — see `failedCreateIds`. Without that, the create's own error is
175
- * overwritten by a run of "unknown thread" rejections that name nothing a caller can act on.
176
- * - **`hydrated` means "a `listThreads` has succeeded"**, not "a load has been attempted". A
177
- * store that is `!hydrated` with a non-undefined `error` failed to load; check both.
178
- * - **`error` latches the most recent failure until something disproves it.** A successful
179
- * `listThreads` clears a LOAD failure; it leaves a WRITE failure alone, since a working list is
180
- * no evidence the rejected write landed. A write clears a latched write failure only if it was
181
- * issued after that failure (a retry) — never the writes that were already queued behind it.
182
- *
183
- * `select` is local-only — which thread is active is UI state, not something the adapter owns.
184
- * `clear()` maps onto `removeThread` for each known thread, since the adapter has no bulk delete.
185
- *
186
- * @example
187
- * export const useThreads = createAdapterThreadsStore(myAdapter, { revalidateOnFocus: true })
188
- */
189
- export function createAdapterThreadsStore<TPart = AgentPart>(
190
- adapter: ThreadsStoreAdapter<TPart>,
191
- opts?: AdapterThreadsStoreOptions,
192
- ): () => ThreadsStore<TPart> {
193
- const revalidateOnFocus = opts?.revalidateOnFocus ?? false
194
-
195
- type Snapshot = {
196
- readonly threads: AgentThread<TPart>[]
197
- readonly activeId: string | null
198
- readonly hydrated: boolean
199
- readonly error: unknown
200
- }
201
-
202
- const EMPTY: AgentThread<TPart>[] = []
203
- // Stable identity for the SSR snapshot — useSyncExternalStore requires getServerSnapshot to be
204
- // referentially stable across calls or React loops on it.
205
- const SERVER_SNAPSHOT: Snapshot = {
206
- threads: EMPTY,
207
- activeId: null,
208
- hydrated: false,
209
- error: undefined,
210
- }
211
-
212
- /** Last confirmed server list. Optimistic patches are layered on top, never merged into it. */
213
- let base: AgentThread<TPart>[] = EMPTY
214
- /**
215
- * The `loadSeq` of the `listThreads` that produced the current `base`. Patch lifetime is keyed
216
- * to this number rather than to "a revalidate returned" — see `prune`.
217
- */
218
- let baseSeq = 0
219
- /** Confirmed selection. The patched value (see `applyActive`) is what the snapshot exposes. */
220
- let activeId: string | null = null
221
- let hydrated = false
222
- let error: unknown
223
- /**
224
- * Bumped on every failure. A write clears `error` only when this token still matches the value
225
- * it captured at ISSUE time — i.e. only when the write was issued AFTER the latched failure
226
- * happened. Without that, the writes queued behind a failing one (the send path issues four in
227
- * one block) would clear the failure the consumer never got to see; with it, a genuine retry
228
- * still clears it.
229
- */
230
- let errorToken = 0
231
- /**
232
- * Whether the latched `error` came from a WRITE. A successful `listThreads` is evidence the load
233
- * path works, not that the rejected write landed — so it clears a load error and leaves a write
234
- * error alone.
235
- */
236
- let errorFromWrite = false
237
- const pending: ThreadPatch<TPart>[] = []
238
- let snapshot: Snapshot = SERVER_SNAPSHOT
239
-
240
- const listeners = new Set<() => void>()
241
-
242
- function recompute(): void {
243
- let threads: AgentThread<TPart>[] = base
244
- let active = activeId
245
- for (const patch of pending) {
246
- threads = patch.apply(threads)
247
- if (patch.applyActive !== undefined) active = patch.applyActive(active)
248
- }
249
- snapshot = { threads, activeId: active, hydrated, error }
250
- for (const listener of listeners) listener()
251
- }
252
-
253
- /**
254
- * Memoizes a patch's `apply` on INPUT IDENTITY. `recompute` re-runs the whole pending stack on
255
- * every store event, so without this an unrelated `select()` would mint fresh thread objects for
256
- * every in-flight write and defeat referential-equality memoization downstream. Sound because
257
- * `apply` is pure: the actions capture their timestamp at issue time, never inside `apply`.
258
- */
259
- function memoizeApply(apply: ThreadPatch<TPart>['apply']): ThreadPatch<TPart>['apply'] {
260
- let lastIn: readonly AgentThread<TPart>[] | undefined
261
- let lastOut: AgentThread<TPart>[] | undefined
262
- return (threads): AgentThread<TPart>[] => {
263
- if (lastOut !== undefined && threads === lastIn) return lastOut
264
- lastIn = threads
265
- lastOut = apply(threads)
266
- return lastOut
267
- }
268
- }
269
-
270
- function subscribe(listener: () => void): () => void {
271
- listeners.add(listener)
272
- return (): void => {
273
- listeners.delete(listener)
274
- }
275
- }
276
-
277
- function getSnapshot(): Snapshot {
278
- return snapshot
279
- }
280
-
281
- function getServerSnapshot(): Snapshot {
282
- return SERVER_SNAPSHOT
283
- }
284
-
285
- // ── revalidation ────────────────────────────────────────────────────────────
286
-
287
- let loadController: AbortController | null = null
288
- let loadSeq = 0
289
- /**
290
- * A follow-up load already earmarked to start the moment the in-flight one finishes, or `null`
291
- * when none is queued. See `revalidate` — this is the coalescing slot every write-triggered
292
- * revalidate() call shares while a load is in flight, instead of each minting (and aborting) its
293
- * own. INVARIANT: non-null only while a load is in flight (`loadController !== null`) — every
294
- * place that ends the in-flight load (a landing, in `startLoad`'s `finally`; a forced supersede,
295
- * in `forceRevalidate`; unmount, in `release`) also clears this, so a stale seq can never be
296
- * handed out to a write that arrives after the load it was queued behind is long gone.
297
- */
298
- let queuedSeq: number | null = null
299
-
300
- /**
301
- * Drops every patch whose proving load has landed in `base`.
302
- *
303
- * INVARIANT: apart from a rollback, this is the ONLY place a patch is ever dropped — a patch
304
- * outlives its write until a `listThreads` ISSUED AFTER that write resolved has actually
305
- * refreshed `base`. Dropping on "some revalidate returned" is not enough: with two writes in
306
- * flight (the normal case on the send path) the earlier revalidate is SUPERSEDED and returns
307
- * without touching `base`, so dropping there would blank the confirmed write's effect out of
308
- * the UI for the whole of the surviving round-trip. Because `loadSeq` is global and monotonic,
309
- * `baseSeq >= confirmSeq` proves the landed list was started after the write resolved, and
310
- * `ThreadsStoreAdapter`'s read-after-write clause makes that list contain the write.
311
- */
312
- function prune(): void {
313
- const kept: ThreadPatch<TPart>[] = []
314
- for (const patch of pending) {
315
- if (patch.confirmSeq !== null && patch.confirmSeq <= baseSeq) {
316
- // Retiring a confirmed patch COMMITS its local-only effect: the rows come back from the
317
- // server, but the selection does not, so dropping a confirmed `remove`/`clear` without
318
- // folding `applyActive` in would resurrect a selection pointing at a deleted thread.
319
- if (patch.applyActive !== undefined) activeId = patch.applyActive(activeId)
320
- continue
321
- }
322
- kept.push(patch)
323
- }
324
- if (kept.length !== pending.length) pending.splice(0, pending.length, ...kept)
325
- }
326
-
327
- /**
328
- * Runs one `listThreads` tagged with `seq` and installs it as `base` unless something has
329
- * superseded it in the meantime (`forceRevalidate` swapping `loadController` out from under it).
330
- * On landing (success, failure, or discard), starts the coalesced follow-up in `queuedSeq` — if
331
- * any writes arrived while this load was in flight, exactly one more load runs for all of them.
332
- */
333
- function startLoad(seq: number): void {
334
- const controller = new AbortController()
335
- loadController = controller
336
- void (async (): Promise<void> => {
337
- try {
338
- const next = await adapter.listThreads(controller.signal)
339
- // A forced revalidate replaced `loadController` while this was in flight — stale, ignore.
340
- if (loadController !== controller) return
341
- base = [...next]
342
- baseSeq = seq
343
- hydrated = true
344
- // A working list says nothing about a rejected WRITE — see `errorFromWrite`.
345
- if (!errorFromWrite) error = undefined
346
- prune()
347
- recompute()
348
- } catch (cause) {
349
- // An abort is a deliberate cancellation (unmount, or a forced supersede) — not a store
350
- // error; nor is a stale rejection landing after something else already replaced us.
351
- if (controller.signal.aborted || loadController !== controller) return
352
- error = cause
353
- errorToken += 1
354
- errorFromWrite = false
355
- recompute()
356
- } finally {
357
- if (loadController === controller) {
358
- loadController = null
359
- if (queuedSeq !== null) {
360
- const next = queuedSeq
361
- queuedSeq = null
362
- startLoad(next)
363
- }
364
- }
365
- }
366
- })()
367
- }
368
-
369
- /**
370
- * Requests a revalidate and returns the seq of the load that will PROVE it — the load a caller
371
- * must wait for (`baseSeq >= this seq`) before treating its write as confirmed, per `ThreadPatch`.
372
- *
373
- * COALESCES rather than restarts. A `listThreads` already in flight started before this call, so
374
- * per `ThreadsStoreAdapter`'s read-after-write clause it is not guaranteed to reflect a write
375
- * that only just resolved — aborting it and starting an identical one is therefore not a
376
- * shortcut, and under back-to-back writes (a streaming append firing every few ms) it means NO
377
- * load ever survives long enough to land: `base` never refreshes and no patch ever retires (this
378
- * was the bug). Instead, while a load is in flight, every caller shares the ONE follow-up load
379
- * already earmarked to start the instant the in-flight one finishes (`queuedSeq`) — that
380
- * follow-up necessarily starts after every write that arrived while the in-flight load was
381
- * running, so a single round trip proves all of them at once.
382
- */
383
- function revalidate(): number {
384
- if (loadController !== null) {
385
- queuedSeq ??= ++loadSeq
386
- return queuedSeq
387
- }
388
- const seq = ++loadSeq
389
- startLoad(seq)
390
- return seq
391
- }
392
-
393
- /**
394
- * Like `revalidate`, but ABORTS an in-flight load instead of coalescing behind it — for the two
395
- * cases that genuinely want the freshest list right now rather than whatever the in-flight load
396
- * happens to land: the initial mount (via `revalidate`, where nothing is in flight yet so the two
397
- * are equivalent) and `revalidateOnFocus`. Drops any coalesced follow-up too: the fresh load this
398
- * starts necessarily begins after every write that follow-up would have proven, so `baseSeq`
399
- * still ends up `>=` their `confirmSeq` once it lands — `prune`'s check was always `>=`, not `==`.
400
- */
401
- function forceRevalidate(): number {
402
- loadController?.abort()
403
- queuedSeq = null
404
- const seq = ++loadSeq
405
- startLoad(seq)
406
- return seq
407
- }
408
-
409
- // ── per-thread write ordering ───────────────────────────────────────────────
410
-
411
- /**
412
- * Tail of the in-flight write chain per thread id. `ThreadsStore`'s send path issues
413
- * `create` → `markRead` → `appendMessage` → `setStatus` in ONE synchronous block, so without a
414
- * chain a server-backed adapter would get three writes against a row whose `createThread` has
415
- * not resolved yet, reject them, and roll the user's own message back out of the transcript.
416
- */
417
- const chains = new Map<string, Promise<void>>()
418
-
419
- /**
420
- * Thread ids whose `createThread` has rejected and been rolled back. The send path issues
421
- * `create()` → `markRead()` → `appendMessage()` → `setStatus()` in one synchronous block, all
422
- * chained behind the same per-thread queue above — so when `createThread` rejects, the three
423
- * dependent writes queued right behind it are guaranteed to reach the adapter next, each against
424
- * a row that was never materialized. Left alone, each of those independently rejects with its own
425
- * "unknown thread" error and overwrites the one that actually explains what happened (the
426
- * create's), so the user ends up staring at the last cascade error instead of the root cause.
427
- *
428
- * Recording the id here lets `mutate` recognize a single-thread write against a rolled-back
429
- * create and drop it silently instead of running a doomed commit — see `mutate`'s `isCreate`
430
- * guard.
431
- *
432
- * Entries are NEVER removed, deliberately. The cascade is not confined to the synchronous block:
433
- * `useAgentThreadRuns`' COMPLETION path (`appendMessage(assistant)` → `setOutcome` → `setStatus`
434
- * → `setResumeToken`) fires when the stream ends, long after this thread's write chain has
435
- * drained — same rolled-back row, same "unknown thread" rejections, and landing at exactly the
436
- * moment the user is watching the thread. Clearing the entry when the chain drains would fix the
437
- * first few hundred milliseconds and hand the cascade straight back at stream end. Retaining it
438
- * is safe and bounded: `create()` always mints its own id (`mintThreadId`, never caller-supplied),
439
- * so an id in here can never be re-created and the set only grows by one per FAILED create in a
440
- * session.
441
- */
442
- const failedCreateIds = new Set<string>()
443
-
444
- /** `task` must never reject, so a failed write cannot poison the chain behind it. */
445
- function enqueue(threadIds: readonly string[], task: () => Promise<void>): void {
446
- const ids = [...new Set(threadIds)]
447
- const prior = ids.map((id) => chains.get(id)).filter((p): p is Promise<void> => p !== undefined)
448
- const after = prior.length === 0 ? Promise.resolve() : Promise.all(prior).then(() => undefined)
449
- const run = after.then(task)
450
- for (const id of ids) chains.set(id, run)
451
- void run.finally(() => {
452
- // Only the tail clears its own entry, so a later write chained behind it is not orphaned.
453
- // `failedCreateIds` is deliberately NOT cleared here — see its doc.
454
- for (const id of ids) if (chains.get(id) === run) chains.delete(id)
455
- })
456
- }
457
-
458
- // ── optimistic mutation pipeline ────────────────────────────────────────────
459
-
460
- function drop(patch: ThreadPatch<TPart>): void {
461
- const index = pending.indexOf(patch)
462
- if (index !== -1) pending.splice(index, 1)
463
- }
464
-
465
- function mutate(i: {
466
- /** Threads this write touches — its ordering key(s). Empty only for a `clear()` of nothing. */
467
- readonly threadIds: readonly string[]
468
- readonly apply: ThreadPatch<TPart>['apply']
469
- readonly applyActive?: (activeId: string | null) => string | null
470
- readonly commit: () => Promise<void>
471
- /**
472
- * True only for `create()`'s own write. On rejection, marks its thread id as failed so the
473
- * dependent writes chained behind it in the same synchronous send-path block recognize the row
474
- * was rolled back — see `failedCreateIds`.
475
- */
476
- readonly isCreate?: boolean
477
- }): void {
478
- const patch: ThreadPatch<TPart> = {
479
- apply: memoizeApply(i.apply),
480
- ...(i.applyActive !== undefined ? { applyActive: i.applyActive } : {}),
481
- confirmSeq: null,
482
- }
483
- pending.push(patch)
484
- const tokenAtIssue = errorToken
485
- recompute()
486
- enqueue(i.threadIds, async (): Promise<void> => {
487
- // A single-thread write against a create that already rolled back cannot succeed — the row
488
- // was never materialized, and running it only manufactures a second "unknown thread" error
489
- // that would bury the create's own. This covers both the writes chained directly behind the
490
- // create and the run's later completion writes (see `failedCreateIds`). Scoped to
491
- // single-thread writes deliberately:
492
- // `clear()`'s multi-id commit already has its own per-id fan-out (`Promise.allSettled`) and
493
- // `removeThread` is contractually a no-op on an unknown id anyway, so folding this check into
494
- // a multi-id write would risk dropping legitimate deletes for OTHER, healthy threads in the
495
- // same batch just because one id in it happened to be orphaned.
496
- const [soleThreadId] = i.threadIds
497
- if (
498
- !i.isCreate &&
499
- i.threadIds.length === 1 &&
500
- soleThreadId !== undefined &&
501
- failedCreateIds.has(soleThreadId)
502
- ) {
503
- drop(patch)
504
- recompute()
505
- return
506
- }
507
- try {
508
- await i.commit()
509
- } catch (cause) {
510
- if (i.isCreate) for (const threadId of i.threadIds) failedCreateIds.add(threadId)
511
- // Roll back: discard THIS patch only, leaving any other in-flight patch applied.
512
- drop(patch)
513
- error = cause
514
- errorToken += 1
515
- errorFromWrite = true
516
- recompute()
517
- // Converge on the server: a rejection does not say how much of the write landed (a
518
- // partially-applied `clear()` fan-out, a write whose response was lost), and rolling back
519
- // to a stale `base` would resurrect rows that really were deleted.
520
- revalidate()
521
- return
522
- }
523
- // Only a write issued AFTER the latched failure is evidence of recovery — see `errorToken`.
524
- if (errorFromWrite && errorToken === tokenAtIssue) {
525
- error = undefined
526
- errorFromWrite = false
527
- recompute()
528
- }
529
- // Keep the patch applied ACROSS the revalidate; `prune` drops it once that list has actually
530
- // refreshed `base`. Dropping on return would flash the pre-mutation state. This is why every
531
- // patch has to be idempotent.
532
- patch.confirmSeq = revalidate()
533
- })
534
- }
535
-
536
- /** Rewrites one thread in place, preserving order; a no-op when the id is unknown. */
537
- function patchThread(
538
- id: string,
539
- update: (thread: AgentThread<TPart>) => AgentThread<TPart>,
540
- ): ThreadPatch<TPart>['apply'] {
541
- return (threads): AgentThread<TPart>[] =>
542
- threads.map((thread) => (thread.id === id ? update(thread) : thread))
543
- }
544
-
545
- // ── mount lifecycle (shared across every component using the hook) ──────────
546
-
547
- let mounted = 0
548
-
549
- function onFocus(): void {
550
- // A refocus genuinely wants the freshest list now, not whatever a stale in-flight load lands.
551
- forceRevalidate()
552
- }
553
-
554
- function retain(): void {
555
- mounted += 1
556
- if (mounted !== 1) return
557
- revalidate()
558
- if (revalidateOnFocus && typeof window !== 'undefined') {
559
- window.addEventListener('focus', onFocus)
560
- }
561
- }
562
-
563
- function release(): void {
564
- mounted -= 1
565
- if (mounted !== 0) return
566
- loadController?.abort()
567
- loadController = null
568
- // Nothing is left to prove it, and a stale seq handed to a future write would let that write's
569
- // patch retire prematurely the moment ANY later load lands — see the `queuedSeq` invariant.
570
- queuedSeq = null
571
- if (revalidateOnFocus && typeof window !== 'undefined') {
572
- window.removeEventListener('focus', onFocus)
573
- }
574
- }
575
-
576
- // ── actions (factory-scope, hence referentially stable across renders) ──────
577
-
578
- function select(id: string | null): void {
579
- activeId = id
580
- recompute()
581
- }
582
-
583
- function create(createOpts?: { readonly meta?: Record<string, unknown> }): string {
584
- // Client-minted: `create()` is synchronous, so there is no await to carry a server id back.
585
- const id = mintThreadId()
586
- const now = Date.now()
587
- const thread: AgentThread<TPart> = {
588
- id,
589
- messages: [],
590
- outcome: null,
591
- status: 'pending',
592
- read: false,
593
- createdAt: now,
594
- updatedAt: now,
595
- ...(createOpts?.meta !== undefined ? { meta: createOpts.meta } : {}),
596
- }
597
- mutate({
598
- threadIds: [id],
599
- apply: (threads) => (threads.some((t) => t.id === id) ? [...threads] : [thread, ...threads]),
600
- isCreate: true,
601
- commit: () =>
602
- adapter.createThread({
603
- id,
604
- ...(createOpts?.meta !== undefined ? { meta: createOpts.meta } : {}),
605
- }),
606
- })
607
- return id
608
- }
609
-
610
- function appendMessage(id: string, message: ChatMessage<TPart>): void {
611
- // Captured at ISSUE time, never read inside `apply` — see ThreadPatch. Same for every setter
612
- // below.
613
- const now = Date.now()
614
- mutate({
615
- threadIds: [id],
616
- apply: patchThread(id, (thread) =>
617
- thread.messages.some((m) => m.id === message.id)
618
- ? thread
619
- : { ...thread, messages: [...thread.messages, message], updatedAt: now },
620
- ),
621
- commit: () => adapter.appendMessage({ threadId: id, message }),
622
- })
623
- }
624
-
625
- function setOutcome(id: string, outcome: AgentOutcome): void {
626
- const now = Date.now()
627
- mutate({
628
- threadIds: [id],
629
- apply: patchThread(id, (thread) => ({ ...thread, outcome, updatedAt: now })),
630
- commit: () => adapter.setOutcome({ threadId: id, outcome }),
631
- })
632
- }
633
-
634
- function setStatus(id: string, status: ThreadStatus): void {
635
- const now = Date.now()
636
- mutate({
637
- threadIds: [id],
638
- apply: patchThread(id, (thread) => ({ ...thread, status, updatedAt: now })),
639
- commit: () => adapter.setStatus({ threadId: id, status }),
640
- })
641
- }
642
-
643
- function setResumeToken(id: string, token: string | undefined): void {
644
- const now = Date.now()
645
- mutate({
646
- threadIds: [id],
647
- apply: patchThread(id, (thread) => {
648
- // exactOptionalPropertyTypes: drop the key entirely to clear it rather than assigning
649
- // `resumeToken: undefined` (same idiom as thread.ts).
650
- const { resumeToken: _resumeToken, ...rest } = thread
651
- return {
652
- ...rest,
653
- ...(token !== undefined ? { resumeToken: token } : {}),
654
- updatedAt: now,
655
- }
656
- }),
657
- commit: () => adapter.setResumeToken({ threadId: id, token }),
658
- })
659
- }
660
-
661
- function markRead(id: string): void {
662
- mutate({
663
- threadIds: [id],
664
- // Identity-preserving when the server already agrees — markRead does not bump updatedAt.
665
- apply: patchThread(id, (thread) => (thread.read ? thread : { ...thread, read: true })),
666
- commit: () => adapter.markRead(id),
667
- })
668
- }
669
-
670
- function remove(id: string): void {
671
- mutate({
672
- threadIds: [id],
673
- apply: (threads) => threads.filter((thread) => thread.id !== id),
674
- // Inside the patch, so a rejected delete restores the SELECTION along with the row. Written
675
- // as a conditional clear rather than a flat `null` so a `select()` issued while the delete
676
- // is in flight survives the patch being re-applied on top of it.
677
- applyActive: (active) => (active === id ? null : active),
678
- commit: () => adapter.removeThread(id),
679
- })
680
- }
681
-
682
- function clear(): void {
683
- // The adapter has no bulk delete, so clear() fans out over the threads we currently know
684
- // about. Threads created concurrently by another tab survive — they are picked up by the
685
- // revalidate that follows.
686
- const ids = snapshot.threads.map((thread) => thread.id)
687
- const clearedIds = new Set(ids)
688
- mutate({
689
- threadIds: ids,
690
- apply: () => [],
691
- // Same idiom as remove(): only null a selection pointing at one of the threads THIS clear
692
- // captured, so a select() issued while the clear is in flight — e.g. into a thread created
693
- // concurrently by create() — survives the patch being re-applied on top of it.
694
- applyActive: (active) => (active !== null && clearedIds.has(active) ? null : active),
695
- commit: async (): Promise<void> => {
696
- // allSettled, not all: `all` rejects on the FIRST failure while the other deletes are
697
- // still in flight, so the convergence load would race them and re-list rows that were
698
- // about to disappear. Settling everything first makes the follow-up list authoritative.
699
- const results = await Promise.allSettled(ids.map((id) => adapter.removeThread(id)))
700
- const failures = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected')
701
- if (failures.length === 0) return
702
- throw new AggregateError(
703
- failures.map((f): unknown => f.reason),
704
- `removeThread rejected for ${failures.length} of ${ids.length} threads`,
705
- )
706
- },
707
- })
708
- }
709
-
710
- return function useAdapterThreadsStore(): ThreadsStore<TPart> {
711
- const current = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
712
-
713
- useEffect(() => {
714
- retain()
715
- return (): void => {
716
- release()
717
- }
718
- }, [])
719
-
720
- return {
721
- threads: current.threads,
722
- activeId: current.activeId,
723
- hydrated: current.hydrated,
724
- error: current.error,
725
- select,
726
- create,
727
- appendMessage,
728
- setOutcome,
729
- setStatus,
730
- setResumeToken,
731
- markRead,
732
- remove,
733
- clear,
734
- }
735
- }
736
- }
737
-
738
- // ── threadsStoreAdapterContract ───────────────────────────────────────────────
739
-
740
- /** One conformance case. `run` rejects with a plain Error describing the violation. */
741
- export type ThreadsStoreAdapterContractCase = {
742
- readonly name: string
743
- readonly run: () => Promise<void>
744
- }
745
-
746
- function assert(condition: boolean, message: string): asserts condition {
747
- if (!condition) throw new Error(`ThreadsStoreAdapter contract: ${message}`)
748
- }
749
-
750
- function message<TPart>(id: string): ChatMessage<TPart> {
751
- // `parts: []` keeps the suite fully generic — there is no way to mint a TPart from here, and
752
- // the contract is about ROW IDENTITY (the id), not part payloads.
753
- return { id, role: 'user', parts: [], createdAt: Date.now() }
754
- }
755
-
756
- /**
757
- * A test-runner-agnostic conformance suite for a `ThreadsStoreAdapter` implementation.
758
- *
759
- * This ships in the published package, so it deliberately imports NO test framework and depends
760
- * on no assertion library — each case throws a plain `Error`. Drive it from whatever runner the
761
- * consumer uses.
762
- *
763
- * `makeAdapter` is called ONCE PER CASE and must hand back a fresh, empty backend: the cases are
764
- * independent and none of them cleans up after itself.
765
- *
766
- * @example
767
- * import { threadsStoreAdapterContract } from 'basalt-ui/agent'
768
- *
769
- * for (const c of threadsStoreAdapterContract(() => postgresThreadsAdapter(freshDb()))) {
770
- * test(c.name, () => c.run())
771
- * }
772
- */
773
- export function threadsStoreAdapterContract<TPart>(
774
- makeAdapter: () => ThreadsStoreAdapter<TPart> | Promise<ThreadsStoreAdapter<TPart>>,
775
- ): readonly ThreadsStoreAdapterContractCase[] {
776
- function define(
777
- name: string,
778
- run: (adapter: ThreadsStoreAdapter<TPart>) => Promise<void>,
779
- ): ThreadsStoreAdapterContractCase {
780
- return {
781
- name,
782
- run: async (): Promise<void> => {
783
- await run(await makeAdapter())
784
- },
785
- }
786
- }
787
-
788
- async function loadOrThrow(
789
- adapter: ThreadsStoreAdapter<TPart>,
790
- id: string,
791
- ): Promise<AgentThread<TPart>> {
792
- const thread = await adapter.loadThread(id)
793
- assert(thread !== null, `loadThread('${id}') returned null for a thread that was created`)
794
- return thread
795
- }
796
-
797
- return [
798
- define('createThread then listThreads returns the thread with empty defaults', async (a) => {
799
- const id = mintThreadId()
800
- await a.createThread({ id })
801
- const threads = await a.listThreads()
802
- const found = threads.filter((t) => t.id === id)
803
- assert(found.length === 1, `listThreads returned ${found.length} threads for one create`)
804
- const thread = found[0]
805
- assert(thread !== undefined, 'listThreads entry was undefined')
806
- assert(thread.messages.length === 0, 'a new thread must start with no messages')
807
- assert(thread.outcome === null, "a new thread's outcome must be null")
808
- assert(thread.status === 'pending', `a new thread's status must be 'pending'`)
809
- assert(thread.read === false, 'a new thread must be unread')
810
- assert(typeof thread.createdAt === 'number', 'createdAt must be a number')
811
- assert(typeof thread.updatedAt === 'number', 'updatedAt must be a number')
812
- }),
813
-
814
- define('createThread persists meta', async (a) => {
815
- const id = mintThreadId()
816
- await a.createThread({ id, meta: { source: 'contract' } })
817
- const thread = await loadOrThrow(a, id)
818
- assert(thread.meta?.['source'] === 'contract', 'meta did not round-trip')
819
- }),
820
-
821
- define('loadThread returns null for an unknown id', async (a) => {
822
- const thread = await a.loadThread(mintThreadId())
823
- assert(thread === null, 'loadThread must return null (not throw, not undefined) when absent')
824
- }),
825
-
826
- define('appendMessage is idempotent on message.id', async (a) => {
827
- const id = mintThreadId()
828
- await a.createThread({ id })
829
- const msg = message<TPart>('contract-msg-1')
830
- await a.appendMessage({ threadId: id, message: msg })
831
- await a.appendMessage({ threadId: id, message: msg })
832
- const thread = await loadOrThrow(a, id)
833
- assert(
834
- thread.messages.length === 1,
835
- `appending the same message.id twice produced ${thread.messages.length} rows — the id is the only idempotency key`,
836
- )
837
- assert(thread.messages[0]?.id === 'contract-msg-1', 'the retained message has the wrong id')
838
- }),
839
-
840
- define('appendMessage preserves order for distinct ids', async (a) => {
841
- const id = mintThreadId()
842
- await a.createThread({ id })
843
- await a.appendMessage({ threadId: id, message: message<TPart>('contract-msg-1') })
844
- await a.appendMessage({ threadId: id, message: message<TPart>('contract-msg-2') })
845
- const thread = await loadOrThrow(a, id)
846
- assert(
847
- thread.messages.map((m) => m.id).join(',') === 'contract-msg-1,contract-msg-2',
848
- 'messages must read back oldest-first in append order',
849
- )
850
- }),
851
-
852
- define('setStatus round-trips', async (a) => {
853
- const id = mintThreadId()
854
- await a.createThread({ id })
855
- await a.setStatus({ threadId: id, status: 'streaming' })
856
- assert((await loadOrThrow(a, id)).status === 'streaming', "status did not become 'streaming'")
857
- await a.setStatus({ threadId: id, status: 'error' })
858
- assert((await loadOrThrow(a, id)).status === 'error', "status did not become 'error'")
859
- }),
860
-
861
- define('setOutcome round-trips', async (a) => {
862
- const id = mintThreadId()
863
- await a.createThread({ id })
864
- const outcome: AgentOutcome = { title: 'Title', summary: 'Summary', status: 'done' }
865
- await a.setOutcome({ threadId: id, outcome })
866
- const thread = await loadOrThrow(a, id)
867
- assert(thread.outcome?.title === 'Title', 'outcome.title did not round-trip')
868
- assert(thread.outcome?.summary === 'Summary', 'outcome.summary did not round-trip')
869
- assert(thread.outcome?.status === 'done', 'outcome.status did not round-trip')
870
- }),
871
-
872
- define('setResumeToken round-trips and clears with undefined', async (a) => {
873
- const id = mintThreadId()
874
- await a.createThread({ id })
875
- await a.setResumeToken({ threadId: id, token: 'tok-1' })
876
- assert((await loadOrThrow(a, id)).resumeToken === 'tok-1', 'resumeToken did not round-trip')
877
- await a.setResumeToken({ threadId: id, token: undefined })
878
- assert(
879
- (await loadOrThrow(a, id)).resumeToken === undefined,
880
- 'setResumeToken(undefined) must CLEAR the token',
881
- )
882
- }),
883
-
884
- define('markRead marks the thread read', async (a) => {
885
- const id = mintThreadId()
886
- await a.createThread({ id })
887
- assert((await loadOrThrow(a, id)).read === false, 'a new thread must start unread')
888
- await a.markRead(id)
889
- assert((await loadOrThrow(a, id)).read === true, 'markRead did not set read')
890
- }),
891
-
892
- define('listThreads reflects a write that has already resolved', async (a) => {
893
- // The store keeps a write's optimistic patch applied until a listThreads STARTED after that
894
- // write resolved lands in the confirmed base. An eventually-consistent list (a stale read
895
- // replica, a cached response) breaks that and flashes the pre-write state back into the UI.
896
- const id = mintThreadId()
897
- await a.createThread({ id })
898
- await a.appendMessage({ threadId: id, message: message<TPart>('contract-msg-raw') })
899
- await a.setStatus({ threadId: id, status: 'streaming' })
900
- const found = (await a.listThreads()).find((t) => t.id === id)
901
- assert(found !== undefined, 'listThreads omitted a thread created before the call')
902
- assert(
903
- found.status === 'streaming',
904
- 'listThreads returned a STALE status — a list started after a write resolves must reflect it',
905
- )
906
- assert(
907
- found.messages.length === 1,
908
- `listThreads returned a STALE message list (${found.messages.length} rows) — a list started after a write resolves must reflect it`,
909
- )
910
- }),
911
-
912
- define(
913
- 'dependent writes issued straight after createThread resolves are honoured',
914
- async (a) => {
915
- // The store serializes writes per thread id, so this exact sequence — the send path's
916
- // create-then-first-message — is what a real adapter receives. Each write must find the row.
917
- const id = mintThreadId()
918
- await a.createThread({ id })
919
- await a.markRead(id)
920
- await a.appendMessage({ threadId: id, message: message<TPart>('contract-msg-dep') })
921
- await a.setStatus({ threadId: id, status: 'streaming' })
922
- const thread = await loadOrThrow(a, id)
923
- assert(thread.read === true, 'markRead did not apply to a just-created thread')
924
- assert(
925
- thread.messages.length === 1,
926
- `appendMessage did not apply to a just-created thread (${thread.messages.length} rows)`,
927
- )
928
- assert(thread.status === 'streaming', 'setStatus did not apply to a just-created thread')
929
- },
930
- ),
931
-
932
- define('removeThread removes the thread', async (a) => {
933
- const id = mintThreadId()
934
- await a.createThread({ id })
935
- await a.removeThread(id)
936
- assert(await a.loadThread(id).then((t) => t === null), 'loadThread must be null after remove')
937
- const threads = await a.listThreads()
938
- assert(!threads.some((t) => t.id === id), 'listThreads still contains the removed thread')
939
- }),
940
-
941
- define('removeThread of an unknown id does not throw', async (a) => {
942
- await a.removeThread(mintThreadId())
943
- }),
944
- ]
945
- }