basalt-ui 1.26.0 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +23 -23
- package/CHANGELOG.md +67 -0
- package/MIGRATING.md +896 -37
- package/README.md +33 -3
- package/agent/rules/basalt-batteries.md +25 -10
- package/agent/rules/basalt-controls.md +73 -43
- package/agent/rules/basalt-mantine.md +17 -6
- package/agent/rules/basalt-state.md +5 -0
- package/agent/rules/basalt-tokens.md +1 -1
- package/configs/oxlint-plugin.js +1286 -73
- package/configs/oxlint-plugin.test.ts +1338 -29
- package/configs/oxlint.json +12 -5
- package/dist/agent/coalesce.d.ts.map +1 -1
- package/dist/agent/coalesce.js +2 -1
- package/dist/agent/merge.d.ts.map +1 -1
- package/dist/agent/merge.js +2 -1
- package/dist/agent/part-list.d.ts.map +1 -1
- package/dist/agent/part-list.js +2 -0
- package/dist/agent/stick-to-bottom.d.ts +3 -6
- package/dist/agent/stick-to-bottom.d.ts.map +1 -1
- package/dist/agent/stick-to-bottom.js +6 -3
- package/dist/agent-chat/composer.d.ts +6 -2
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +49 -33
- package/dist/agent-chat/relative-time.d.ts +0 -9
- package/dist/agent-chat/relative-time.d.ts.map +1 -1
- package/dist/agent-chat/relative-time.js +2 -1
- package/dist/agent-chat/thread-detail-panel.d.ts +3 -2
- package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
- package/dist/agent-chat/thread-detail-panel.js +8 -5
- package/dist/agent-chat/thread-feed-row.d.ts +2 -1
- package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed-row.js +92 -92
- package/dist/agent-chat/thread-feed.d.ts +3 -2
- package/dist/agent-chat/thread-feed.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed.js +8 -3
- package/dist/agent-chat/thread-message.d.ts +4 -3
- package/dist/agent-chat/thread-message.d.ts.map +1 -1
- package/dist/agent-chat/thread-message.js +70 -31
- package/dist/agent-chat/thread-outcome-card.d.ts +3 -2
- package/dist/agent-chat/thread-outcome-card.d.ts.map +1 -1
- package/dist/agent-chat/thread-outcome-card.js +9 -2
- package/dist/agent-chat/thread-workspace.d.ts +7 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +62 -29
- package/dist/agent-chat/tool-chip.d.ts +3 -2
- package/dist/agent-chat/tool-chip.d.ts.map +1 -1
- package/dist/agent-chat/tool-chip.js +7 -2
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +15 -8
- package/dist/charts/index.d.ts +8 -7
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +45 -5
- package/dist/charts/kinds/BandStrip.d.ts +10 -7
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +12 -7
- package/dist/charts/kinds/Bars.d.ts +12 -6
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +37 -11
- package/dist/charts/kinds/Donut.d.ts +6 -5
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +19 -2
- package/dist/charts/kinds/DualPanel.d.ts +6 -5
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +36 -8
- package/dist/charts/kinds/Heatmap.d.ts +10 -6
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +58 -31
- package/dist/charts/kinds/MirroredBars.d.ts +6 -5
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +9 -2
- package/dist/charts/kinds/MultiLine.d.ts +11 -6
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +24 -15
- package/dist/charts/kinds/StackedArea.d.ts +18 -8
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +29 -4
- package/dist/charts/kinds/ZonedLine.d.ts +12 -8
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +33 -23
- package/dist/charts/layout/auto-margin.d.ts +65 -8
- package/dist/charts/layout/auto-margin.d.ts.map +1 -1
- package/dist/charts/layout/auto-margin.js +45 -10
- package/dist/charts/layout/log-ticks.d.ts +27 -0
- package/dist/charts/layout/log-ticks.d.ts.map +1 -0
- package/dist/charts/layout/log-ticks.js +42 -0
- package/dist/charts/primitives/Axes.d.ts +11 -2
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +15 -5
- package/dist/charts/primitives/CartesianChart.d.ts +45 -7
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +86 -28
- package/dist/charts/primitives/ChartCard.d.ts +27 -2
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +37 -17
- package/dist/charts/primitives/ChartFrame.d.ts +30 -4
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +45 -15
- package/dist/charts/primitives/ChartLegend.d.ts +13 -2
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +33 -13
- package/dist/charts/primitives/ChartPending.d.ts +77 -4
- package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
- package/dist/charts/primitives/ChartPending.js +100 -6
- package/dist/charts/primitives/ChartTooltip.d.ts +7 -0
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +18 -5
- package/dist/charts/primitives/Crosshair.d.ts +3 -0
- package/dist/charts/primitives/Crosshair.d.ts.map +1 -1
- package/dist/charts/primitives/Crosshair.js +3 -1
- package/dist/charts/primitives/chart-frame-layout.d.ts +95 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -0
- package/dist/charts/primitives/chart-frame-layout.js +89 -0
- package/dist/charts/primitives/chart-tier.d.ts +16 -0
- package/dist/charts/primitives/chart-tier.d.ts.map +1 -0
- package/dist/charts/primitives/chart-tier.js +20 -0
- package/dist/charts/series.d.ts +55 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/series.js +27 -1
- package/dist/charts/sparklines/BarSparkline.d.ts +3 -3
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +9 -3
- package/dist/charts/sparklines/LineSparkline.d.ts +3 -3
- package/dist/charts/sparklines/LineSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/LineSparkline.js +15 -3
- package/dist/charts/sparklines/index.d.ts +2 -2
- package/dist/charts/sparklines/index.d.ts.map +1 -1
- package/dist/charts/utils/format.d.ts +92 -2
- package/dist/charts/utils/format.d.ts.map +1 -1
- package/dist/charts/utils/format.js +64 -1
- package/dist/charts/utils/ticks.d.ts +81 -2
- package/dist/charts/utils/ticks.d.ts.map +1 -1
- package/dist/charts/utils/ticks.js +54 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/commands/ShortcutsHelp.d.ts +3 -2
- package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
- package/dist/commands/ShortcutsHelp.js +8 -2
- package/dist/commands/define-commands.d.ts.map +1 -1
- package/dist/commands/define-commands.js +2 -2
- package/dist/commands/define-overlays.d.ts +102 -0
- package/dist/commands/define-overlays.d.ts.map +1 -1
- package/dist/commands/define-overlays.js +102 -2
- package/dist/commands/index.d.ts +10 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +3 -1
- package/dist/commands/overlays-mount.d.ts +13 -6
- package/dist/commands/overlays-mount.d.ts.map +1 -1
- package/dist/commands/overlays-mount.js +23 -14
- package/dist/common/errors.d.ts +34 -0
- package/dist/common/errors.d.ts.map +1 -0
- package/dist/common/errors.js +36 -0
- package/dist/common/index.d.ts +15 -0
- package/dist/common/index.d.ts.map +1 -0
- package/dist/common/index.js +29 -0
- package/dist/common/props.d.ts +63 -0
- package/dist/common/props.d.ts.map +1 -0
- package/dist/common/props.js +6 -0
- package/dist/common/query-state-like.d.ts +43 -0
- package/dist/common/query-state-like.d.ts.map +1 -0
- package/dist/common/query-state-like.js +20 -0
- package/dist/common/refs.d.ts +29 -0
- package/dist/common/refs.d.ts.map +1 -0
- package/dist/common/refs.js +29 -0
- package/dist/common/scroll-parent.d.ts +23 -0
- package/dist/common/scroll-parent.d.ts.map +1 -0
- package/dist/common/scroll-parent.js +21 -0
- package/dist/common/validate.d.ts +38 -0
- package/dist/common/validate.d.ts.map +1 -0
- package/dist/common/validate.js +36 -0
- package/dist/connectivity/connectivity-indicator.d.ts +3 -1
- package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
- package/dist/connectivity/connectivity-indicator.js +3 -1
- package/dist/connectivity/connectivity-provider.d.ts.map +1 -1
- package/dist/connectivity/connectivity-provider.js +4 -1
- package/dist/connectivity/index.d.ts +1 -0
- package/dist/connectivity/index.d.ts.map +1 -1
- package/dist/connectivity/types.d.ts +2 -2
- package/dist/connectivity/types.d.ts.map +1 -1
- package/dist/connectivity/use-connectivity.d.ts.map +1 -1
- package/dist/content/article-layout.module.css +11 -4
- package/dist/content/callout.d.ts +8 -4
- package/dist/content/callout.d.ts.map +1 -1
- package/dist/content/callout.js +15 -8
- package/dist/content/guide.d.ts +5 -4
- package/dist/content/guide.d.ts.map +1 -1
- package/dist/content/guide.js +13 -4
- package/dist/content/heading-anchor.d.ts +24 -2
- package/dist/content/heading-anchor.d.ts.map +1 -1
- package/dist/content/heading-anchor.js +23 -9
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +2 -0
- package/dist/content/prose.module.css +9 -7
- package/dist/content/reading-progress.d.ts +10 -4
- package/dist/content/reading-progress.d.ts.map +1 -1
- package/dist/content/reading-progress.js +26 -10
- package/dist/content/slug.d.ts +4 -0
- package/dist/content/slug.d.ts.map +1 -1
- package/dist/content/toc.d.ts +11 -0
- package/dist/content/toc.d.ts.map +1 -1
- package/dist/content/toc.js +23 -10
- package/dist/controls/actions.d.ts +49 -6
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +39 -8
- package/dist/controls/compare-filter.d.ts +3 -2
- package/dist/controls/compare-filter.d.ts.map +1 -1
- package/dist/controls/compare-filter.js +17 -2
- package/dist/controls/control-group.d.ts +3 -3
- package/dist/controls/control-group.d.ts.map +1 -1
- package/dist/controls/control-group.js +9 -2
- package/dist/controls/controls.module.css +169 -186
- package/dist/controls/controls.module.css.d.ts +9 -11
- package/dist/controls/enum-filter.d.ts +13 -3
- package/dist/controls/enum-filter.d.ts.map +1 -1
- package/dist/controls/enum-filter.js +32 -15
- package/dist/controls/filter-context.d.ts +15 -6
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-pill.d.ts +3 -3
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +4 -1
- package/dist/controls/filter-set.d.ts +3 -2
- package/dist/controls/filter-set.d.ts.map +1 -1
- package/dist/controls/filter-set.js +66 -41
- package/dist/controls/filter-sheet.d.ts +20 -80
- package/dist/controls/filter-sheet.d.ts.map +1 -1
- package/dist/controls/filter-sheet.js +9 -95
- package/dist/controls/index.d.ts +3 -0
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +6 -0
- package/dist/controls/multi-select-filter.d.ts +18 -2
- package/dist/controls/multi-select-filter.d.ts.map +1 -1
- package/dist/controls/multi-select-filter.js +124 -22
- package/dist/controls/number-filter.d.ts +28 -0
- package/dist/controls/number-filter.d.ts.map +1 -0
- package/dist/controls/number-filter.js +172 -0
- package/dist/controls/panel-row.d.ts +132 -0
- package/dist/controls/panel-row.d.ts.map +1 -0
- package/dist/controls/panel-row.js +173 -0
- package/dist/controls/panel-row.module.css +86 -0
- package/dist/controls/panel-row.module.css.d.ts +13 -0
- package/dist/controls/range-filter.d.ts +20 -5
- package/dist/controls/range-filter.d.ts.map +1 -1
- package/dist/controls/range-filter.js +75 -38
- package/dist/controls/search-filter.d.ts +3 -2
- package/dist/controls/search-filter.d.ts.map +1 -1
- package/dist/controls/search-filter.js +26 -11
- package/dist/controls/select-filter.d.ts +2 -1
- package/dist/controls/select-filter.d.ts.map +1 -1
- package/dist/controls/select-filter.js +4 -0
- package/dist/controls/slider-control.d.ts +36 -0
- package/dist/controls/slider-control.d.ts.map +1 -0
- package/dist/controls/slider-control.js +64 -0
- package/dist/controls/sync-button.d.ts +3 -2
- package/dist/controls/sync-button.d.ts.map +1 -1
- package/dist/controls/sync-button.js +16 -3
- package/dist/controls/toggle-filter.d.ts +3 -2
- package/dist/controls/toggle-filter.d.ts.map +1 -1
- package/dist/controls/toggle-filter.js +29 -12
- package/dist/controls/view-tabs.d.ts +3 -2
- package/dist/controls/view-tabs.d.ts.map +1 -1
- package/dist/controls/view-tabs.js +43 -11
- package/dist/controls-dates/date-range-picker.d.ts +1 -1
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -1
- package/dist/controls-dates/date-range-picker.js +6 -1
- package/dist/dashboard/delta-badge.d.ts +5 -1
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/empty-state.d.ts +10 -3
- package/dist/dashboard/empty-state.d.ts.map +1 -1
- package/dist/dashboard/empty-state.js +37 -6
- package/dist/dashboard/index.d.ts +6 -4
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +10 -2
- package/dist/dashboard/query-state.d.ts +48 -27
- package/dist/dashboard/query-state.d.ts.map +1 -1
- package/dist/dashboard/query-state.js +111 -57
- package/dist/dashboard/settings-section.d.ts +19 -9
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +74 -25
- package/dist/dashboard/stat-card.d.ts +76 -19
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +78 -38
- package/dist/dashboard/stat-card.module.css +112 -26
- package/dist/dashboard/stat-card.module.css.d.ts +5 -0
- package/dist/dashboard/stat-group.d.ts +41 -0
- package/dist/dashboard/stat-group.d.ts.map +1 -0
- package/dist/dashboard/stat-group.js +30 -0
- package/dist/dashboard/stat-group.module.css +66 -0
- package/dist/dashboard/stat-group.module.css.d.ts +8 -0
- package/dist/dashboard/widget-grid.d.ts +27 -0
- package/dist/dashboard/widget-grid.d.ts.map +1 -0
- package/dist/dashboard/widget-grid.js +41 -0
- package/dist/dashboard/widget-grid.module.css +53 -0
- package/dist/dashboard/widget-grid.module.css.d.ts +9 -0
- package/dist/data/data-table.d.ts +151 -12
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +392 -138
- package/dist/data/data-table.module.css +64 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- package/dist/data/index.d.ts +2 -2
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +24 -2
- package/dist/data/query-branch.d.ts +28 -0
- package/dist/data/query-branch.d.ts.map +1 -0
- package/dist/data/query-branch.js +10 -0
- package/dist/data/table.d.ts +2 -2
- package/dist/data/table.d.ts.map +1 -1
- package/dist/data/virtual-list.d.ts +65 -6
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual-list.js +89 -38
- package/dist/data/virtual.d.ts +1 -1
- package/dist/data/virtual.d.ts.map +1 -1
- package/dist/forms/FormErrorSummary.d.ts +4 -3
- package/dist/forms/FormErrorSummary.d.ts.map +1 -1
- package/dist/forms/FormErrorSummary.js +6 -4
- package/dist/forms/create-form.d.ts +2 -2
- package/dist/forms/field.d.ts +46 -6
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +10 -5
- package/dist/forms/form-layout.d.ts +142 -0
- package/dist/forms/form-layout.d.ts.map +1 -0
- package/dist/forms/form-layout.js +110 -0
- package/dist/forms/form-layout.module.css +145 -0
- package/dist/forms/form-layout.module.css.d.ts +20 -0
- package/dist/forms/form-state.d.ts +42 -0
- package/dist/forms/form-state.d.ts.map +1 -0
- package/dist/forms/form-state.js +26 -0
- package/dist/forms/index.d.ts +22 -6
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +19 -3
- package/dist/forms/use-field-array.d.ts +60 -0
- package/dist/forms/use-field-array.d.ts.map +1 -0
- package/dist/forms/use-field-array.js +20 -0
- package/dist/forms/use-form-draft.d.ts +25 -10
- package/dist/forms/use-form-draft.d.ts.map +1 -1
- package/dist/forms/use-form-draft.js +55 -18
- package/dist/forms/use-form-submit.d.ts +88 -0
- package/dist/forms/use-form-submit.d.ts.map +1 -0
- package/dist/forms/use-form-submit.js +63 -0
- package/dist/guard/index.d.ts +1 -1
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +27 -11
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -4
- package/dist/nav/types.d.ts +15 -6
- package/dist/nav/types.d.ts.map +1 -1
- package/dist/notifications/bell.d.ts +3 -2
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/bell.js +73 -56
- package/dist/notifications/center.d.ts +15 -4
- package/dist/notifications/center.d.ts.map +1 -1
- package/dist/notifications/center.js +5 -2
- package/dist/notifications/define-notifications.d.ts +56 -14
- package/dist/notifications/define-notifications.d.ts.map +1 -1
- package/dist/notifications/define-notifications.js +2 -1
- package/dist/notifications/index.d.ts +6 -2
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +5 -1
- package/dist/notifications/mount-guard.d.ts +8 -0
- package/dist/notifications/mount-guard.d.ts.map +1 -0
- package/dist/notifications/mount-guard.js +19 -0
- package/dist/notifications/notify.d.ts +58 -1
- package/dist/notifications/notify.d.ts.map +1 -1
- package/dist/notifications/notify.js +46 -2
- package/dist/notifications/overlay.d.ts.map +1 -1
- package/dist/notifications/overlay.js +2 -0
- package/dist/notifications/store.d.ts +7 -0
- package/dist/notifications/store.d.ts.map +1 -1
- package/dist/provider/index.d.ts +37 -5
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +66 -28
- package/dist/provider/lab-theme.d.ts.map +1 -1
- package/dist/provider/lab-theme.js +2 -1
- package/dist/query/devtools.d.ts.map +1 -1
- package/dist/query/devtools.js +1 -0
- package/dist/router-tanstack/field.d.ts +1 -1
- package/dist/router-tanstack/field.d.ts.map +1 -1
- package/dist/router-tanstack/index.d.ts +1 -1
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.d.ts +4 -1
- package/dist/router-tanstack/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.js +92 -8
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +1 -1
- package/dist/section/index.d.ts +1 -1
- package/dist/section/index.d.ts.map +1 -1
- package/dist/section/section.d.ts +36 -2
- package/dist/section/section.d.ts.map +1 -1
- package/dist/section/section.js +49 -57
- package/dist/section/section.module.css +11 -0
- package/dist/shell/account-types.d.ts +5 -2
- package/dist/shell/account-types.d.ts.map +1 -1
- package/dist/shell/app-brand.d.ts +13 -0
- package/dist/shell/app-brand.d.ts.map +1 -0
- package/dist/shell/app-brand.js +94 -0
- package/dist/shell/app-brand.module.css +109 -0
- package/dist/shell/app-brand.module.css.d.ts +14 -0
- package/dist/shell/app-breadcrumbs.d.ts +4 -2
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +33 -22
- package/dist/shell/app-header.module.css +10 -5
- package/dist/shell/app-main.module.css +107 -0
- package/dist/shell/app-main.module.css.d.ts +11 -0
- package/dist/shell/app-mobile-nav.d.ts +3 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +29 -15
- package/dist/shell/app-mobile-nav.module.css +48 -27
- package/dist/shell/app-mobile-nav.module.css.d.ts +0 -1
- package/dist/shell/app-sidebar-account.d.ts +2 -1
- package/dist/shell/app-sidebar-account.d.ts.map +1 -1
- package/dist/shell/app-sidebar-account.js +57 -22
- package/dist/shell/app-sidebar.d.ts +9 -3
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +55 -109
- package/dist/shell/app-sidebar.module.css +53 -96
- package/dist/shell/app-sidebar.module.css.d.ts +0 -5
- package/dist/shell/index.d.ts +14 -6
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +47 -12
- package/dist/shell/isomorphic-layout-effect.d.ts +14 -0
- package/dist/shell/isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/shell/isomorphic-layout-effect.js +5 -0
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +27 -6
- package/dist/shell/nav-count-badge.d.ts +5 -3
- package/dist/shell/nav-count-badge.d.ts.map +1 -1
- package/dist/shell/nav-count-badge.js +3 -1
- package/dist/shell/page-aside.d.ts +43 -0
- package/dist/shell/page-aside.d.ts.map +1 -0
- package/dist/shell/page-aside.js +202 -0
- package/dist/shell/page-aside.module.css +174 -0
- package/dist/shell/page-aside.module.css.d.ts +14 -0
- package/dist/shell/page-bar.d.ts +55 -15
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +130 -41
- package/dist/shell/page-bar.module.css +107 -13
- package/dist/shell/page-bar.module.css.d.ts +4 -1
- package/dist/shell/sidebar-search.d.ts +4 -2
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +9 -4
- package/dist/shell/sidebar-search.module.css +1 -1
- package/dist/state/fields.d.ts +227 -34
- package/dist/state/fields.d.ts.map +1 -1
- package/dist/state/fields.js +77 -30
- package/dist/state/persisted-or-local.d.ts +9 -0
- package/dist/state/persisted-or-local.d.ts.map +1 -0
- package/dist/state/persisted-or-local.js +24 -0
- package/dist/state/persisted.d.ts +12 -0
- package/dist/state/persisted.d.ts.map +1 -1
- package/dist/state/persisted.js +36 -18
- package/dist/state.d.ts +1 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +2 -6
- package/dist/surfaces.d.ts +12 -11
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +43 -12
- package/dist/theme/ctl-theme.d.ts.map +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +102 -17
- package/dist/theme/nav-link.module.css +37 -26
- package/dist/theme/segmented-control.module.css +22 -0
- package/dist/theme/segmented-control.module.css.d.ts +1 -0
- package/dist/theme-lab/boot.d.ts +66 -0
- package/dist/theme-lab/boot.d.ts.map +1 -0
- package/dist/theme-lab/boot.js +82 -0
- package/dist/theme-lab/index.d.ts +4 -43
- package/dist/theme-lab/index.d.ts.map +1 -1
- package/dist/theme-lab/index.js +9 -69
- package/dist/theme-toggle/index.d.ts +3 -2
- package/dist/theme-toggle/index.d.ts.map +1 -1
- package/dist/theme-toggle/index.js +8 -2
- package/dist/tokens/index.d.ts +42 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +35 -6
- package/dist/tokens/palette.d.ts +45 -25
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +31 -14
- package/dist/tokens.css +4 -5
- package/dist/utils/is-dev.d.ts +3 -0
- package/dist/utils/is-dev.d.ts.map +1 -0
- package/dist/utils/is-dev.js +6 -0
- package/dist/widget-header/delta-badge.d.ts +7 -1
- package/dist/widget-header/delta-badge.d.ts.map +1 -1
- package/dist/widget-header/delta-badge.js +8 -1
- package/dist/widget-header/index.d.ts +2 -2
- package/dist/widget-header/index.d.ts.map +1 -1
- package/dist/widget-header/index.js +3 -1
- package/dist/widget-header/widget-header.d.ts +99 -4
- package/dist/widget-header/widget-header.d.ts.map +1 -1
- package/dist/widget-header/widget-header.js +45 -18
- package/dist/widget-header/widget-header.module.css +87 -7
- package/dist/widget-header/widget-header.module.css.d.ts +1 -0
- package/llms.txt +6 -6
- package/package.json +49 -25
- package/src/agent/coalesce.ts +2 -1
- package/src/agent/merge.ts +2 -1
- package/src/agent/part-list.tsx +2 -0
- package/src/agent/stick-to-bottom.tsx +9 -11
- package/src/agent-chat/composer.tsx +124 -103
- package/src/agent-chat/relative-time.ts +2 -1
- package/src/agent-chat/thread-detail-panel.tsx +9 -5
- package/src/agent-chat/thread-feed-row.tsx +17 -9
- package/src/agent-chat/thread-feed.tsx +9 -3
- package/src/agent-chat/thread-message.tsx +48 -13
- package/src/agent-chat/thread-outcome-card.tsx +9 -1
- package/src/agent-chat/thread-workspace.tsx +72 -33
- package/src/agent-chat/tool-chip.tsx +8 -2
- package/src/charts/hooks/useBandPlot.ts +26 -8
- package/src/charts/index.ts +39 -4
- package/src/charts/kinds/BandStrip.tsx +36 -14
- package/src/charts/kinds/Bars.tsx +67 -16
- package/src/charts/kinds/Donut.tsx +30 -3
- package/src/charts/kinds/DualPanel.tsx +56 -10
- package/src/charts/kinds/Heatmap.tsx +90 -33
- package/src/charts/kinds/MirroredBars.tsx +21 -3
- package/src/charts/kinds/MultiLine.tsx +61 -18
- package/src/charts/kinds/StackedArea.tsx +77 -6
- package/src/charts/kinds/ZonedLine.tsx +71 -31
- package/src/charts/layout/auto-margin.ts +134 -21
- package/src/charts/layout/log-ticks.ts +90 -0
- package/src/charts/primitives/Axes.tsx +33 -5
- package/src/charts/primitives/CartesianChart.tsx +194 -41
- package/src/charts/primitives/ChartCard.tsx +66 -25
- package/src/charts/primitives/ChartFrame.tsx +107 -26
- package/src/charts/primitives/ChartLegend.tsx +43 -13
- package/src/charts/primitives/ChartPending.tsx +185 -7
- package/src/charts/primitives/ChartTooltip.tsx +37 -5
- package/src/charts/primitives/Crosshair.tsx +6 -3
- package/src/charts/primitives/chart-frame-layout.ts +212 -0
- package/src/charts/primitives/chart-tier.tsx +37 -0
- package/src/charts/series.ts +77 -1
- package/src/charts/sparklines/BarSparkline.tsx +11 -3
- package/src/charts/sparklines/LineSparkline.tsx +18 -4
- package/src/charts/sparklines/index.ts +2 -2
- package/src/charts/utils/format.ts +163 -2
- package/src/charts/utils/ticks.ts +149 -4
- package/src/cli/index.ts +8 -3
- package/src/commands/ShortcutsHelp.tsx +10 -3
- package/src/commands/define-commands.ts +2 -2
- package/src/commands/define-overlays.ts +216 -1
- package/src/commands/index.ts +12 -0
- package/src/commands/overlays-mount.tsx +60 -26
- package/src/common/errors.ts +73 -0
- package/src/common/index.ts +21 -0
- package/src/common/props.ts +71 -0
- package/src/common/query-state-like.ts +64 -0
- package/src/common/refs.ts +58 -0
- package/src/common/scroll-parent.ts +42 -0
- package/src/common/validate.ts +94 -0
- package/src/connectivity/connectivity-indicator.tsx +6 -1
- package/src/connectivity/connectivity-provider.tsx +4 -1
- package/src/connectivity/index.ts +1 -0
- package/src/connectivity/types.ts +5 -2
- package/src/connectivity/use-connectivity.ts +3 -2
- package/src/content/article-layout.module.css +11 -4
- package/src/content/callout.tsx +13 -7
- package/src/content/guide.tsx +14 -4
- package/src/content/heading-anchor.tsx +14 -3
- package/src/content/index.ts +2 -0
- package/src/content/markdown.tsx +2 -0
- package/src/content/prose.module.css +9 -7
- package/src/content/reading-progress.tsx +40 -15
- package/src/content/slug.ts +4 -0
- package/src/content/toc.tsx +47 -10
- package/src/controls/actions.tsx +89 -9
- package/src/controls/compare-filter.tsx +20 -3
- package/src/controls/control-group.tsx +11 -4
- package/src/controls/controls.module.css +169 -186
- package/src/controls/controls.module.css.d.ts +9 -11
- package/src/controls/enum-filter.tsx +46 -19
- package/src/controls/filter-context.tsx +21 -8
- package/src/controls/filter-pill.tsx +6 -3
- package/src/controls/filter-set.tsx +32 -13
- package/src/controls/filter-sheet.tsx +44 -194
- package/src/controls/index.ts +8 -0
- package/src/controls/multi-select-filter.tsx +179 -29
- package/src/controls/number-filter.tsx +329 -0
- package/src/controls/panel-row.module.css +86 -0
- package/src/controls/panel-row.module.css.d.ts +13 -0
- package/src/controls/panel-row.tsx +392 -0
- package/src/controls/range-filter.tsx +133 -43
- package/src/controls/search-filter.tsx +31 -13
- package/src/controls/select-filter.tsx +11 -1
- package/src/controls/slider-control.tsx +159 -0
- package/src/controls/sync-button.tsx +13 -3
- package/src/controls/toggle-filter.tsx +37 -17
- package/src/controls/view-tabs.tsx +63 -11
- package/src/controls-dates/date-range-picker.tsx +8 -1
- package/src/dashboard/delta-badge.tsx +5 -1
- package/src/dashboard/empty-state.tsx +72 -25
- package/src/dashboard/index.ts +19 -2
- package/src/dashboard/query-state.tsx +154 -84
- package/src/dashboard/settings-section.tsx +87 -31
- package/src/dashboard/stat-card.module.css +112 -26
- package/src/dashboard/stat-card.module.css.d.ts +5 -0
- package/src/dashboard/stat-card.tsx +207 -68
- package/src/dashboard/stat-group.module.css +66 -0
- package/src/dashboard/stat-group.module.css.d.ts +8 -0
- package/src/dashboard/stat-group.tsx +74 -0
- package/src/dashboard/widget-grid.module.css +53 -0
- package/src/dashboard/widget-grid.module.css.d.ts +9 -0
- package/src/dashboard/widget-grid.tsx +127 -0
- package/src/data/data-table.module.css +64 -0
- package/src/data/data-table.module.css.d.ts +11 -0
- package/src/data/data-table.tsx +927 -346
- package/src/data/index.ts +30 -2
- package/src/data/query-branch.ts +53 -0
- package/src/data/table.ts +5 -0
- package/src/data/virtual-list.tsx +189 -55
- package/src/data/virtual.ts +5 -1
- package/src/forms/FormErrorSummary.tsx +13 -6
- package/src/forms/create-form.ts +2 -2
- package/src/forms/field.ts +60 -11
- package/src/forms/form-layout.module.css +145 -0
- package/src/forms/form-layout.module.css.d.ts +20 -0
- package/src/forms/form-layout.tsx +305 -0
- package/src/forms/form-state.tsx +94 -0
- package/src/forms/index.ts +38 -7
- package/src/forms/use-field-array.ts +94 -0
- package/src/forms/use-form-draft.ts +145 -53
- package/src/forms/use-form-submit.ts +199 -0
- package/src/guard/index.ts +91 -23
- package/src/index.ts +79 -10
- package/src/nav/types.ts +15 -6
- package/src/notifications/bell.tsx +18 -4
- package/src/notifications/center.tsx +18 -5
- package/src/notifications/define-notifications.ts +64 -18
- package/src/notifications/index.ts +12 -1
- package/src/notifications/mount-guard.ts +41 -0
- package/src/notifications/notify.tsx +122 -3
- package/src/notifications/overlay.tsx +2 -0
- package/src/notifications/store.ts +7 -0
- package/src/provider/index.tsx +101 -13
- package/src/provider/lab-theme.ts +2 -1
- package/src/query/devtools.tsx +1 -0
- package/src/router-tanstack/field.ts +5 -0
- package/src/router-tanstack/index.ts +4 -0
- package/src/router-tanstack/search-store.ts +171 -12
- package/src/router-tanstack/use-nav.ts +18 -13
- package/src/section/index.ts +1 -1
- package/src/section/section.module.css +11 -0
- package/src/section/section.tsx +163 -96
- package/src/shell/account-types.ts +5 -2
- package/src/shell/app-brand.module.css +109 -0
- package/src/shell/app-brand.module.css.d.ts +14 -0
- package/src/shell/app-brand.tsx +140 -0
- package/src/shell/app-breadcrumbs.tsx +19 -9
- package/src/shell/app-header.module.css +10 -5
- package/src/shell/app-main.module.css +107 -0
- package/src/shell/app-main.module.css.d.ts +11 -0
- package/src/shell/app-mobile-nav.module.css +48 -27
- package/src/shell/app-mobile-nav.module.css.d.ts +0 -1
- package/src/shell/app-mobile-nav.tsx +43 -11
- package/src/shell/app-sidebar-account.tsx +29 -6
- package/src/shell/app-sidebar.module.css +53 -96
- package/src/shell/app-sidebar.module.css.d.ts +0 -5
- package/src/shell/app-sidebar.tsx +40 -113
- package/src/shell/index.tsx +156 -65
- package/src/shell/isomorphic-layout-effect.ts +14 -0
- package/src/shell/mobile-nav-model.ts +66 -10
- package/src/shell/nav-count-badge.tsx +6 -1
- package/src/shell/page-aside.module.css +174 -0
- package/src/shell/page-aside.module.css.d.ts +14 -0
- package/src/shell/page-aside.tsx +377 -0
- package/src/shell/page-bar.module.css +107 -13
- package/src/shell/page-bar.module.css.d.ts +4 -1
- package/src/shell/page-bar.tsx +294 -94
- package/src/shell/sidebar-search.module.css +1 -1
- package/src/shell/sidebar-search.tsx +13 -4
- package/src/state/fields.ts +360 -69
- package/src/state/persisted-or-local.ts +50 -0
- package/src/state/persisted.ts +76 -26
- package/src/state.ts +4 -7
- package/src/surfaces.ts +43 -12
- package/src/theme/ctl-theme.tsx +10 -0
- package/src/theme/index.ts +129 -16
- package/src/theme/nav-link.module.css +37 -26
- package/src/theme/segmented-control.module.css +22 -0
- package/src/theme/segmented-control.module.css.d.ts +1 -0
- package/src/theme-lab/boot.ts +140 -0
- package/src/theme-lab/index.tsx +21 -112
- package/src/theme-toggle/index.tsx +10 -3
- package/src/tokens/index.ts +50 -6
- package/src/tokens/palette.ts +58 -30
- package/src/utils/is-dev.ts +4 -0
- package/src/widget-header/delta-badge.tsx +25 -5
- package/src/widget-header/index.ts +10 -2
- package/src/widget-header/widget-header.module.css +87 -7
- package/src/widget-header/widget-header.module.css.d.ts +1 -0
- package/src/widget-header/widget-header.tsx +166 -42
- package/dist/agent/adapter.test.d.ts +0 -2
- package/dist/agent/adapter.test.d.ts.map +0 -1
- package/dist/agent/ai-sdk-transport.test.d.ts +0 -2
- package/dist/agent/ai-sdk-transport.test.d.ts.map +0 -1
- package/dist/agent/coalesce.test.d.ts +0 -2
- package/dist/agent/coalesce.test.d.ts.map +0 -1
- package/dist/agent/foreign.test.d.ts +0 -2
- package/dist/agent/foreign.test.d.ts.map +0 -1
- package/dist/agent/id.test.d.ts +0 -2
- package/dist/agent/id.test.d.ts.map +0 -1
- package/dist/agent/merge.test.d.ts +0 -2
- package/dist/agent/merge.test.d.ts.map +0 -1
- package/dist/agent/part-list.test.d.ts +0 -2
- package/dist/agent/part-list.test.d.ts.map +0 -1
- package/dist/agent/parts.test.d.ts +0 -2
- package/dist/agent/parts.test.d.ts.map +0 -1
- package/dist/agent/thread.test.d.ts +0 -2
- package/dist/agent/thread.test.d.ts.map +0 -1
- package/dist/agent/transport.test.d.ts +0 -2
- package/dist/agent/transport.test.d.ts.map +0 -1
- package/dist/agent/use-agent-stream.test.d.ts +0 -2
- package/dist/agent/use-agent-stream.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.test.d.ts.map +0 -1
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +0 -2
- package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +0 -1
- package/dist/agent-chat/composer.test.d.ts +0 -2
- package/dist/agent-chat/composer.test.d.ts.map +0 -1
- package/dist/agent-chat/relative-time.test.d.ts +0 -2
- package/dist/agent-chat/relative-time.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-feed-row.test.d.ts +0 -2
- package/dist/agent-chat/thread-feed-row.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-feed.test.d.ts +0 -2
- package/dist/agent-chat/thread-feed.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-message.test.d.ts +0 -2
- package/dist/agent-chat/thread-message.test.d.ts.map +0 -1
- package/dist/agent-chat/thread-workspace.test.d.ts +0 -2
- package/dist/agent-chat/thread-workspace.test.d.ts.map +0 -1
- package/dist/agent-chat/tool-chip.test.d.ts +0 -2
- package/dist/agent-chat/tool-chip.test.d.ts.map +0 -1
- package/dist/agent-chat/virtualize.type-guard.test.d.ts +0 -2
- package/dist/agent-chat/virtualize.type-guard.test.d.ts.map +0 -1
- package/dist/charts/cursor/resolve.test.d.ts +0 -2
- package/dist/charts/cursor/resolve.test.d.ts.map +0 -1
- package/dist/charts/cursor/store.test.d.ts +0 -2
- package/dist/charts/cursor/store.test.d.ts.map +0 -1
- package/dist/charts/kinds/BandStrip.test.d.ts +0 -2
- package/dist/charts/kinds/BandStrip.test.d.ts.map +0 -1
- package/dist/charts/kinds/Bars.test.d.ts +0 -2
- package/dist/charts/kinds/Bars.test.d.ts.map +0 -1
- package/dist/charts/kinds/DualPanel.test.d.ts +0 -2
- package/dist/charts/kinds/DualPanel.test.d.ts.map +0 -1
- package/dist/charts/kinds/MirroredBars.test.d.ts +0 -2
- package/dist/charts/kinds/MirroredBars.test.d.ts.map +0 -1
- package/dist/charts/kinds/MultiLine.test.d.ts +0 -2
- package/dist/charts/kinds/MultiLine.test.d.ts.map +0 -1
- package/dist/charts/kinds/StackedArea.test.d.ts +0 -2
- package/dist/charts/kinds/StackedArea.test.d.ts.map +0 -1
- package/dist/charts/kinds/ZonedLine.test.d.ts +0 -2
- package/dist/charts/kinds/ZonedLine.test.d.ts.map +0 -1
- package/dist/charts/layout/auto-margin.test.d.ts +0 -2
- package/dist/charts/layout/auto-margin.test.d.ts.map +0 -1
- package/dist/charts/primitives/CartesianChart.test.d.ts +0 -2
- package/dist/charts/primitives/CartesianChart.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartCard.test.d.ts +0 -2
- package/dist/charts/primitives/ChartCard.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartFrame.test.d.ts +0 -2
- package/dist/charts/primitives/ChartFrame.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartLegend.test.d.ts +0 -2
- package/dist/charts/primitives/ChartLegend.test.d.ts.map +0 -1
- package/dist/charts/primitives/ChartTooltip.test.d.ts +0 -2
- package/dist/charts/primitives/ChartTooltip.test.d.ts.map +0 -1
- package/dist/charts/primitives/XZoneRects.test.d.ts +0 -2
- package/dist/charts/primitives/XZoneRects.test.d.ts.map +0 -1
- package/dist/charts/series.test.d.ts +0 -2
- package/dist/charts/series.test.d.ts.map +0 -1
- package/dist/charts/utils/domain.test.d.ts +0 -2
- package/dist/charts/utils/domain.test.d.ts.map +0 -1
- package/dist/charts/utils/measure-text.test.d.ts +0 -2
- package/dist/charts/utils/measure-text.test.d.ts.map +0 -1
- package/dist/cli/check-coverage.test.d.ts +0 -2
- package/dist/cli/check-coverage.test.d.ts.map +0 -1
- package/dist/cli/check-theme.test.d.ts +0 -2
- package/dist/cli/check-theme.test.d.ts.map +0 -1
- package/dist/cli/help.test.d.ts +0 -2
- package/dist/cli/help.test.d.ts.map +0 -1
- package/dist/cli/init-sync.test.d.ts +0 -2
- package/dist/cli/init-sync.test.d.ts.map +0 -1
- package/dist/cli/placement-engine.test.d.ts +0 -2
- package/dist/cli/placement-engine.test.d.ts.map +0 -1
- package/dist/cli/shipped-versions.test.d.ts +0 -2
- package/dist/cli/shipped-versions.test.d.ts.map +0 -1
- package/dist/cli/tarball-parity.test.d.ts +0 -2
- package/dist/cli/tarball-parity.test.d.ts.map +0 -1
- package/dist/cli/template-format.test.d.ts +0 -2
- package/dist/cli/template-format.test.d.ts.map +0 -1
- package/dist/cli/template-ledger.test.d.ts +0 -2
- package/dist/cli/template-ledger.test.d.ts.map +0 -1
- package/dist/cli/tokens-css.test.d.ts +0 -2
- package/dist/cli/tokens-css.test.d.ts.map +0 -1
- package/dist/cli/toolchain-wiring.test.d.ts +0 -2
- package/dist/cli/toolchain-wiring.test.d.ts.map +0 -1
- package/dist/cli/waiver-audit.test.d.ts +0 -2
- package/dist/cli/waiver-audit.test.d.ts.map +0 -1
- package/dist/content/article-actions.test.d.ts +0 -2
- package/dist/content/article-actions.test.d.ts.map +0 -1
- package/dist/content/article-model.test.d.ts +0 -2
- package/dist/content/article-model.test.d.ts.map +0 -1
- package/dist/content/block-split.test.d.ts +0 -2
- package/dist/content/block-split.test.d.ts.map +0 -1
- package/dist/content/heading-components.test.d.ts +0 -2
- package/dist/content/heading-components.test.d.ts.map +0 -1
- package/dist/content/markdown.test.d.ts +0 -2
- package/dist/content/markdown.test.d.ts.map +0 -1
- package/dist/content/prose.module.css.test.d.ts +0 -2
- package/dist/content/prose.module.css.test.d.ts.map +0 -1
- package/dist/content/sanitize.test.d.ts +0 -2
- package/dist/content/sanitize.test.d.ts.map +0 -1
- package/dist/content/slug.test.d.ts +0 -2
- package/dist/content/slug.test.d.ts.map +0 -1
- package/dist/controls/actions.test.d.ts +0 -2
- package/dist/controls/actions.test.d.ts.map +0 -1
- package/dist/controls/actions.type-guard.test.d.ts +0 -2
- package/dist/controls/actions.type-guard.test.d.ts.map +0 -1
- package/dist/controls/control-group.test.d.ts +0 -2
- package/dist/controls/control-group.test.d.ts.map +0 -1
- package/dist/controls/controls.router.test.d.ts +0 -2
- package/dist/controls/controls.router.test.d.ts.map +0 -1
- package/dist/controls/filter-set.test.d.ts +0 -2
- package/dist/controls/filter-set.test.d.ts.map +0 -1
- package/dist/controls/peer-boundary.test.d.ts +0 -2
- package/dist/controls/peer-boundary.test.d.ts.map +0 -1
- package/dist/controls/props-contract.test.d.ts +0 -2
- package/dist/controls/props-contract.test.d.ts.map +0 -1
- package/dist/controls/sync-button.test.d.ts +0 -2
- package/dist/controls/sync-button.test.d.ts.map +0 -1
- package/dist/dashboard/query-state.test.d.ts +0 -2
- package/dist/dashboard/query-state.test.d.ts.map +0 -1
- package/dist/dashboard/settings-section.test.d.ts +0 -2
- package/dist/dashboard/settings-section.test.d.ts.map +0 -1
- package/dist/dashboard/stat-card.test.d.ts +0 -2
- package/dist/dashboard/stat-card.test.d.ts.map +0 -1
- package/dist/data/data-table.test.d.ts +0 -2
- package/dist/data/data-table.test.d.ts.map +0 -1
- package/dist/guard/check-source.test.d.ts +0 -2
- package/dist/guard/check-source.test.d.ts.map +0 -1
- package/dist/guard/grace.test.d.ts +0 -20
- package/dist/guard/grace.test.d.ts.map +0 -1
- package/dist/guard/guard-hook.test.d.ts +0 -2
- package/dist/guard/guard-hook.test.d.ts.map +0 -1
- package/dist/provider/index.test.d.ts +0 -2
- package/dist/provider/index.test.d.ts.map +0 -1
- package/dist/provider/lab-theme.test.d.ts +0 -2
- package/dist/provider/lab-theme.test.d.ts.map +0 -1
- package/dist/query/error-message.test.d.ts +0 -2
- package/dist/query/error-message.test.d.ts.map +0 -1
- package/dist/router-tanstack/multi-search-param-store.test.d.ts +0 -2
- package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/nav.test.d.ts +0 -2
- package/dist/router-tanstack/nav.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-param-store.test.d.ts +0 -2
- package/dist/router-tanstack/search-param-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-store.router.test.d.ts +0 -2
- package/dist/router-tanstack/search-store.router.test.d.ts.map +0 -1
- package/dist/router-tanstack/search-store.test.d.ts +0 -2
- package/dist/router-tanstack/search-store.test.d.ts.map +0 -1
- package/dist/router-tanstack/use-nav.test.d.ts +0 -2
- package/dist/router-tanstack/use-nav.test.d.ts.map +0 -1
- package/dist/section/section.test.d.ts +0 -2
- package/dist/section/section.test.d.ts.map +0 -1
- package/dist/shell/app-mobile-nav.test.d.ts +0 -2
- package/dist/shell/app-mobile-nav.test.d.ts.map +0 -1
- package/dist/shell/app-sidebar.test.d.ts +0 -2
- package/dist/shell/app-sidebar.test.d.ts.map +0 -1
- package/dist/shell/index.test.d.ts +0 -2
- package/dist/shell/index.test.d.ts.map +0 -1
- package/dist/shell/mobile-nav-model.test.d.ts +0 -2
- package/dist/shell/mobile-nav-model.test.d.ts.map +0 -1
- package/dist/shell/page-bar.test.d.ts +0 -2
- package/dist/shell/page-bar.test.d.ts.map +0 -1
- package/dist/state/fields.test.d.ts +0 -2
- package/dist/state/fields.test.d.ts.map +0 -1
- package/dist/styles.floor.test.d.ts +0 -2
- package/dist/styles.floor.test.d.ts.map +0 -1
- package/dist/theme/border-coverage.test.d.ts +0 -18
- package/dist/theme/border-coverage.test.d.ts.map +0 -1
- package/dist/theme/contrast.test.d.ts +0 -2
- package/dist/theme/contrast.test.d.ts.map +0 -1
- package/dist/theme/ctl-theme.test.d.ts +0 -2
- package/dist/theme/ctl-theme.test.d.ts.map +0 -1
- package/dist/theme/ctl-tier-coverage.test.d.ts +0 -2
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +0 -1
- package/dist/theme/density-option.test.d.ts +0 -2
- package/dist/theme/density-option.test.d.ts.map +0 -1
- package/dist/theme/density-relations.test.d.ts +0 -2
- package/dist/theme/density-relations.test.d.ts.map +0 -1
- package/dist/theme/derive-theme.test.d.ts +0 -2
- package/dist/theme/derive-theme.test.d.ts.map +0 -1
- package/dist/theme/divider-law.test.d.ts +0 -2
- package/dist/theme/divider-law.test.d.ts.map +0 -1
- package/dist/theme/fonts-option.test.d.ts +0 -2
- package/dist/theme/fonts-option.test.d.ts.map +0 -1
- package/dist/theme/icon-slot.test.d.ts +0 -2
- package/dist/theme/icon-slot.test.d.ts.map +0 -1
- package/dist/theme/layout-rhythm-css.test.d.ts +0 -2
- package/dist/theme/layout-rhythm-css.test.d.ts.map +0 -1
- package/dist/theme/radius-option.test.d.ts +0 -2
- package/dist/theme/radius-option.test.d.ts.map +0 -1
- package/dist/theme/radius.test.d.ts +0 -2
- package/dist/theme/radius.test.d.ts.map +0 -1
- package/dist/theme/shadow-surfaces.test.d.ts +0 -26
- package/dist/theme/shadow-surfaces.test.d.ts.map +0 -1
- package/dist/theme/spacing.test.d.ts +0 -2
- package/dist/theme/spacing.test.d.ts.map +0 -1
- package/dist/theme/use-basalt-spacing.test.d.ts +0 -2
- package/dist/theme/use-basalt-spacing.test.d.ts.map +0 -1
- package/dist/theme-lab/derive-controls.test.d.ts +0 -2
- package/dist/theme-lab/derive-controls.test.d.ts.map +0 -1
- package/dist/theme-lab/derive-state.test.d.ts +0 -2
- package/dist/theme-lab/derive-state.test.d.ts.map +0 -1
- package/dist/theme-lab/theme-lab.test.d.ts +0 -2
- package/dist/theme-lab/theme-lab.test.d.ts.map +0 -1
- package/dist/tokens/build-fonts-css.test.d.ts +0 -2
- package/dist/tokens/build-fonts-css.test.d.ts.map +0 -1
- package/dist/tokens/build-radius-css.test.d.ts +0 -2
- package/dist/tokens/build-radius-css.test.d.ts.map +0 -1
- package/dist/tokens/chart-margin.test.d.ts +0 -2
- package/dist/tokens/chart-margin.test.d.ts.map +0 -1
- package/dist/tokens/density.test.d.ts +0 -2
- package/dist/tokens/density.test.d.ts.map +0 -1
- package/dist/tokens/derive-radius.test.d.ts +0 -2
- package/dist/tokens/derive-radius.test.d.ts.map +0 -1
- package/dist/tokens/derive.test.d.ts +0 -2
- package/dist/tokens/derive.test.d.ts.map +0 -1
- package/dist/tokens/palette.test.d.ts +0 -2
- package/dist/tokens/palette.test.d.ts.map +0 -1
- package/dist/tokens/text-scale.test.d.ts +0 -2
- package/dist/tokens/text-scale.test.d.ts.map +0 -1
- package/dist/vite-color-mix.test.d.ts +0 -2
- package/dist/vite-color-mix.test.d.ts.map +0 -1
- package/dist/vite.test.d.ts +0 -2
- package/dist/vite.test.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.test.d.ts +0 -2
- package/dist/widget-header/delta-badge.test.d.ts.map +0 -1
- package/dist/widget-header/no-mantine.test.d.ts +0 -2
- package/dist/widget-header/no-mantine.test.d.ts.map +0 -1
- package/dist/widget-header/widget-header.test.d.ts +0 -2
- package/dist/widget-header/widget-header.test.d.ts.map +0 -1
- package/src/agent/adapter.test.ts +0 -1640
- package/src/agent/ai-sdk-transport.test.ts +0 -658
- package/src/agent/coalesce.test.ts +0 -387
- package/src/agent/foreign.test.ts +0 -51
- package/src/agent/id.test.ts +0 -169
- package/src/agent/merge.test.ts +0 -206
- package/src/agent/part-list.test.tsx +0 -112
- package/src/agent/parts.test.ts +0 -720
- package/src/agent/thread.test.ts +0 -220
- package/src/agent/transport.test.ts +0 -70
- package/src/agent/use-agent-stream.test.tsx +0 -287
- package/src/agent/use-agent-thread-runs.foreign.test.tsx +0 -138
- package/src/agent/use-agent-thread-runs.resume.test.tsx +0 -294
- package/src/agent/use-agent-thread-runs.test.tsx +0 -1120
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +0 -387
- package/src/agent-chat/composer.test.tsx +0 -924
- package/src/agent-chat/relative-time.test.ts +0 -95
- package/src/agent-chat/thread-feed-row.test.tsx +0 -510
- package/src/agent-chat/thread-feed.test.tsx +0 -241
- package/src/agent-chat/thread-message.test.tsx +0 -1285
- package/src/agent-chat/thread-workspace.test.tsx +0 -169
- package/src/agent-chat/tool-chip.test.tsx +0 -139
- package/src/agent-chat/virtualize.type-guard.test.ts +0 -49
- package/src/charts/cursor/resolve.test.ts +0 -151
- package/src/charts/cursor/store.test.ts +0 -64
- package/src/charts/kinds/BandStrip.test.tsx +0 -282
- package/src/charts/kinds/Bars.test.tsx +0 -183
- package/src/charts/kinds/DualPanel.test.tsx +0 -527
- package/src/charts/kinds/MirroredBars.test.tsx +0 -232
- package/src/charts/kinds/MultiLine.test.tsx +0 -218
- package/src/charts/kinds/StackedArea.test.tsx +0 -114
- package/src/charts/kinds/ZonedLine.test.tsx +0 -147
- package/src/charts/layout/auto-margin.test.ts +0 -42
- package/src/charts/primitives/CartesianChart.test.tsx +0 -647
- package/src/charts/primitives/ChartCard.test.tsx +0 -66
- package/src/charts/primitives/ChartFrame.test.tsx +0 -142
- package/src/charts/primitives/ChartLegend.test.tsx +0 -78
- package/src/charts/primitives/ChartTooltip.test.tsx +0 -44
- package/src/charts/primitives/XZoneRects.test.tsx +0 -123
- package/src/charts/series.test.ts +0 -112
- package/src/charts/utils/domain.test.ts +0 -54
- package/src/charts/utils/measure-text.test.ts +0 -36
- package/src/cli/check-coverage.test.ts +0 -297
- package/src/cli/check-theme.test.ts +0 -837
- package/src/cli/help.test.ts +0 -170
- package/src/cli/init-sync.test.ts +0 -343
- package/src/cli/placement-engine.test.ts +0 -298
- package/src/cli/shipped-versions.test.ts +0 -83
- package/src/cli/tarball-parity.test.ts +0 -44
- package/src/cli/template-format.test.ts +0 -64
- package/src/cli/template-ledger.test.ts +0 -219
- package/src/cli/tokens-css.test.ts +0 -249
- package/src/cli/toolchain-wiring.test.ts +0 -1358
- package/src/cli/waiver-audit.test.ts +0 -215
- package/src/content/article-actions.test.ts +0 -78
- package/src/content/article-model.test.ts +0 -119
- package/src/content/block-split.test.ts +0 -75
- package/src/content/heading-components.test.tsx +0 -65
- package/src/content/markdown.test.tsx +0 -603
- package/src/content/prose.module.css.test.ts +0 -39
- package/src/content/sanitize.test.ts +0 -659
- package/src/content/slug.test.ts +0 -62
- package/src/controls/actions.test.tsx +0 -252
- package/src/controls/actions.type-guard.test.ts +0 -63
- package/src/controls/control-group.test.tsx +0 -229
- package/src/controls/controls.router.test.tsx +0 -637
- package/src/controls/filter-set.test.tsx +0 -472
- package/src/controls/peer-boundary.test.ts +0 -85
- package/src/controls/props-contract.test.tsx +0 -106
- package/src/controls/sync-button.test.tsx +0 -183
- package/src/dashboard/query-state.test.tsx +0 -152
- package/src/dashboard/settings-section.test.tsx +0 -56
- package/src/dashboard/stat-card.test.tsx +0 -154
- package/src/data/data-table.test.tsx +0 -434
- package/src/guard/check-source.test.ts +0 -3355
- package/src/guard/grace.test.ts +0 -149
- package/src/guard/guard-hook.test.ts +0 -203
- package/src/provider/index.test.tsx +0 -101
- package/src/provider/lab-theme.test.ts +0 -147
- package/src/query/error-message.test.ts +0 -67
- package/src/router-tanstack/multi-search-param-store.test.ts +0 -124
- package/src/router-tanstack/nav.test.ts +0 -111
- package/src/router-tanstack/search-param-store.test.ts +0 -135
- package/src/router-tanstack/search-store.router.test.tsx +0 -661
- package/src/router-tanstack/search-store.test.ts +0 -418
- package/src/router-tanstack/use-nav.test.tsx +0 -187
- package/src/section/section.test.tsx +0 -204
- package/src/shell/app-mobile-nav.test.tsx +0 -403
- package/src/shell/app-sidebar.test.tsx +0 -509
- package/src/shell/index.test.tsx +0 -428
- package/src/shell/mobile-nav-model.test.ts +0 -517
- package/src/shell/page-bar.test.tsx +0 -316
- package/src/state/fields.test.tsx +0 -258
- package/src/styles.floor.test.ts +0 -36
- package/src/theme/border-coverage.test.ts +0 -272
- package/src/theme/contrast.test.ts +0 -354
- package/src/theme/ctl-theme.test.tsx +0 -275
- package/src/theme/ctl-tier-coverage.test.ts +0 -170
- package/src/theme/density-option.test.ts +0 -96
- package/src/theme/density-relations.test.ts +0 -506
- package/src/theme/derive-theme.test.ts +0 -101
- package/src/theme/divider-law.test.ts +0 -259
- package/src/theme/fonts-option.test.ts +0 -75
- package/src/theme/icon-slot.test.tsx +0 -275
- package/src/theme/layout-rhythm-css.test.ts +0 -72
- package/src/theme/radius-option.test.ts +0 -86
- package/src/theme/radius.test.ts +0 -142
- package/src/theme/shadow-surfaces.test.ts +0 -488
- package/src/theme/spacing.test.ts +0 -495
- package/src/theme/use-basalt-spacing.test.tsx +0 -55
- package/src/theme-lab/derive-controls.test.ts +0 -94
- package/src/theme-lab/derive-state.test.ts +0 -71
- package/src/theme-lab/theme-lab.test.ts +0 -46
- package/src/tokens/build-fonts-css.test.ts +0 -52
- package/src/tokens/build-radius-css.test.ts +0 -25
- package/src/tokens/chart-margin.test.ts +0 -39
- package/src/tokens/density.test.ts +0 -259
- package/src/tokens/derive-radius.test.ts +0 -62
- package/src/tokens/derive.test.ts +0 -30
- package/src/tokens/palette.test.ts +0 -114
- package/src/tokens/text-scale.test.ts +0 -51
- package/src/vite-color-mix.test.ts +0 -46
- package/src/vite.test.ts +0 -423
- package/src/widget-header/delta-badge.test.tsx +0 -60
- package/src/widget-header/no-mantine.test.ts +0 -33
- package/src/widget-header/widget-header.test.tsx +0 -187
|
@@ -1,658 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* aiSdkTransport — snapshot→delta diffing (text/reasoning/source/tool), deterministic id minting,
|
|
3
|
-
* durationMs, signal-abort mid-stream, and deterministic chat-id binding via `.forThread()`.
|
|
4
|
-
*
|
|
5
|
-
* Scope: the diffing behaviour post-1.11.0's B2 rewrite — `diffToolPart` now models all seven
|
|
6
|
-
* `UIToolInvocation` states (including `input-streaming`, the nested `approval` envelope, and
|
|
7
|
-
* `output-denied`), mints deterministic ids (`${chatId}#${index}` for index-addressed parts,
|
|
8
|
-
* `tool#${toolCallId}` for tool parts), carries an authoritative `offset` on every text/reasoning
|
|
9
|
-
* delta, derives a dynamic tool's real name instead of the literal `'dynamic-tool'`, and does not
|
|
10
|
-
* suppress a `preliminary: true` output-available refinement.
|
|
11
|
-
*
|
|
12
|
-
* Driving `aiSdkTransport` through a scripted HTTP response — the only way to exercise its public
|
|
13
|
-
* stream()/resume() diffing at all, since the diffing internals are not exported — requires a real
|
|
14
|
-
* web-streams `TransformStream` for `ai`'s HTTP transport and `eventsource-parser` to construct
|
|
15
|
-
* internally (`DefaultChatTransport.processResponseStream`, `EventSourceParserStream`). The DOM
|
|
16
|
-
* harness (`tests/setup/dom.ts`, preloaded once per `bun test` process) restores the native
|
|
17
|
-
* web-stream classes after happy-dom registration, so a plain static `import` here is safe
|
|
18
|
-
* regardless of file/module evaluation order.
|
|
19
|
-
*/
|
|
20
|
-
import { describe, expect, test } from 'bun:test'
|
|
21
|
-
import { JsonToSseTransformStream } from 'ai'
|
|
22
|
-
import { aiSdkTransport } from './ai-sdk-transport'
|
|
23
|
-
import { withPartIds } from './id'
|
|
24
|
-
import { mergePart } from './merge'
|
|
25
|
-
import type { UIMessageChunk } from 'ai'
|
|
26
|
-
import type { AgentPart, ToolCallPart } from './parts'
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Builds a fetch-compatible Response whose body is the given low-level UIMessageChunks, SSE-
|
|
30
|
-
* encoded exactly as AI SDK's own `DefaultChatTransport` expects (real `JsonToSseTransformStream`,
|
|
31
|
-
* not a hand-rolled encoding) so this exercises the real parse/diff pipeline end to end.
|
|
32
|
-
*/
|
|
33
|
-
function scriptedResponse(chunks: UIMessageChunk[]): Response {
|
|
34
|
-
const source = new ReadableStream<UIMessageChunk>({
|
|
35
|
-
start(controller) {
|
|
36
|
-
for (const chunk of chunks) controller.enqueue(chunk)
|
|
37
|
-
controller.close()
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
const body = source
|
|
41
|
-
.pipeThrough(new JsonToSseTransformStream())
|
|
42
|
-
.pipeThrough(new TextEncoderStream())
|
|
43
|
-
return new Response(body, { status: 200, headers: { 'content-type': 'text/event-stream' } })
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Casts a scripted fetch stub into `aiSdkTransport`'s `fetch: typeof globalThis.fetch` option.
|
|
48
|
-
* The DOM lib's `typeof fetch` type also demands a static `preconnect` method (a real
|
|
49
|
-
* browser/undici API these scripted mocks never call and don't need to implement) — the cast
|
|
50
|
-
* documents that the only structural gap is that one, unused static member.
|
|
51
|
-
*/
|
|
52
|
-
function mockFetch(impl: () => Promise<Response>): typeof fetch {
|
|
53
|
-
return impl as unknown as typeof fetch
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Drains an aiSdkTransport `.stream()`/`.resume()` async generator into a plain array. */
|
|
57
|
-
async function collect(gen: AsyncGenerator<AgentPart>): Promise<AgentPart[]> {
|
|
58
|
-
const parts: AgentPart[] = []
|
|
59
|
-
for await (const part of gen) parts.push(part)
|
|
60
|
-
return parts
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* `Omit<T, K>` does not distribute over a union — `keyof T` on a union collapses to only the
|
|
65
|
-
* KEYS COMMON to every member, so `Omit<ToolCallPart, 'durationMs'>` would silently drop each
|
|
66
|
-
* state's own fields (`input`, `output`, …) rather than just `durationMs`. This variant forces
|
|
67
|
-
* distribution via a naked conditional type parameter.
|
|
68
|
-
*/
|
|
69
|
-
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never
|
|
70
|
-
|
|
71
|
-
/** Strips a non-deterministic `durationMs` (wall-clock, so never equality-comparable) before an
|
|
72
|
-
* exact `toEqual`, and separately asserts its type. Constrained to `Record<string, unknown>`
|
|
73
|
-
* rather than `{ durationMs?: number }` because ErrorPart (uniquely among AgentPart variants)
|
|
74
|
-
* carries no `durationMs` field at all — that stricter constraint trips TS's weak-type check
|
|
75
|
-
* ("no properties in common") the moment this is called with the full AgentPart union. */
|
|
76
|
-
function withoutDurationMs<T extends Record<string, unknown>>(
|
|
77
|
-
part: T,
|
|
78
|
-
): DistributiveOmit<T, 'durationMs'> {
|
|
79
|
-
const { durationMs: _durationMs, ...rest } = part
|
|
80
|
-
return rest as DistributiveOmit<T, 'durationMs'>
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
describe('aiSdkTransport — snapshot→delta diffing', () => {
|
|
84
|
-
test('yields a synthesized start part first, with a deterministic id, then diffs text deltas across snapshots with an authoritative offset', async () => {
|
|
85
|
-
const chunks: UIMessageChunk[] = [
|
|
86
|
-
{ type: 'text-start', id: 't1' },
|
|
87
|
-
{ type: 'text-delta', id: 't1', delta: 'Hel' },
|
|
88
|
-
{ type: 'text-delta', id: 't1', delta: 'lo' },
|
|
89
|
-
{ type: 'text-end', id: 't1' },
|
|
90
|
-
]
|
|
91
|
-
const transport = aiSdkTransport({
|
|
92
|
-
api: '/api/chat',
|
|
93
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
94
|
-
})
|
|
95
|
-
const parts = await collect(transport.stream('hi'))
|
|
96
|
-
|
|
97
|
-
expect(parts[0]?.type).toBe('start')
|
|
98
|
-
expect(typeof (parts[0] as { id: string }).id).toBe('string')
|
|
99
|
-
const textParts = parts.filter(
|
|
100
|
-
(p): p is { id: string; type: 'text'; text: string; offset: number } => p.type === 'text',
|
|
101
|
-
)
|
|
102
|
-
// AI SDK's snapshot carries the FULL accumulated text at every write — the module's whole job
|
|
103
|
-
// is re-deriving the two incremental deltas ('Hel' then 'lo') rather than replaying 'Hello'
|
|
104
|
-
// twice or once whole. Both deltas share ONE id (the same message-part index) so mergePart
|
|
105
|
-
// splices them into a single TextPart; `offset` is the length of the previously-seen text.
|
|
106
|
-
expect(textParts.map((p) => p.text)).toEqual(['Hel', 'lo'])
|
|
107
|
-
expect(textParts.map((p) => p.offset)).toEqual([0, 3])
|
|
108
|
-
expect(new Set(textParts.map((p) => p.id)).size).toBe(1)
|
|
109
|
-
expect(textParts.reduce((acc, p) => acc + p.text, '')).toBe('Hello')
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
// F1: withPartIds is only ever a normalizer for DRAFTS (id-less parts). aiSdkTransport mints its
|
|
113
|
-
// own deterministic, content-stable ids for every part it yields (`${chatId}#${index}`,
|
|
114
|
-
// `tool#${toolCallId}`, `${chatId}#start`), so wrapping its output in withPartIds must be a pure
|
|
115
|
-
// no-op passthrough — proves the two hooks wiring withPartIds in unconditionally does not disturb
|
|
116
|
-
// a transport that already identifies its own parts.
|
|
117
|
-
test('withPartIds is a no-op over aiSdkTransport output — every part already carries a stable id', async () => {
|
|
118
|
-
const chunks: UIMessageChunk[] = [
|
|
119
|
-
{ type: 'text-start', id: 't1' },
|
|
120
|
-
{ type: 'text-delta', id: 't1', delta: 'Hel' },
|
|
121
|
-
{ type: 'text-delta', id: 't1', delta: 'lo' },
|
|
122
|
-
{ type: 'text-end', id: 't1' },
|
|
123
|
-
{ type: 'tool-input-start', toolCallId: 'call-1', toolName: 'search' },
|
|
124
|
-
{ type: 'tool-input-available', toolCallId: 'call-1', toolName: 'search', input: { q: 'x' } },
|
|
125
|
-
{ type: 'tool-output-available', toolCallId: 'call-1', output: { hits: 3 } },
|
|
126
|
-
]
|
|
127
|
-
|
|
128
|
-
// Same transport instance for both sides — aiSdkTransport mints its chat id at CONSTRUCTION
|
|
129
|
-
// time, so two separately-constructed transports would mint two different chat ids and
|
|
130
|
-
// trivially fail to match on that alone, masking whether withPartIds itself changed anything.
|
|
131
|
-
const transport = aiSdkTransport({
|
|
132
|
-
api: '/api/chat',
|
|
133
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
134
|
-
})
|
|
135
|
-
const direct = await collect(transport.stream('hi'))
|
|
136
|
-
const wrapped = await collect(withPartIds('unrelated-run-id', transport.stream('hi')))
|
|
137
|
-
|
|
138
|
-
expect(wrapped.map(withoutDurationMs)).toEqual(direct.map(withoutDurationMs))
|
|
139
|
-
// Every part already had an id — withPartIds' counter never had anything id-less to stamp.
|
|
140
|
-
expect(direct.every((part) => typeof (part as { id: string }).id === 'string')).toBe(true)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
test('diffs reasoning deltas the same way as text, with the same id/offset mechanism', async () => {
|
|
144
|
-
const chunks: UIMessageChunk[] = [
|
|
145
|
-
{ type: 'reasoning-start', id: 'r1' },
|
|
146
|
-
{ type: 'reasoning-delta', id: 'r1', delta: 'thinking ' },
|
|
147
|
-
{ type: 'reasoning-delta', id: 'r1', delta: 'more' },
|
|
148
|
-
{ type: 'reasoning-end', id: 'r1' },
|
|
149
|
-
]
|
|
150
|
-
const transport = aiSdkTransport({
|
|
151
|
-
api: '/api/chat',
|
|
152
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
153
|
-
})
|
|
154
|
-
const parts = await collect(transport.stream('hi'))
|
|
155
|
-
|
|
156
|
-
const reasoningParts = parts.filter((p) => p.type === 'reasoning') as {
|
|
157
|
-
id: string
|
|
158
|
-
text: string
|
|
159
|
-
offset: number
|
|
160
|
-
}[]
|
|
161
|
-
expect(reasoningParts.map((p) => p.text)).toEqual(['thinking ', 'more'])
|
|
162
|
-
expect(reasoningParts.map((p) => p.offset)).toEqual([0, 9])
|
|
163
|
-
expect(new Set(reasoningParts.map((p) => p.id)).size).toBe(1)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
test('emits a source part exactly once for a source-url chunk, with a deterministic id', async () => {
|
|
167
|
-
const chunks: UIMessageChunk[] = [
|
|
168
|
-
{ type: 'source-url', sourceId: 's1', url: 'https://example.com', title: 'Example' },
|
|
169
|
-
]
|
|
170
|
-
const transport = aiSdkTransport({
|
|
171
|
-
api: '/api/chat',
|
|
172
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
173
|
-
})
|
|
174
|
-
const parts = await collect(transport.stream('hi'))
|
|
175
|
-
|
|
176
|
-
const sourceParts = parts.filter((p) => p.type === 'source')
|
|
177
|
-
expect(sourceParts).toHaveLength(1)
|
|
178
|
-
expect(sourceParts[0]).toMatchObject({
|
|
179
|
-
type: 'source',
|
|
180
|
-
url: 'https://example.com',
|
|
181
|
-
title: 'Example',
|
|
182
|
-
})
|
|
183
|
-
expect(typeof (sourceParts[0] as { id: string }).id).toBe('string')
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
test('a tool call progresses input-streaming → input-available → output-available, one draft per state, addressed by tool#<toolCallId>', async () => {
|
|
187
|
-
const chunks: UIMessageChunk[] = [
|
|
188
|
-
{ type: 'tool-input-start', toolCallId: 'call-1', toolName: 'search' },
|
|
189
|
-
{ type: 'tool-input-available', toolCallId: 'call-1', toolName: 'search', input: { q: 'x' } },
|
|
190
|
-
{ type: 'tool-output-available', toolCallId: 'call-1', output: { hits: 3 } },
|
|
191
|
-
]
|
|
192
|
-
const transport = aiSdkTransport({
|
|
193
|
-
api: '/api/chat',
|
|
194
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
195
|
-
})
|
|
196
|
-
const parts = await collect(transport.stream('hi'))
|
|
197
|
-
|
|
198
|
-
const toolParts = parts.filter((p) => p.type === 'tool')
|
|
199
|
-
expect(toolParts.every((p) => (p as { id: string }).id === 'tool#call-1')).toBe(true)
|
|
200
|
-
// Annotated so each element is checked individually against the ToolCallPart union — passed
|
|
201
|
-
// inline, TS collapses the array's contextual element type and rejects the settled states'
|
|
202
|
-
// extra `input`/`output` fields against the first (narrowest) member it picks.
|
|
203
|
-
const expectedToolParts: DistributiveOmit<ToolCallPart, 'durationMs'>[] = [
|
|
204
|
-
{
|
|
205
|
-
id: 'tool#call-1',
|
|
206
|
-
type: 'tool',
|
|
207
|
-
state: 'input-streaming',
|
|
208
|
-
toolName: 'search',
|
|
209
|
-
toolCallId: 'call-1',
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
id: 'tool#call-1',
|
|
213
|
-
type: 'tool',
|
|
214
|
-
state: 'input-available',
|
|
215
|
-
toolName: 'search',
|
|
216
|
-
toolCallId: 'call-1',
|
|
217
|
-
input: { q: 'x' },
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
id: 'tool#call-1',
|
|
221
|
-
type: 'tool',
|
|
222
|
-
state: 'output-available',
|
|
223
|
-
toolName: 'search',
|
|
224
|
-
toolCallId: 'call-1',
|
|
225
|
-
input: { q: 'x' },
|
|
226
|
-
output: { hits: 3 },
|
|
227
|
-
},
|
|
228
|
-
]
|
|
229
|
-
expect(toolParts.map(withoutDurationMs)).toEqual(expectedToolParts)
|
|
230
|
-
// durationMs is only set once the call reaches a terminal state (output-available here).
|
|
231
|
-
expect((toolParts[0] as { durationMs?: number }).durationMs).toBeUndefined()
|
|
232
|
-
expect(typeof (toolParts.at(-1) as { durationMs?: number }).durationMs).toBe('number')
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
test('a repeated chunk carrying the SAME tool state is deduped — nothing new to report', async () => {
|
|
236
|
-
const chunks: UIMessageChunk[] = [
|
|
237
|
-
{ type: 'tool-input-start', toolCallId: 'call-1', toolName: 'search' },
|
|
238
|
-
{ type: 'tool-input-available', toolCallId: 'call-1', toolName: 'search', input: { q: 'x' } },
|
|
239
|
-
// Redundant re-send of the identical input-available state — must not double-emit.
|
|
240
|
-
{ type: 'tool-input-available', toolCallId: 'call-1', toolName: 'search', input: { q: 'x' } },
|
|
241
|
-
{ type: 'tool-output-available', toolCallId: 'call-1', output: { hits: 3 } },
|
|
242
|
-
]
|
|
243
|
-
const transport = aiSdkTransport({
|
|
244
|
-
api: '/api/chat',
|
|
245
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
246
|
-
})
|
|
247
|
-
const parts = await collect(transport.stream('hi'))
|
|
248
|
-
|
|
249
|
-
const toolParts = parts.filter((p) => p.type === 'tool')
|
|
250
|
-
// input-streaming once, input-available once (redundant resend deduped), output-available
|
|
251
|
-
// once — three states, not four.
|
|
252
|
-
expect(toolParts).toHaveLength(3)
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
test('a preliminary output-available result emits EVERY refinement, not just the first (bug: state-equality alone would freeze on the first snapshot)', async () => {
|
|
256
|
-
const chunks: UIMessageChunk[] = [
|
|
257
|
-
{ type: 'tool-input-start', toolCallId: 'call-1', toolName: 'search' },
|
|
258
|
-
{ type: 'tool-input-available', toolCallId: 'call-1', toolName: 'search', input: { q: 'x' } },
|
|
259
|
-
{
|
|
260
|
-
type: 'tool-output-available',
|
|
261
|
-
toolCallId: 'call-1',
|
|
262
|
-
output: { hits: 1 },
|
|
263
|
-
preliminary: true,
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
type: 'tool-output-available',
|
|
267
|
-
toolCallId: 'call-1',
|
|
268
|
-
output: { hits: 5 },
|
|
269
|
-
preliminary: true,
|
|
270
|
-
},
|
|
271
|
-
{ type: 'tool-output-available', toolCallId: 'call-1', output: { hits: 5, done: true } },
|
|
272
|
-
]
|
|
273
|
-
const transport = aiSdkTransport({
|
|
274
|
-
api: '/api/chat',
|
|
275
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
276
|
-
})
|
|
277
|
-
const parts = await collect(transport.stream('hi'))
|
|
278
|
-
|
|
279
|
-
const outputs = parts
|
|
280
|
-
.filter((p) => p.type === 'tool' && p.state === 'output-available')
|
|
281
|
-
.map((p) => (p as { output: unknown; preliminary?: boolean }).output)
|
|
282
|
-
// All THREE refinements surfaced — a naive `prev.state === curr.state` dedup would have
|
|
283
|
-
// dropped the second and third (state stays 'output-available' throughout).
|
|
284
|
-
expect(outputs).toEqual([{ hits: 1 }, { hits: 5 }, { hits: 5, done: true }])
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
test('approval-requested → approval-responded carries the nested approval envelope through verbatim (isAutomatic and signature survive)', async () => {
|
|
288
|
-
const chunks: UIMessageChunk[] = [
|
|
289
|
-
{ type: 'tool-input-start', toolCallId: 'call-1', toolName: 'delete-file' },
|
|
290
|
-
{
|
|
291
|
-
type: 'tool-input-available',
|
|
292
|
-
toolCallId: 'call-1',
|
|
293
|
-
toolName: 'delete-file',
|
|
294
|
-
input: { path: '/x' },
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
type: 'tool-approval-request',
|
|
298
|
-
approvalId: 'appr-1',
|
|
299
|
-
toolCallId: 'call-1',
|
|
300
|
-
isAutomatic: true,
|
|
301
|
-
signature: 'sig-xyz',
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
type: 'tool-approval-response',
|
|
305
|
-
approvalId: 'appr-1',
|
|
306
|
-
approved: true,
|
|
307
|
-
reason: 'looks safe',
|
|
308
|
-
},
|
|
309
|
-
]
|
|
310
|
-
const transport = aiSdkTransport({
|
|
311
|
-
api: '/api/chat',
|
|
312
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
313
|
-
})
|
|
314
|
-
const parts = await collect(transport.stream('hi'))
|
|
315
|
-
|
|
316
|
-
const toolParts = parts.filter((p) => p.type === 'tool') as {
|
|
317
|
-
state: string
|
|
318
|
-
approval?: {
|
|
319
|
-
id: string
|
|
320
|
-
approved?: boolean
|
|
321
|
-
reason?: string
|
|
322
|
-
isAutomatic?: boolean
|
|
323
|
-
signature?: string
|
|
324
|
-
}
|
|
325
|
-
}[]
|
|
326
|
-
|
|
327
|
-
const requested = toolParts.find((p) => p.state === 'approval-requested')
|
|
328
|
-
// signature only exists on the SDK's approval-requested envelope — this pins that OUR diffing
|
|
329
|
-
// does not drop it (a flattened `approvalId`/`approved` shape, the spec's own error, would).
|
|
330
|
-
expect(requested?.approval).toEqual({ id: 'appr-1', isAutomatic: true, signature: 'sig-xyz' })
|
|
331
|
-
|
|
332
|
-
const responded = toolParts.find((p) => p.state === 'approval-responded')
|
|
333
|
-
// AI SDK's OWN runtime carries `isAutomatic` forward into approval-responded but drops
|
|
334
|
-
// `signature` there (verified against dist/index.js) — this is the SDK's behavior, not a bug
|
|
335
|
-
// in this diffing layer; what this pins is that basalt passes whatever the SDK gives it
|
|
336
|
-
// through unflattened, not that basalt itself re-adds a dropped field.
|
|
337
|
-
expect(responded?.approval).toEqual({
|
|
338
|
-
id: 'appr-1',
|
|
339
|
-
approved: true,
|
|
340
|
-
reason: 'looks safe',
|
|
341
|
-
isAutomatic: true,
|
|
342
|
-
})
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
test('output-denied is unambiguously terminal — no output, no errorText, just the explicit state + a settled durationMs', async () => {
|
|
346
|
-
const chunks: UIMessageChunk[] = [
|
|
347
|
-
{ type: 'tool-input-start', toolCallId: 'call-2', toolName: 'delete-file' },
|
|
348
|
-
{
|
|
349
|
-
type: 'tool-input-available',
|
|
350
|
-
toolCallId: 'call-2',
|
|
351
|
-
toolName: 'delete-file',
|
|
352
|
-
input: { path: '/etc' },
|
|
353
|
-
},
|
|
354
|
-
{ type: 'tool-approval-request', approvalId: 'appr-2', toolCallId: 'call-2' },
|
|
355
|
-
{ type: 'tool-approval-response', approvalId: 'appr-2', approved: false, reason: 'blocked' },
|
|
356
|
-
{ type: 'tool-output-denied', toolCallId: 'call-2' },
|
|
357
|
-
]
|
|
358
|
-
const transport = aiSdkTransport({
|
|
359
|
-
api: '/api/chat',
|
|
360
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
361
|
-
})
|
|
362
|
-
const parts = await collect(transport.stream('hi'))
|
|
363
|
-
|
|
364
|
-
const toolParts = parts.filter((p) => p.type === 'tool')
|
|
365
|
-
const denied = toolParts.at(-1) as {
|
|
366
|
-
state: string
|
|
367
|
-
output?: unknown
|
|
368
|
-
errorText?: unknown
|
|
369
|
-
input: unknown
|
|
370
|
-
approval?: unknown
|
|
371
|
-
durationMs?: number
|
|
372
|
-
}
|
|
373
|
-
expect(denied.state).toBe('output-denied')
|
|
374
|
-
expect('output' in denied).toBe(false)
|
|
375
|
-
expect('errorText' in denied).toBe(false)
|
|
376
|
-
expect(denied.input).toEqual({ path: '/etc' })
|
|
377
|
-
expect(denied.approval).toEqual({ id: 'appr-2', approved: false, reason: 'blocked' })
|
|
378
|
-
expect(typeof denied.durationMs).toBe('number')
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
// ToolCallPart's `output-error` state carries a dedicated `errorText` field (the SDK's own field
|
|
382
|
-
// name — there is no field named `error` anywhere in the union). Stuffing the failure into
|
|
383
|
-
// `output` is gone: `output-error` doesn't even have an `output` field to smuggle it through.
|
|
384
|
-
test('output-error surfaces a dedicated `errorText` field, not `output`, with input carried forward', async () => {
|
|
385
|
-
const chunks: UIMessageChunk[] = [
|
|
386
|
-
{ type: 'tool-input-start', toolCallId: 'call-1', toolName: 'search' },
|
|
387
|
-
{ type: 'tool-input-available', toolCallId: 'call-1', toolName: 'search', input: { q: 'x' } },
|
|
388
|
-
{ type: 'tool-output-error', toolCallId: 'call-1', errorText: 'boom' },
|
|
389
|
-
]
|
|
390
|
-
const transport = aiSdkTransport({
|
|
391
|
-
api: '/api/chat',
|
|
392
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
393
|
-
})
|
|
394
|
-
const parts = await collect(transport.stream('hi'))
|
|
395
|
-
|
|
396
|
-
const toolParts = parts.filter((p) => p.type === 'tool')
|
|
397
|
-
const failed = toolParts.at(-1)
|
|
398
|
-
expect(withoutDurationMs(failed as { durationMs?: number })).toEqual({
|
|
399
|
-
id: 'tool#call-1',
|
|
400
|
-
type: 'tool',
|
|
401
|
-
state: 'output-error',
|
|
402
|
-
toolName: 'search',
|
|
403
|
-
toolCallId: 'call-1',
|
|
404
|
-
input: { q: 'x' },
|
|
405
|
-
errorText: 'boom',
|
|
406
|
-
})
|
|
407
|
-
expect('output' in (failed as object)).toBe(false)
|
|
408
|
-
})
|
|
409
|
-
|
|
410
|
-
test('a dynamic tool call yields its REAL toolName, not the literal "dynamic-tool"', async () => {
|
|
411
|
-
const chunks: UIMessageChunk[] = [
|
|
412
|
-
{ type: 'tool-input-start', toolCallId: 'call-4', toolName: 'custom-lookup', dynamic: true },
|
|
413
|
-
{
|
|
414
|
-
type: 'tool-input-available',
|
|
415
|
-
toolCallId: 'call-4',
|
|
416
|
-
toolName: 'custom-lookup',
|
|
417
|
-
input: { id: 7 },
|
|
418
|
-
dynamic: true,
|
|
419
|
-
},
|
|
420
|
-
{ type: 'tool-output-available', toolCallId: 'call-4', output: { found: true } },
|
|
421
|
-
]
|
|
422
|
-
const transport = aiSdkTransport({
|
|
423
|
-
api: '/api/chat',
|
|
424
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
425
|
-
})
|
|
426
|
-
const parts = await collect(transport.stream('hi'))
|
|
427
|
-
|
|
428
|
-
const toolParts = parts.filter((p) => p.type === 'tool') as { toolName: string; id: string }[]
|
|
429
|
-
expect(toolParts.length).toBeGreaterThan(0)
|
|
430
|
-
expect(toolParts.every((p) => p.toolName === 'custom-lookup')).toBe(true)
|
|
431
|
-
expect(toolParts.every((p) => p.id === 'tool#call-4')).toBe(true)
|
|
432
|
-
})
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
describe('aiSdkTransport — signal.aborted stops the yield', () => {
|
|
436
|
-
test('once the caller aborts, no further parts are yielded even though more remain', async () => {
|
|
437
|
-
const chunks: UIMessageChunk[] = [
|
|
438
|
-
{ type: 'text-start', id: 't1' },
|
|
439
|
-
{ type: 'text-delta', id: 't1', delta: 'a' },
|
|
440
|
-
{ type: 'text-delta', id: 't1', delta: 'b' },
|
|
441
|
-
{ type: 'text-end', id: 't1' },
|
|
442
|
-
]
|
|
443
|
-
const transport = aiSdkTransport({
|
|
444
|
-
api: '/api/chat',
|
|
445
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
446
|
-
})
|
|
447
|
-
const controller = new AbortController()
|
|
448
|
-
const gen = transport.stream('hi', controller.signal)
|
|
449
|
-
|
|
450
|
-
const start = await gen.next()
|
|
451
|
-
expect(start.value).toMatchObject({ type: 'start' })
|
|
452
|
-
const firstDelta = await gen.next()
|
|
453
|
-
expect(firstDelta.value).toMatchObject({ type: 'text', text: 'a', offset: 0 })
|
|
454
|
-
|
|
455
|
-
// Let AI SDK's background readUIMessageStream pump (kicked off inside diffChunkStream, and
|
|
456
|
-
// NOT gated on our own reads) fully settle before aborting — this is inherent to the library,
|
|
457
|
-
// not to the AgentTransport code under test: aborting while chunks are still in flight through
|
|
458
|
-
// that pump races diffChunkStream's early return against the pump's own stream teardown. A
|
|
459
|
-
// short real-time wait avoids exercising that unrelated race so this test isolates exactly
|
|
460
|
-
// ai-sdk-transport.ts's `if (signal?.aborted) return`.
|
|
461
|
-
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
462
|
-
|
|
463
|
-
controller.abort()
|
|
464
|
-
const afterAbort = await gen.next()
|
|
465
|
-
expect(afterAbort.done).toBe(true)
|
|
466
|
-
expect(afterAbort.value).toBeUndefined()
|
|
467
|
-
})
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
describe('aiSdkTransport — deterministic chat-id binding', () => {
|
|
471
|
-
test('the fixed (non-forThread) transport mints its chat id ONCE, stable across repeated stream() calls', async () => {
|
|
472
|
-
// Never actually reached — the synthesized StartPart is yielded before any fetch call, so a
|
|
473
|
-
// generator that never advances past its first `.next()` never touches the network.
|
|
474
|
-
const transport = aiSdkTransport({
|
|
475
|
-
api: '/api/chat',
|
|
476
|
-
fetch: mockFetch(() => {
|
|
477
|
-
throw new Error('must not be called — only the first, pre-network yield is read')
|
|
478
|
-
}),
|
|
479
|
-
})
|
|
480
|
-
|
|
481
|
-
const first = await transport.stream('a').next()
|
|
482
|
-
const second = await transport.stream('b').next()
|
|
483
|
-
expect(first.value).toEqual(second.value)
|
|
484
|
-
expect((first.value as { runId: string }).runId).toBe((second.value as { runId: string }).runId)
|
|
485
|
-
})
|
|
486
|
-
|
|
487
|
-
test('.forThread(id) binds the StartPart runId/resumeToken/id deterministically to the given id', async () => {
|
|
488
|
-
const transport = aiSdkTransport({
|
|
489
|
-
api: '/api/chat',
|
|
490
|
-
fetch: mockFetch(() => {
|
|
491
|
-
throw new Error('must not be called — only the first, pre-network yield is read')
|
|
492
|
-
}),
|
|
493
|
-
})
|
|
494
|
-
const bound = transport.forThread('caller-supplied-thread-id')
|
|
495
|
-
|
|
496
|
-
const first = await bound.stream('first turn').next()
|
|
497
|
-
const second = await bound.stream('second turn').next()
|
|
498
|
-
|
|
499
|
-
expect(first.value).toEqual({
|
|
500
|
-
id: 'caller-supplied-thread-id#start',
|
|
501
|
-
type: 'start',
|
|
502
|
-
runId: 'caller-supplied-thread-id',
|
|
503
|
-
resumeToken: 'caller-supplied-thread-id',
|
|
504
|
-
})
|
|
505
|
-
// Same across repeated turns on the same bound transport — the id is NOT re-minted per call.
|
|
506
|
-
expect(second.value).toEqual(first.value)
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
test('two different .forThread() ids produce two different StartPart runIds/ids', async () => {
|
|
510
|
-
const transport = aiSdkTransport({
|
|
511
|
-
api: '/api/chat',
|
|
512
|
-
fetch: mockFetch(() => {
|
|
513
|
-
throw new Error('must not be called — only the first, pre-network yield is read')
|
|
514
|
-
}),
|
|
515
|
-
})
|
|
516
|
-
|
|
517
|
-
const a = await transport.forThread('thread-a').stream('hi').next()
|
|
518
|
-
const b = await transport.forThread('thread-b').stream('hi').next()
|
|
519
|
-
|
|
520
|
-
expect(a.value).toEqual({
|
|
521
|
-
id: 'thread-a#start',
|
|
522
|
-
type: 'start',
|
|
523
|
-
runId: 'thread-a',
|
|
524
|
-
resumeToken: 'thread-a',
|
|
525
|
-
})
|
|
526
|
-
expect(b.value).toEqual({
|
|
527
|
-
id: 'thread-b#start',
|
|
528
|
-
type: 'start',
|
|
529
|
-
runId: 'thread-b',
|
|
530
|
-
resumeToken: 'thread-b',
|
|
531
|
-
})
|
|
532
|
-
})
|
|
533
|
-
})
|
|
534
|
-
|
|
535
|
-
describe('aiSdkTransport — deterministic ids and replay idempotency', () => {
|
|
536
|
-
test('the same snapshot sequence produces the same ids twice', async () => {
|
|
537
|
-
const chunks: UIMessageChunk[] = [
|
|
538
|
-
{ type: 'text-start', id: 't1' },
|
|
539
|
-
{ type: 'text-delta', id: 't1', delta: 'Hello' },
|
|
540
|
-
{ type: 'text-end', id: 't1' },
|
|
541
|
-
{ type: 'tool-input-start', toolCallId: 'call-1', toolName: 'search' },
|
|
542
|
-
{ type: 'tool-input-available', toolCallId: 'call-1', toolName: 'search', input: { q: 'x' } },
|
|
543
|
-
]
|
|
544
|
-
const transport = aiSdkTransport({
|
|
545
|
-
api: '/api/chat',
|
|
546
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
547
|
-
}).forThread('thread-x')
|
|
548
|
-
|
|
549
|
-
const firstIds = (await collect(transport.stream('hi'))).map((p) => (p as { id: string }).id)
|
|
550
|
-
const secondIds = (await collect(transport.stream('hi'))).map((p) => (p as { id: string }).id)
|
|
551
|
-
|
|
552
|
-
expect(secondIds).toEqual(firstIds)
|
|
553
|
-
expect(firstIds).toEqual(['thread-x#start', 'thread-x#0', 'tool#call-1', 'tool#call-1'])
|
|
554
|
-
})
|
|
555
|
-
|
|
556
|
-
test('replaying the identical delta sequence through mergePart converges — it does not double the parts array (the defect this lane exists to make impossible)', async () => {
|
|
557
|
-
const chunks: UIMessageChunk[] = [
|
|
558
|
-
{ type: 'text-start', id: 't1' },
|
|
559
|
-
{ type: 'text-delta', id: 't1', delta: 'Hel' },
|
|
560
|
-
{ type: 'text-delta', id: 't1', delta: 'lo' },
|
|
561
|
-
{ type: 'text-end', id: 't1' },
|
|
562
|
-
]
|
|
563
|
-
const transport = aiSdkTransport({
|
|
564
|
-
api: '/api/chat',
|
|
565
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
566
|
-
}).forThread('thread-x')
|
|
567
|
-
|
|
568
|
-
const firstPass = await collect(transport.stream('hi'))
|
|
569
|
-
let parts: AgentPart[] = []
|
|
570
|
-
for (const part of firstPass) parts = mergePart(parts, part)
|
|
571
|
-
const afterFirstPass = parts
|
|
572
|
-
expect(afterFirstPass).toHaveLength(2) // start + one merged text part
|
|
573
|
-
|
|
574
|
-
// Simulate a resume()/replay that re-diffs the SAME chunk sequence from scratch (a fresh
|
|
575
|
-
// diffChunkStream call starts `prev = undefined` again, so the first snapshot it sees carries
|
|
576
|
-
// the FULL accumulated text at offset 0 — exactly what a real reconnectToStream reconnect
|
|
577
|
-
// looks like). Same bound transport (same chatId) ⇒ identical ids.
|
|
578
|
-
const secondPass = await collect(transport.stream('hi'))
|
|
579
|
-
for (const part of secondPass) parts = mergePart(parts, part)
|
|
580
|
-
|
|
581
|
-
expect(parts).toEqual(afterFirstPass)
|
|
582
|
-
expect(parts).toHaveLength(2)
|
|
583
|
-
const merged = parts.find((p) => p.type === 'text') as { text: string }
|
|
584
|
-
expect(merged.text).toBe('Hello')
|
|
585
|
-
|
|
586
|
-
// What this guards against: naive array-append accumulation (`[...prev, part]`, this lane's
|
|
587
|
-
// predecessor) would have appended the second pass's deltas onto the first pass's, doubling
|
|
588
|
-
// (and garbling) the rendered text instead of converging.
|
|
589
|
-
const naiveAppendTextParts = [...firstPass, ...secondPass].filter((p) => p.type === 'text')
|
|
590
|
-
expect(naiveAppendTextParts.length).toBeGreaterThan(
|
|
591
|
-
parts.filter((p) => p.type === 'text').length,
|
|
592
|
-
)
|
|
593
|
-
})
|
|
594
|
-
})
|
|
595
|
-
|
|
596
|
-
describe('aiSdkTransport — id minting under degraded crypto', () => {
|
|
597
|
-
test('the fixed chatId (mintThreadId, low collision cost) mints fine with no usable crypto at all — construction never throws', () => {
|
|
598
|
-
const originalCrypto = globalThis.crypto
|
|
599
|
-
Object.defineProperty(globalThis, 'crypto', { value: undefined, configurable: true })
|
|
600
|
-
try {
|
|
601
|
-
expect(() =>
|
|
602
|
-
aiSdkTransport({
|
|
603
|
-
api: '/api/chat',
|
|
604
|
-
fetch: mockFetch(() => {
|
|
605
|
-
throw new Error('must not be called')
|
|
606
|
-
}),
|
|
607
|
-
}),
|
|
608
|
-
).not.toThrow()
|
|
609
|
-
} finally {
|
|
610
|
-
Object.defineProperty(globalThis, 'crypto', { value: originalCrypto, configurable: true })
|
|
611
|
-
}
|
|
612
|
-
})
|
|
613
|
-
|
|
614
|
-
test('the outbound user message id (mintMessageId, an idempotency key on the AI SDK backend) works via getRandomValues when randomUUID is unavailable', async () => {
|
|
615
|
-
const chunks: UIMessageChunk[] = [
|
|
616
|
-
{ type: 'text-start', id: 't1' },
|
|
617
|
-
{ type: 'text-end', id: 't1' },
|
|
618
|
-
]
|
|
619
|
-
const transport = aiSdkTransport({
|
|
620
|
-
api: '/api/chat',
|
|
621
|
-
fetch: mockFetch(async () => scriptedResponse(chunks)),
|
|
622
|
-
})
|
|
623
|
-
|
|
624
|
-
const originalCrypto = globalThis.crypto
|
|
625
|
-
Object.defineProperty(globalThis, 'crypto', {
|
|
626
|
-
value: { getRandomValues: originalCrypto.getRandomValues.bind(originalCrypto) },
|
|
627
|
-
configurable: true,
|
|
628
|
-
})
|
|
629
|
-
try {
|
|
630
|
-
const parts = await collect(transport.stream('hi'))
|
|
631
|
-
expect(parts[0]?.type).toBe('start')
|
|
632
|
-
} finally {
|
|
633
|
-
Object.defineProperty(globalThis, 'crypto', { value: originalCrypto, configurable: true })
|
|
634
|
-
}
|
|
635
|
-
})
|
|
636
|
-
|
|
637
|
-
test('the outbound user message id THROWS on a host with no usable crypto at all, rather than silently colliding', async () => {
|
|
638
|
-
const transport = aiSdkTransport({
|
|
639
|
-
api: '/api/chat',
|
|
640
|
-
fetch: mockFetch(() => {
|
|
641
|
-
throw new Error('must not be called — must throw before any network call')
|
|
642
|
-
}),
|
|
643
|
-
})
|
|
644
|
-
const gen = transport.stream('hi')
|
|
645
|
-
|
|
646
|
-
// The synthesized StartPart is yielded first, before mintMessageId is ever reached.
|
|
647
|
-
const start = await gen.next()
|
|
648
|
-
expect(start.value).toMatchObject({ type: 'start' })
|
|
649
|
-
|
|
650
|
-
const originalCrypto = globalThis.crypto
|
|
651
|
-
Object.defineProperty(globalThis, 'crypto', { value: undefined, configurable: true })
|
|
652
|
-
try {
|
|
653
|
-
await expect(gen.next()).rejects.toThrow(/idempotency key/)
|
|
654
|
-
} finally {
|
|
655
|
-
Object.defineProperty(globalThis, 'crypto', { value: originalCrypto, configurable: true })
|
|
656
|
-
}
|
|
657
|
-
})
|
|
658
|
-
})
|