basalt-ui 1.40.3 → 2.0.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 +50 -45
- package/CHANGELOG.md +543 -513
- package/MIGRATING.md +969 -37
- package/README.md +172 -367
- package/agent/rules/basalt-batteries.md +19 -74
- package/agent/rules/basalt-charts.md +21 -95
- package/agent/rules/basalt-controls.md +23 -131
- package/agent/rules/basalt-mantine.md +26 -99
- package/agent/rules/basalt-state.md +21 -103
- package/agent/rules/basalt-tokens.md +33 -114
- package/agent/skills/basalt-app/SKILL.md +229 -72
- package/agent/skills/basalt-charts/SKILL.md +23 -57
- package/agent/skills/basalt-design/SKILL.md +12 -70
- package/agent/templates/AGENTS-block.md.tpl +30 -0
- package/agent/templates/DESIGN.md.tpl +2 -2
- package/agent/templates/__root.tpl +9 -16
- package/agent/templates/main.tpl +21 -0
- package/agent/templates/nav.tpl +18 -0
- package/configs/check.yml +1 -1
- package/configs/oxlint-basalt.json +4 -10
- package/configs/oxlint-plugin.js +468 -847
- package/configs/oxlint-plugin.test.ts +662 -710
- package/configs/oxlint.json +10 -2
- package/dist/agent/ai-sdk-transport.d.ts +19 -7
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +10 -7
- package/dist/{agent-chat → agent/chat}/composer.d.ts +16 -16
- package/dist/agent/chat/composer.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/composer.js +10 -9
- package/dist/{agent-chat → agent/chat}/message-affordances.d.ts +7 -6
- package/dist/agent/chat/message-affordances.d.ts.map +1 -0
- package/dist/agent/chat/relative-time.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/thread-feed-row.d.ts +18 -16
- package/dist/agent/chat/thread-feed-row.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/thread-feed-row.js +12 -11
- package/dist/{agent-chat → agent/chat}/thread-feed.d.ts +8 -8
- package/dist/agent/chat/thread-feed.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/thread-feed.js +12 -10
- package/dist/{agent-chat → agent/chat}/thread-message.d.ts +12 -9
- package/dist/agent/chat/thread-message.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/thread-message.js +15 -13
- package/dist/{agent-chat → agent/chat}/thread-outcome-card.d.ts +2 -2
- package/dist/agent/chat/thread-outcome-card.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/thread-outcome-card.js +3 -3
- package/dist/{agent-chat → agent/chat}/tool-chip.d.ts +5 -5
- package/dist/agent/chat/tool-chip.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/tool-chip.js +4 -4
- package/dist/agent/chat/use-keyboard-inset.d.ts.map +1 -0
- package/dist/{agent-chat → agent/chat}/use-keyboard-inset.js +1 -1
- package/dist/{agent-chat → agent/chat}/virtualize.d.ts +7 -7
- package/dist/agent/chat/virtualize.d.ts.map +1 -0
- package/dist/agent/error.d.ts +31 -0
- package/dist/agent/error.d.ts.map +1 -0
- package/dist/agent/error.js +18 -0
- package/dist/agent/foreign.d.ts +3 -2
- package/dist/agent/foreign.d.ts.map +1 -1
- package/dist/agent/history.d.ts +2 -36
- package/dist/agent/history.d.ts.map +1 -1
- package/dist/agent/history.js +0 -32
- package/dist/agent/id.d.ts +4 -5
- package/dist/agent/id.d.ts.map +1 -1
- package/dist/agent/index.d.ts +19 -52
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +10 -32
- package/dist/agent/outcome.d.ts +3 -14
- package/dist/agent/outcome.d.ts.map +1 -1
- package/dist/agent/outcome.js +0 -30
- package/dist/agent/part-list.d.ts +3 -3
- package/dist/agent/part-list.d.ts.map +1 -1
- package/dist/agent/parts.d.ts +23 -24
- package/dist/agent/parts.d.ts.map +1 -1
- package/dist/agent/run-committer.d.ts +23 -0
- package/dist/agent/run-committer.d.ts.map +1 -0
- package/dist/agent/run-committer.js +41 -0
- package/dist/agent/run-records.d.ts +77 -0
- package/dist/agent/run-records.d.ts.map +1 -0
- package/dist/agent/run-records.js +69 -0
- package/dist/agent/thread.d.ts +23 -47
- package/dist/agent/thread.d.ts.map +1 -1
- package/dist/agent/thread.js +3 -134
- package/dist/agent/transport.d.ts +6 -69
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/transport.js +0 -18
- package/dist/agent/use-agent-thread-runs.d.ts +63 -26
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +202 -66
- package/dist/charts/BandStrip.d.ts +46 -0
- package/dist/charts/BandStrip.d.ts.map +1 -0
- package/dist/charts/BandStrip.js +151 -0
- package/dist/charts/Bars.d.ts +19 -0
- package/dist/charts/Bars.d.ts.map +1 -0
- package/dist/charts/Bars.js +77 -0
- package/dist/charts/CartesianChart.d.ts +57 -0
- package/dist/charts/CartesianChart.d.ts.map +1 -0
- package/dist/charts/CartesianChart.js +121 -0
- package/dist/charts/ChartCard.d.ts +34 -0
- package/dist/charts/ChartCard.d.ts.map +1 -0
- package/dist/charts/ChartCard.js +115 -0
- package/dist/charts/Donut.d.ts +25 -0
- package/dist/charts/Donut.d.ts.map +1 -0
- package/dist/charts/Donut.js +191 -0
- package/dist/charts/DualPanel.d.ts +47 -0
- package/dist/charts/DualPanel.d.ts.map +1 -0
- package/dist/charts/DualPanel.js +214 -0
- package/dist/charts/Heatmap.d.ts +41 -0
- package/dist/charts/Heatmap.d.ts.map +1 -0
- package/dist/charts/Heatmap.js +216 -0
- package/dist/charts/Lines.d.ts +11 -0
- package/dist/charts/Lines.d.ts.map +1 -0
- package/dist/charts/Lines.js +114 -0
- package/dist/charts/MirroredBars.d.ts +44 -0
- package/dist/charts/MirroredBars.d.ts.map +1 -0
- package/dist/charts/MirroredBars.js +190 -0
- package/dist/charts/Sparkline.d.ts +22 -0
- package/dist/charts/Sparkline.d.ts.map +1 -0
- package/dist/charts/Sparkline.js +129 -0
- package/dist/charts/StackedArea.d.ts +15 -0
- package/dist/charts/StackedArea.d.ts.map +1 -0
- package/dist/charts/StackedArea.js +46 -0
- package/dist/charts/box.d.ts +38 -0
- package/dist/charts/box.d.ts.map +1 -0
- package/dist/charts/box.js +72 -0
- package/dist/charts/card-context.d.ts +3 -0
- package/dist/charts/card-context.d.ts.map +1 -0
- package/dist/charts/card-context.js +7 -0
- package/dist/charts/chart-box.module.css +64 -0
- package/{src/content/article-card.module.css.d.ts → dist/charts/chart-box.module.css.d.ts} +5 -9
- package/dist/charts/chart-card.module.css +25 -0
- package/dist/{dashboard/widget-grid.module.css.d.ts → charts/chart-card.module.css.d.ts} +1 -3
- package/dist/charts/chart-legend.module.css +38 -0
- package/{src/dashboard/widget-grid.module.css.d.ts → dist/charts/chart-legend.module.css.d.ts} +3 -3
- package/dist/charts/compile.d.ts +62 -0
- package/dist/charts/compile.d.ts.map +1 -0
- package/dist/charts/compile.js +386 -0
- package/dist/charts/cursor.d.ts +50 -0
- package/dist/charts/cursor.d.ts.map +1 -0
- package/dist/charts/cursor.js +157 -0
- package/dist/charts/curves.d.ts +12 -0
- package/dist/charts/curves.d.ts.map +1 -0
- package/dist/charts/curves.js +86 -0
- package/dist/charts/donut.module.css +54 -0
- package/dist/{content/article-card.module.css.d.ts → charts/donut.module.css.d.ts} +2 -9
- package/dist/charts/host.d.ts +17 -0
- package/dist/charts/host.d.ts.map +1 -0
- package/dist/charts/host.js +35 -0
- package/dist/charts/index.d.ts +18 -53
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +17 -189
- package/dist/charts/legend.d.ts +23 -0
- package/dist/charts/legend.d.ts.map +1 -0
- package/dist/charts/legend.js +160 -0
- package/dist/charts/marks.d.ts +25 -0
- package/dist/charts/marks.d.ts.map +1 -0
- package/dist/charts/marks.js +128 -0
- package/dist/charts/panel.d.ts +56 -0
- package/dist/charts/panel.d.ts.map +1 -0
- package/dist/charts/panel.js +123 -0
- package/dist/charts/scales.d.ts +44 -0
- package/dist/charts/scales.d.ts.map +1 -0
- package/dist/charts/scales.js +163 -0
- package/dist/charts/sizing.d.ts +12 -0
- package/dist/charts/sizing.d.ts.map +1 -0
- package/dist/charts/sizing.js +33 -0
- package/dist/charts/state.d.ts +17 -0
- package/dist/charts/state.d.ts.map +1 -0
- package/dist/charts/state.js +53 -0
- package/dist/charts/theme.d.ts +38 -19
- package/dist/charts/theme.d.ts.map +1 -1
- package/dist/charts/theme.js +48 -26
- package/dist/charts/types.d.ts +227 -0
- package/dist/charts/types.d.ts.map +1 -0
- package/dist/charts/types.js +4 -0
- package/dist/cli/audit-allows.d.ts +4 -17
- package/dist/cli/audit-allows.d.ts.map +1 -1
- package/dist/cli/config.d.ts +14 -43
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/doctor-upgrade.d.ts +31 -0
- package/dist/cli/doctor-upgrade.d.ts.map +1 -0
- package/dist/cli/doctor-upgrade.js +269 -0
- package/dist/cli/doctor.d.ts +13 -26
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +79 -8
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +2 -4
- package/dist/cli/index.d.ts +68 -135
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +89 -76
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +43 -27
- package/dist/cli/sync.d.ts +5 -21
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +4 -6
- package/dist/cli/tokens-css.d.ts +7 -28
- package/dist/cli/tokens-css.d.ts.map +1 -1
- package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
- package/dist/commands/ShortcutsHelp.js +4 -5
- package/dist/commands/define-commands.d.ts +15 -31
- package/dist/commands/define-commands.d.ts.map +1 -1
- package/dist/commands/define-commands.js +1 -11
- package/dist/commands/define-overlays.d.ts +5 -19
- package/dist/commands/define-overlays.d.ts.map +1 -1
- package/dist/commands/define-overlays.js +3 -11
- package/dist/commands/index.d.ts +10 -40
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +9 -46
- package/dist/{notifications → commands/notifications}/bell.d.ts +1 -1
- package/dist/commands/notifications/bell.d.ts.map +1 -0
- package/dist/{notifications → commands/notifications}/center.d.ts +2 -2
- package/dist/commands/notifications/center.d.ts.map +1 -0
- package/dist/{notifications → commands/notifications}/center.js +2 -2
- package/dist/{notifications → commands/notifications}/define-notifications.d.ts +3 -3
- package/dist/commands/notifications/define-notifications.d.ts.map +1 -0
- package/dist/commands/notifications/notify.d.ts +44 -0
- package/dist/commands/notifications/notify.d.ts.map +1 -0
- package/dist/commands/notifications/notify.js +79 -0
- package/dist/{notifications → commands/notifications}/store.d.ts +2 -0
- package/dist/commands/notifications/store.d.ts.map +1 -0
- package/dist/{notifications → commands/notifications}/store.js +4 -0
- package/dist/commands/projectors.d.ts +1 -131
- package/dist/commands/projectors.d.ts.map +1 -1
- package/dist/commands/projectors.js +2 -73
- package/dist/commands/shell-bridge.d.ts +3 -3
- package/dist/commands/shortcut-format.d.ts +5 -4
- package/dist/commands/shortcut-format.d.ts.map +1 -1
- package/dist/common/errors.d.ts +3 -3
- package/dist/common/use-measured-widths.d.ts.map +1 -1
- package/dist/common/use-measured-widths.js +5 -3
- package/dist/common/validate.d.ts +2 -2
- package/dist/content/article-layout.d.ts.map +1 -1
- package/dist/content/article-layout.js +3 -3
- package/dist/content/article-model.d.ts +1 -46
- package/dist/content/article-model.d.ts.map +1 -1
- package/dist/content/article-model.js +1 -29
- package/dist/content/copy-action.d.ts +1 -1
- package/dist/content/glyphs.d.ts +1 -1
- package/dist/content/index.d.ts +11 -41
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +3 -35
- package/dist/content/markdown.d.ts +1 -1
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +1 -1
- package/dist/content/mermaid.d.ts.map +1 -1
- package/dist/content/mermaid.js +7 -7
- package/dist/content/slug.d.ts +1 -8
- package/dist/content/slug.d.ts.map +1 -1
- package/dist/content/slug.js +1 -8
- package/dist/content/url-hardening.d.ts +2 -2
- package/dist/content/url-hardening.d.ts.map +1 -1
- package/dist/content/url-hardening.js +2 -0
- package/dist/controls/actions.d.ts +16 -1
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/enum-filter.d.ts +1 -1
- package/dist/controls/enum-filter.d.ts.map +1 -1
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-context.js +3 -1
- package/dist/controls/filter-set.js +4 -6
- package/dist/controls/form-layout.d.ts +1 -1
- package/dist/controls/form-layout.d.ts.map +1 -1
- package/dist/controls/form-layout.js +1 -1
- package/dist/controls/glyphs.d.ts +0 -2
- package/dist/controls/glyphs.d.ts.map +1 -1
- package/dist/controls/glyphs.js +0 -7
- package/dist/controls/index.d.ts +12 -17
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +5 -39
- package/dist/controls/multi-select-filter.d.ts +4 -3
- package/dist/controls/multi-select-filter.d.ts.map +1 -1
- package/dist/controls/number-filter.d.ts +4 -3
- package/dist/controls/number-filter.d.ts.map +1 -1
- package/dist/controls/number-filter.js +3 -1
- package/dist/controls/range-filter.d.ts +4 -3
- package/dist/controls/range-filter.d.ts.map +1 -1
- package/dist/controls/search-filter.d.ts +4 -3
- package/dist/controls/search-filter.d.ts.map +1 -1
- package/dist/controls/search-filter.js +19 -14
- package/dist/controls/select-filter.d.ts +4 -3
- package/dist/controls/select-filter.d.ts.map +1 -1
- package/dist/controls/sync-button.d.ts.map +1 -1
- package/dist/controls/sync-button.js +12 -15
- package/dist/controls/toggle-filter.d.ts +4 -3
- package/dist/controls/toggle-filter.d.ts.map +1 -1
- package/dist/controls/uncontrolled.d.ts +12 -0
- package/dist/controls/uncontrolled.d.ts.map +1 -0
- package/dist/controls/view-tabs.d.ts +2 -2
- package/dist/controls/view-tabs.d.ts.map +1 -1
- package/dist/controls-dates/index.d.ts +0 -1
- package/dist/controls-dates/index.d.ts.map +1 -1
- package/dist/dashboard/index.d.ts +7 -9
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +3 -23
- package/dist/dashboard/section.d.ts +4 -6
- package/dist/dashboard/section.d.ts.map +1 -1
- package/dist/dashboard/section.js +4 -10
- package/dist/dashboard/stat-card.d.ts +4 -4
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +3 -3
- package/dist/dashboard/widget-header.module.css +9 -7
- package/dist/dev-dock.d.ts.map +1 -1
- package/dist/dev-dock.js +6 -1
- package/dist/format/date.d.ts +0 -34
- package/dist/format/date.d.ts.map +1 -1
- package/dist/format/date.js +0 -54
- package/dist/format/index.d.ts +7 -22
- package/dist/format/index.d.ts.map +1 -1
- package/dist/format/index.js +2 -43
- package/dist/format/number.d.ts +3 -14
- package/dist/format/number.d.ts.map +1 -1
- package/dist/format/number.js +0 -13
- package/dist/format/shared.d.ts +2 -3
- package/dist/format/shared.d.ts.map +1 -1
- package/dist/forms/index.d.ts +0 -11
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +1 -14
- package/dist/guard/index.d.ts +6 -21
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +27 -115
- package/dist/guard/types.d.ts +7 -18
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +16 -48
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -117
- package/dist/provider/create-router.d.ts +44 -0
- package/dist/provider/create-router.d.ts.map +1 -0
- package/dist/provider/create-router.js +62 -0
- package/dist/provider/hotkeys.d.ts +8 -0
- package/dist/provider/hotkeys.d.ts.map +1 -0
- package/dist/provider/hotkeys.js +50 -0
- package/dist/provider/index.d.ts +33 -110
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +182 -82
- package/dist/provider/lab-theme.d.ts.map +1 -1
- package/dist/provider/route-transition.d.ts +76 -0
- package/dist/provider/route-transition.d.ts.map +1 -0
- package/dist/provider/route-transition.js +98 -0
- package/dist/provider/spotlight-actions.d.ts +10 -0
- package/dist/provider/spotlight-actions.d.ts.map +1 -0
- package/dist/provider/spotlight-actions.js +55 -0
- package/dist/provider/test-router.d.ts +6 -0
- package/dist/provider/test-router.d.ts.map +1 -0
- package/dist/provider/test-router.js +18 -0
- package/dist/query-client.d.ts +4 -20
- package/dist/query-client.d.ts.map +1 -1
- package/dist/query-client.js +2 -2
- package/dist/router-tanstack/index.d.ts +18 -64
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +2 -16
- package/dist/router-tanstack/nav.d.ts +12 -1
- package/dist/router-tanstack/nav.d.ts.map +1 -1
- package/dist/router-tanstack/nav.js +8 -1
- package/dist/router-tanstack/search-store.d.ts +1 -1
- package/dist/router-tanstack/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.d.ts +8 -9
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +20 -1
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +1 -1
- package/dist/shell/index.d.ts +31 -10
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +14 -3
- package/dist/shell/page-aside.d.ts +2 -2
- package/dist/shell/page-aside.d.ts.map +1 -1
- package/dist/shell/page-aside.js +31 -12
- package/dist/shell/page-bar.d.ts +2 -2
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +1 -1
- package/dist/state.d.ts +10 -12
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +4 -6
- package/dist/styles.css +107 -7
- package/dist/surfaces.d.ts +28 -103
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +54 -143
- package/dist/{data → table}/data-table-state.d.ts +1 -1
- package/dist/table/data-table-state.d.ts.map +1 -0
- package/dist/{data → table}/data-table-state.js +4 -3
- package/dist/{data → table}/data-table.d.ts +28 -49
- package/dist/table/data-table.d.ts.map +1 -0
- package/dist/{data → table}/data-table.js +74 -60
- package/dist/table/index.d.ts +18 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/{data/table.js → table/index.js} +1 -1
- package/dist/table/query-branch.d.ts.map +1 -0
- package/dist/table/table-features.d.ts +69 -0
- package/dist/table/table-features.d.ts.map +1 -0
- package/dist/table/table-features.js +49 -0
- package/dist/telemetry.d.ts +77 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +33 -0
- package/dist/theme/index.d.ts +10 -21
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +3 -14
- package/dist/theme/theme-toggle.d.ts.map +1 -1
- package/dist/theme/theme-toggle.js +7 -33
- package/dist/theme/theme-toggle.module.css +30 -0
- package/dist/theme/theme-toggle.module.css.d.ts +10 -0
- package/dist/theme-lab/derive-controls.d.ts.map +1 -1
- package/dist/theme-lab/derive-controls.js +5 -3
- package/dist/theme-lab/derive-state.d.ts.map +1 -1
- package/dist/theme-lab/derive-state.js +2 -1
- package/dist/tokens/core.d.ts +360 -0
- package/dist/tokens/core.d.ts.map +1 -0
- package/dist/tokens/core.js +708 -0
- package/dist/tokens/derive.d.ts +1 -1
- package/dist/tokens/derive.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +5 -373
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +8 -700
- package/dist/tokens/palette.d.ts +2 -6
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +1 -4
- package/dist/vite.d.ts +4 -3
- package/dist/vite.d.ts.map +1 -1
- package/llms.txt +43 -85
- package/package.json +77 -170
- package/src/agent/ai-sdk-transport.ts +57 -37
- package/src/{agent-chat → agent/chat}/composer.tsx +29 -30
- package/src/{agent-chat → agent/chat}/message-affordances.ts +8 -5
- package/src/{agent-chat → agent/chat}/thread-feed-row.tsx +30 -35
- package/src/{agent-chat → agent/chat}/thread-feed.tsx +40 -35
- package/src/{agent-chat → agent/chat}/thread-message.tsx +37 -35
- package/src/{agent-chat → agent/chat}/thread-outcome-card.tsx +5 -5
- package/src/{agent-chat → agent/chat}/tool-chip.tsx +12 -12
- package/src/{agent-chat → agent/chat}/use-keyboard-inset.ts +1 -1
- package/src/{agent-chat → agent/chat}/virtualize.ts +5 -5
- package/src/agent/error.ts +43 -0
- package/src/agent/foreign.ts +3 -2
- package/src/agent/history.ts +2 -107
- package/src/agent/id.ts +4 -5
- package/src/agent/index.ts +23 -112
- package/src/agent/outcome.ts +3 -56
- package/src/agent/part-list.tsx +3 -3
- package/src/agent/parts.ts +23 -22
- package/src/agent/run-committer.ts +64 -0
- package/src/agent/run-records.ts +147 -0
- package/src/agent/thread.ts +24 -231
- package/src/agent/transport.ts +6 -96
- package/src/agent/use-agent-thread-runs.ts +364 -199
- package/src/charts/BandStrip.tsx +276 -0
- package/src/charts/Bars.tsx +141 -0
- package/src/charts/CartesianChart.tsx +206 -0
- package/src/charts/ChartCard.tsx +178 -0
- package/src/charts/Donut.tsx +256 -0
- package/src/charts/DualPanel.tsx +326 -0
- package/src/charts/Heatmap.tsx +282 -0
- package/src/charts/Lines.tsx +151 -0
- package/src/charts/MirroredBars.tsx +303 -0
- package/src/charts/Sparkline.tsx +176 -0
- package/src/charts/StackedArea.tsx +80 -0
- package/src/charts/box.tsx +96 -0
- package/src/charts/card-context.ts +11 -0
- package/src/charts/chart-box.module.css +64 -0
- package/src/charts/chart-box.module.css.d.ts +12 -0
- package/src/charts/chart-card.module.css +25 -0
- package/src/charts/chart-card.module.css.d.ts +8 -0
- package/src/charts/chart-legend.module.css +38 -0
- package/src/charts/chart-legend.module.css.d.ts +10 -0
- package/src/charts/compile.ts +585 -0
- package/src/charts/cursor.ts +282 -0
- package/src/charts/curves.ts +111 -0
- package/src/charts/donut.module.css +54 -0
- package/src/charts/donut.module.css.d.ts +9 -0
- package/src/charts/host.tsx +60 -0
- package/src/charts/index.ts +41 -178
- package/src/charts/legend.tsx +204 -0
- package/src/charts/marks.ts +201 -0
- package/src/charts/panel.tsx +227 -0
- package/src/charts/scales.ts +237 -0
- package/src/charts/sizing.ts +47 -0
- package/src/charts/state.tsx +80 -0
- package/src/charts/theme.ts +65 -0
- package/src/charts/types.ts +242 -0
- package/src/cli/audit-allows.ts +8 -30
- package/src/cli/config.ts +14 -43
- package/src/cli/doctor-upgrade.ts +392 -0
- package/src/cli/doctor.ts +120 -68
- package/src/cli/help.ts +2 -4
- package/src/cli/index.ts +161 -276
- package/src/cli/init.ts +60 -37
- package/src/cli/sync.ts +13 -45
- package/src/cli/tokens-css.ts +7 -28
- package/src/commands/ShortcutsHelp.tsx +8 -9
- package/src/commands/define-commands.ts +16 -43
- package/src/commands/define-overlays.ts +12 -36
- package/src/commands/index.ts +11 -94
- package/src/{notifications → commands/notifications}/bell.tsx +2 -2
- package/src/{notifications → commands/notifications}/center.tsx +7 -5
- package/src/{notifications → commands/notifications}/define-notifications.ts +3 -3
- package/src/commands/notifications/notify.tsx +158 -0
- package/src/{notifications → commands/notifications}/store.ts +10 -2
- package/src/commands/projectors.ts +5 -225
- package/src/commands/shell-bridge.ts +5 -5
- package/src/commands/shortcut-format.ts +5 -4
- package/src/common/errors.ts +3 -3
- package/src/common/use-measured-widths.ts +6 -3
- package/src/common/validate.ts +2 -2
- package/src/content/article-layout.tsx +37 -38
- package/src/content/article-model.ts +1 -84
- package/src/content/copy-action.tsx +1 -1
- package/src/content/glyphs.tsx +1 -1
- package/src/content/index.ts +12 -83
- package/src/content/markdown.tsx +4 -3
- package/src/content/mermaid.tsx +9 -7
- package/src/content/slug.ts +1 -16
- package/src/content/url-hardening.ts +8 -2
- package/src/controls/actions.tsx +24 -1
- package/src/controls/enum-filter.tsx +1 -1
- package/src/controls/filter-context.tsx +3 -1
- package/src/controls/filter-set.tsx +5 -7
- package/src/controls/form-layout.tsx +2 -2
- package/src/controls/glyphs.tsx +0 -10
- package/src/controls/index.ts +13 -64
- package/src/controls/multi-select-filter.tsx +5 -4
- package/src/controls/number-filter.tsx +8 -4
- package/src/controls/range-filter.tsx +5 -4
- package/src/controls/search-filter.tsx +25 -19
- package/src/controls/select-filter.tsx +5 -4
- package/src/controls/sync-button.tsx +18 -20
- package/src/controls/toggle-filter.tsx +5 -4
- package/src/controls/uncontrolled.ts +12 -0
- package/src/controls/view-tabs.tsx +3 -3
- package/src/controls-dates/date-range-picker.tsx +1 -1
- package/src/controls-dates/index.ts +0 -1
- package/src/dashboard/index.ts +7 -48
- package/src/dashboard/section.tsx +11 -40
- package/src/dashboard/stat-card.tsx +14 -16
- package/src/dashboard/widget-header.module.css +9 -7
- package/src/dev-dock.tsx +14 -27
- package/src/format/date.ts +2 -92
- package/src/format/index.ts +7 -53
- package/src/format/number.ts +3 -26
- package/src/format/shared.ts +2 -3
- package/src/forms/index.ts +0 -23
- package/src/guard/index.ts +37 -192
- package/src/guard/types.ts +6 -19
- package/src/index.ts +30 -282
- package/src/provider/create-router.tsx +138 -0
- package/src/provider/hotkeys.ts +70 -0
- package/src/provider/index.tsx +260 -230
- package/src/provider/lab-theme.ts +3 -2
- package/src/provider/route-transition.ts +199 -0
- package/src/provider/spotlight-actions.tsx +69 -0
- package/src/provider/test-router.tsx +25 -0
- package/src/query-client.ts +4 -22
- package/src/router-tanstack/index.ts +18 -112
- package/src/router-tanstack/nav.ts +28 -2
- package/src/router-tanstack/search-store.ts +9 -2
- package/src/router-tanstack/use-nav.ts +47 -13
- package/src/shell/app-mobile-nav.tsx +3 -2
- package/src/shell/app-sidebar.tsx +1 -1
- package/src/shell/aside-docking.ts +1 -1
- package/src/shell/index.tsx +61 -20
- package/src/shell/page-aside.tsx +47 -23
- package/src/shell/page-bar.tsx +3 -3
- package/src/shell/page-title.tsx +1 -1
- package/src/state.ts +11 -37
- package/src/styles.css +107 -7
- package/src/surfaces.ts +56 -151
- package/src/{data → table}/data-table-state.ts +5 -4
- package/src/{data → table}/data-table.tsx +170 -161
- package/src/table/index.ts +18 -0
- package/src/table/table-features.ts +102 -0
- package/src/telemetry.ts +110 -0
- package/src/theme/index.ts +22 -47
- package/src/theme/theme-toggle.module.css +30 -0
- package/src/theme/theme-toggle.module.css.d.ts +10 -0
- package/src/theme/theme-toggle.tsx +15 -43
- package/src/theme-lab/derive-controls.tsx +5 -3
- package/src/theme-lab/derive-state.ts +2 -1
- package/src/tokens/core.ts +1163 -0
- package/src/tokens/derive.ts +1 -1
- package/src/tokens/index.ts +14 -1181
- package/src/tokens/palette.ts +3 -7
- package/src/vite.ts +3 -3
- package/agent/templates/CLAUDE-block.md.tpl +0 -40
- package/agent/templates/query-client.ts.tpl +0 -26
- package/dist/agent/adapter.d.ts +0 -150
- package/dist/agent/adapter.d.ts.map +0 -1
- package/dist/agent/adapter.js +0 -482
- package/dist/agent/use-agent-stream.d.ts +0 -35
- package/dist/agent/use-agent-stream.d.ts.map +0 -1
- package/dist/agent/use-agent-stream.js +0 -59
- package/dist/agent-chat/composer.d.ts.map +0 -1
- package/dist/agent-chat/index.d.ts +0 -31
- package/dist/agent-chat/index.d.ts.map +0 -1
- package/dist/agent-chat/index.js +0 -19
- package/dist/agent-chat/message-affordances.d.ts.map +0 -1
- package/dist/agent-chat/relative-time.d.ts.map +0 -1
- package/dist/agent-chat/thread-detail-panel.d.ts +0 -28
- package/dist/agent-chat/thread-detail-panel.d.ts.map +0 -1
- package/dist/agent-chat/thread-detail-panel.js +0 -115
- package/dist/agent-chat/thread-feed-row.d.ts.map +0 -1
- package/dist/agent-chat/thread-feed.d.ts.map +0 -1
- package/dist/agent-chat/thread-message.d.ts.map +0 -1
- package/dist/agent-chat/thread-outcome-card.d.ts.map +0 -1
- package/dist/agent-chat/thread-workspace.d.ts +0 -44
- package/dist/agent-chat/thread-workspace.d.ts.map +0 -1
- package/dist/agent-chat/thread-workspace.js +0 -153
- package/dist/agent-chat/tool-chip.d.ts.map +0 -1
- package/dist/agent-chat/use-keyboard-inset.d.ts.map +0 -1
- package/dist/agent-chat/virtualize.d.ts.map +0 -1
- package/dist/charts/cursor/resolve.d.ts +0 -75
- package/dist/charts/cursor/resolve.d.ts.map +0 -1
- package/dist/charts/cursor/resolve.js +0 -91
- package/dist/charts/cursor/scope.d.ts +0 -20
- package/dist/charts/cursor/scope.d.ts.map +0 -1
- package/dist/charts/cursor/scope.js +0 -20
- package/dist/charts/cursor/store.d.ts +0 -35
- package/dist/charts/cursor/store.d.ts.map +0 -1
- package/dist/charts/cursor/store.js +0 -25
- package/dist/charts/cursor/touch-pin.d.ts +0 -45
- package/dist/charts/cursor/touch-pin.d.ts.map +0 -1
- package/dist/charts/cursor/touch-pin.js +0 -79
- package/dist/charts/hooks/use-discrete-cursor.d.ts +0 -66
- package/dist/charts/hooks/use-discrete-cursor.d.ts.map +0 -1
- package/dist/charts/hooks/use-discrete-cursor.js +0 -198
- package/dist/charts/hooks/use-hysteresis.d.ts +0 -18
- package/dist/charts/hooks/use-hysteresis.d.ts.map +0 -1
- package/dist/charts/hooks/use-hysteresis.js +0 -12
- package/dist/charts/hooks/useBandPlot.d.ts +0 -145
- package/dist/charts/hooks/useBandPlot.d.ts.map +0 -1
- package/dist/charts/hooks/useBandPlot.js +0 -178
- package/dist/charts/hooks/useChartCursor.d.ts +0 -75
- package/dist/charts/hooks/useChartCursor.d.ts.map +0 -1
- package/dist/charts/hooks/useChartCursor.js +0 -200
- package/dist/charts/hooks/useChartSize.d.ts +0 -29
- package/dist/charts/hooks/useChartSize.d.ts.map +0 -1
- package/dist/charts/hooks/useChartSize.js +0 -8
- package/dist/charts/kinds/BandStrip.d.ts +0 -132
- package/dist/charts/kinds/BandStrip.d.ts.map +0 -1
- package/dist/charts/kinds/BandStrip.js +0 -241
- package/dist/charts/kinds/Bars.d.ts +0 -141
- package/dist/charts/kinds/Bars.d.ts.map +0 -1
- package/dist/charts/kinds/Bars.js +0 -343
- package/dist/charts/kinds/Donut.d.ts +0 -77
- package/dist/charts/kinds/Donut.d.ts.map +0 -1
- package/dist/charts/kinds/Donut.js +0 -245
- package/dist/charts/kinds/DualPanel.d.ts +0 -111
- package/dist/charts/kinds/DualPanel.d.ts.map +0 -1
- package/dist/charts/kinds/DualPanel.js +0 -471
- package/dist/charts/kinds/Heatmap.d.ts +0 -85
- package/dist/charts/kinds/Heatmap.d.ts.map +0 -1
- package/dist/charts/kinds/Heatmap.js +0 -243
- package/dist/charts/kinds/MirroredBars.d.ts +0 -98
- package/dist/charts/kinds/MirroredBars.d.ts.map +0 -1
- package/dist/charts/kinds/MirroredBars.js +0 -292
- package/dist/charts/kinds/MultiLine.d.ts +0 -87
- package/dist/charts/kinds/MultiLine.d.ts.map +0 -1
- package/dist/charts/kinds/MultiLine.js +0 -176
- package/dist/charts/kinds/StackedArea.d.ts +0 -73
- package/dist/charts/kinds/StackedArea.d.ts.map +0 -1
- package/dist/charts/kinds/StackedArea.js +0 -135
- package/dist/charts/kinds/ZonedLine.d.ts +0 -97
- package/dist/charts/kinds/ZonedLine.d.ts.map +0 -1
- package/dist/charts/kinds/ZonedLine.js +0 -155
- package/dist/charts/kinds/band-chrome.d.ts +0 -31
- package/dist/charts/kinds/band-chrome.d.ts.map +0 -1
- package/dist/charts/kinds/band-chrome.js +0 -71
- package/dist/charts/kinds/donut-layout.d.ts +0 -47
- package/dist/charts/kinds/donut-layout.d.ts.map +0 -1
- package/dist/charts/kinds/donut-layout.js +0 -52
- package/dist/charts/layout/auto-margin.d.ts +0 -122
- package/dist/charts/layout/auto-margin.d.ts.map +0 -1
- package/dist/charts/layout/auto-margin.js +0 -81
- package/dist/charts/layout/end-labels.d.ts +0 -64
- package/dist/charts/layout/end-labels.d.ts.map +0 -1
- package/dist/charts/layout/end-labels.js +0 -76
- package/dist/charts/layout/log-ticks.d.ts +0 -27
- package/dist/charts/layout/log-ticks.d.ts.map +0 -1
- package/dist/charts/layout/log-ticks.js +0 -42
- package/dist/charts/primitives/AreaGradient.d.ts +0 -18
- package/dist/charts/primitives/AreaGradient.d.ts.map +0 -1
- package/dist/charts/primitives/AreaGradient.js +0 -13
- package/dist/charts/primitives/Axes.d.ts +0 -83
- package/dist/charts/primitives/Axes.d.ts.map +0 -1
- package/dist/charts/primitives/Axes.js +0 -168
- package/dist/charts/primitives/CartesianChart.d.ts +0 -257
- package/dist/charts/primitives/CartesianChart.d.ts.map +0 -1
- package/dist/charts/primitives/CartesianChart.js +0 -549
- package/dist/charts/primitives/ChartCard.d.ts +0 -66
- package/dist/charts/primitives/ChartCard.d.ts.map +0 -1
- package/dist/charts/primitives/ChartCard.js +0 -143
- package/dist/charts/primitives/ChartFrame.d.ts +0 -156
- package/dist/charts/primitives/ChartFrame.d.ts.map +0 -1
- package/dist/charts/primitives/ChartFrame.js +0 -241
- package/dist/charts/primitives/ChartLegend.d.ts +0 -67
- package/dist/charts/primitives/ChartLegend.d.ts.map +0 -1
- package/dist/charts/primitives/ChartLegend.js +0 -292
- package/dist/charts/primitives/ChartPending.d.ts +0 -113
- package/dist/charts/primitives/ChartPending.d.ts.map +0 -1
- package/dist/charts/primitives/ChartPending.js +0 -117
- package/dist/charts/primitives/ChartTooltip.d.ts +0 -59
- package/dist/charts/primitives/ChartTooltip.d.ts.map +0 -1
- package/dist/charts/primitives/ChartTooltip.js +0 -197
- package/dist/charts/primitives/Crosshair.d.ts +0 -26
- package/dist/charts/primitives/Crosshair.d.ts.map +0 -1
- package/dist/charts/primitives/Crosshair.js +0 -23
- package/dist/charts/primitives/HatchPattern.d.ts +0 -31
- package/dist/charts/primitives/HatchPattern.d.ts.map +0 -1
- package/dist/charts/primitives/HatchPattern.js +0 -31
- package/dist/charts/primitives/HoverOverlay.d.ts +0 -45
- package/dist/charts/primitives/HoverOverlay.d.ts.map +0 -1
- package/dist/charts/primitives/HoverOverlay.js +0 -72
- package/dist/charts/primitives/HoverOverlay.module.css +0 -13
- package/dist/charts/primitives/HoverOverlay.module.css.d.ts +0 -8
- package/dist/charts/primitives/XZoneRects.d.ts +0 -54
- package/dist/charts/primitives/XZoneRects.d.ts.map +0 -1
- package/dist/charts/primitives/XZoneRects.js +0 -65
- package/dist/charts/primitives/ZoneRects.d.ts +0 -38
- package/dist/charts/primitives/ZoneRects.d.ts.map +0 -1
- package/dist/charts/primitives/ZoneRects.js +0 -30
- package/dist/charts/primitives/chart-card-context.d.ts +0 -33
- package/dist/charts/primitives/chart-card-context.d.ts.map +0 -1
- package/dist/charts/primitives/chart-card-context.js +0 -18
- package/dist/charts/primitives/chart-frame-layout.d.ts +0 -185
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +0 -1
- package/dist/charts/primitives/chart-frame-layout.js +0 -175
- package/dist/charts/primitives/chart-layout.d.ts +0 -112
- package/dist/charts/primitives/chart-layout.d.ts.map +0 -1
- package/dist/charts/primitives/chart-layout.js +0 -175
- package/dist/charts/primitives/chart-tier.d.ts +0 -16
- package/dist/charts/primitives/chart-tier.d.ts.map +0 -1
- package/dist/charts/primitives/chart-tier.js +0 -44
- package/dist/charts/primitives/legend-disclosure.d.ts +0 -27
- package/dist/charts/primitives/legend-disclosure.d.ts.map +0 -1
- package/dist/charts/primitives/legend-disclosure.js +0 -153
- package/dist/charts/series.d.ts +0 -189
- package/dist/charts/series.d.ts.map +0 -1
- package/dist/charts/series.js +0 -84
- package/dist/charts/sparklines/BarSparkline.d.ts +0 -37
- package/dist/charts/sparklines/BarSparkline.d.ts.map +0 -1
- package/dist/charts/sparklines/BarSparkline.js +0 -53
- package/dist/charts/sparklines/LineSparkline.d.ts +0 -15
- package/dist/charts/sparklines/LineSparkline.d.ts.map +0 -1
- package/dist/charts/sparklines/LineSparkline.js +0 -51
- package/dist/charts/sparklines/index.d.ts +0 -3
- package/dist/charts/sparklines/index.d.ts.map +0 -1
- package/dist/charts/sparklines/index.js +0 -6
- package/dist/charts/utils/domain.d.ts +0 -35
- package/dist/charts/utils/domain.d.ts.map +0 -1
- package/dist/charts/utils/domain.js +0 -12
- package/dist/charts/utils/format.d.ts +0 -8
- package/dist/charts/utils/format.d.ts.map +0 -1
- package/dist/charts/utils/format.js +0 -20
- package/dist/charts/utils/measure-text.d.ts +0 -25
- package/dist/charts/utils/measure-text.d.ts.map +0 -1
- package/dist/charts/utils/measure-text.js +0 -64
- package/dist/charts/utils/ticks.d.ts +0 -103
- package/dist/charts/utils/ticks.d.ts.map +0 -1
- package/dist/charts/utils/ticks.js +0 -92
- package/dist/commands/overlays-mount.d.ts +0 -122
- package/dist/commands/overlays-mount.d.ts.map +0 -1
- package/dist/commands/overlays-mount.js +0 -125
- package/dist/commands/useCommandHotkeys.d.ts +0 -14
- package/dist/commands/useCommandHotkeys.d.ts.map +0 -1
- package/dist/commands/useCommandHotkeys.js +0 -45
- package/dist/content/article-actions.d.ts +0 -39
- package/dist/content/article-actions.d.ts.map +0 -1
- package/dist/content/article-actions.js +0 -28
- package/dist/content/article-card.d.ts +0 -58
- package/dist/content/article-card.d.ts.map +0 -1
- package/dist/content/article-card.js +0 -79
- package/dist/content/article-card.module.css +0 -111
- package/dist/content/mdx.d.ts +0 -20
- package/dist/content/mdx.d.ts.map +0 -1
- package/dist/content/mdx.js +0 -64
- package/dist/controls/compare-filter.d.ts +0 -42
- package/dist/controls/compare-filter.d.ts.map +0 -1
- package/dist/controls/compare-filter.js +0 -38
- package/dist/controls/slider-control.d.ts +0 -36
- package/dist/controls/slider-control.d.ts.map +0 -1
- package/dist/controls/slider-control.js +0 -64
- package/dist/dashboard/widget-grid.d.ts +0 -27
- package/dist/dashboard/widget-grid.d.ts.map +0 -1
- package/dist/dashboard/widget-grid.js +0 -40
- package/dist/dashboard/widget-grid.module.css +0 -69
- package/dist/data/data-table-state.d.ts.map +0 -1
- package/dist/data/data-table.d.ts.map +0 -1
- package/dist/data/query-branch.d.ts.map +0 -1
- package/dist/data/table.d.ts +0 -24
- package/dist/data/table.d.ts.map +0 -1
- package/dist/data/virtual-list.d.ts +0 -133
- package/dist/data/virtual-list.d.ts.map +0 -1
- package/dist/data/virtual-list.js +0 -157
- package/dist/data/virtual.d.ts +0 -21
- package/dist/data/virtual.d.ts.map +0 -1
- package/dist/data/virtual.js +0 -4
- package/dist/forms/FormErrorSummary.d.ts +0 -30
- package/dist/forms/FormErrorSummary.d.ts.map +0 -1
- package/dist/forms/FormErrorSummary.js +0 -30
- package/dist/forms/use-field-array.d.ts +0 -60
- package/dist/forms/use-field-array.d.ts.map +0 -1
- package/dist/forms/use-field-array.js +0 -20
- package/dist/forms/use-form-draft.d.ts +0 -76
- package/dist/forms/use-form-draft.d.ts.map +0 -1
- package/dist/forms/use-form-draft.js +0 -92
- package/dist/forms/use-form-submit.d.ts +0 -88
- package/dist/forms/use-form-submit.d.ts.map +0 -1
- package/dist/forms/use-form-submit.js +0 -63
- package/dist/notifications/bell.d.ts.map +0 -1
- package/dist/notifications/center.d.ts.map +0 -1
- package/dist/notifications/define-notifications.d.ts.map +0 -1
- package/dist/notifications/index.d.ts +0 -40
- package/dist/notifications/index.d.ts.map +0 -1
- package/dist/notifications/index.js +0 -54
- package/dist/notifications/mount-guard.d.ts +0 -8
- package/dist/notifications/mount-guard.d.ts.map +0 -1
- package/dist/notifications/mount-guard.js +0 -18
- package/dist/notifications/notify.d.ts +0 -149
- package/dist/notifications/notify.d.ts.map +0 -1
- package/dist/notifications/notify.js +0 -171
- package/dist/notifications/store.d.ts.map +0 -1
- package/dist/provider/connectivity-indicator.d.ts +0 -13
- package/dist/provider/connectivity-indicator.d.ts.map +0 -1
- package/dist/provider/connectivity-indicator.js +0 -205
- package/dist/provider/connectivity-provider.d.ts +0 -7
- package/dist/provider/connectivity-provider.d.ts.map +0 -1
- package/dist/provider/connectivity-provider.js +0 -128
- package/dist/provider/connectivity-types.d.ts +0 -36
- package/dist/provider/connectivity-types.d.ts.map +0 -1
- package/dist/provider/use-connectivity.d.ts +0 -3
- package/dist/provider/use-connectivity.d.ts.map +0 -1
- package/dist/provider/use-connectivity.js +0 -14
- package/dist/query-devtools.d.ts +0 -21
- package/dist/query-devtools.d.ts.map +0 -1
- package/dist/query-devtools.js +0 -13
- package/src/agent/adapter.ts +0 -945
- package/src/agent/use-agent-stream.ts +0 -154
- package/src/agent-chat/index.ts +0 -54
- package/src/agent-chat/thread-detail-panel.tsx +0 -201
- package/src/agent-chat/thread-workspace.tsx +0 -279
- package/src/charts/cursor/resolve.ts +0 -198
- package/src/charts/cursor/scope.tsx +0 -35
- package/src/charts/cursor/store.ts +0 -58
- package/src/charts/cursor/touch-pin.ts +0 -138
- package/src/charts/hooks/use-discrete-cursor.ts +0 -335
- package/src/charts/hooks/use-hysteresis.ts +0 -26
- package/src/charts/hooks/useBandPlot.ts +0 -383
- package/src/charts/hooks/useChartCursor.ts +0 -367
- package/src/charts/hooks/useChartSize.ts +0 -35
- package/src/charts/kinds/BandStrip.tsx +0 -449
- package/src/charts/kinds/Bars.tsx +0 -589
- package/src/charts/kinds/Donut.tsx +0 -368
- package/src/charts/kinds/DualPanel.tsx +0 -712
- package/src/charts/kinds/Heatmap.tsx +0 -396
- package/src/charts/kinds/MirroredBars.tsx +0 -469
- package/src/charts/kinds/MultiLine.tsx +0 -322
- package/src/charts/kinds/StackedArea.tsx +0 -267
- package/src/charts/kinds/ZonedLine.tsx +0 -301
- package/src/charts/kinds/band-chrome.tsx +0 -91
- package/src/charts/kinds/donut-layout.ts +0 -82
- package/src/charts/layout/auto-margin.ts +0 -242
- package/src/charts/layout/end-labels.ts +0 -135
- package/src/charts/layout/log-ticks.ts +0 -90
- package/src/charts/primitives/AreaGradient.tsx +0 -26
- package/src/charts/primitives/Axes.tsx +0 -271
- package/src/charts/primitives/CartesianChart.tsx +0 -1002
- package/src/charts/primitives/ChartCard.tsx +0 -280
- package/src/charts/primitives/ChartFrame.tsx +0 -493
- package/src/charts/primitives/ChartLegend.tsx +0 -422
- package/src/charts/primitives/ChartPending.tsx +0 -243
- package/src/charts/primitives/ChartTooltip.tsx +0 -265
- package/src/charts/primitives/Crosshair.tsx +0 -43
- package/src/charts/primitives/HatchPattern.tsx +0 -55
- package/src/charts/primitives/HoverOverlay.module.css +0 -13
- package/src/charts/primitives/HoverOverlay.module.css.d.ts +0 -8
- package/src/charts/primitives/HoverOverlay.tsx +0 -109
- package/src/charts/primitives/XZoneRects.tsx +0 -132
- package/src/charts/primitives/ZoneRects.tsx +0 -67
- package/src/charts/primitives/chart-card-context.ts +0 -46
- package/src/charts/primitives/chart-frame-layout.ts +0 -430
- package/src/charts/primitives/chart-layout.ts +0 -318
- package/src/charts/primitives/chart-tier.tsx +0 -76
- package/src/charts/primitives/legend-disclosure.tsx +0 -178
- package/src/charts/series.ts +0 -276
- package/src/charts/sparklines/BarSparkline.tsx +0 -94
- package/src/charts/sparklines/LineSparkline.tsx +0 -71
- package/src/charts/sparklines/index.ts +0 -2
- package/src/charts/theme.tsx +0 -53
- package/src/charts/utils/domain.ts +0 -41
- package/src/charts/utils/format.ts +0 -19
- package/src/charts/utils/measure-text.ts +0 -109
- package/src/charts/utils/ticks.ts +0 -239
- package/src/commands/overlays-mount.tsx +0 -333
- package/src/commands/useCommandHotkeys.ts +0 -114
- package/src/content/article-actions.ts +0 -72
- package/src/content/article-card.module.css +0 -111
- package/src/content/article-card.tsx +0 -174
- package/src/content/mdx.tsx +0 -127
- package/src/controls/compare-filter.tsx +0 -81
- package/src/controls/slider-control.tsx +0 -159
- package/src/dashboard/widget-grid.module.css +0 -69
- package/src/dashboard/widget-grid.tsx +0 -146
- package/src/data/table.ts +0 -39
- package/src/data/virtual-list.tsx +0 -356
- package/src/data/virtual.ts +0 -26
- package/src/forms/FormErrorSummary.tsx +0 -71
- package/src/forms/use-field-array.ts +0 -94
- package/src/forms/use-form-draft.ts +0 -262
- package/src/forms/use-form-submit.ts +0 -199
- package/src/notifications/index.ts +0 -88
- package/src/notifications/mount-guard.ts +0 -38
- package/src/notifications/notify.tsx +0 -379
- package/src/provider/connectivity-indicator.tsx +0 -267
- package/src/provider/connectivity-provider.tsx +0 -189
- package/src/provider/connectivity-types.ts +0 -42
- package/src/provider/use-connectivity.ts +0 -26
- package/src/query-devtools.tsx +0 -40
- /package/dist/{agent-chat → agent/chat}/message-affordances.js +0 -0
- /package/dist/{agent-chat → agent/chat}/relative-time.d.ts +0 -0
- /package/dist/{agent-chat → agent/chat}/relative-time.js +0 -0
- /package/dist/{agent-chat → agent/chat}/use-keyboard-inset.d.ts +0 -0
- /package/dist/{agent-chat → agent/chat}/virtualize.js +0 -0
- /package/dist/{notifications → commands/notifications}/bell.js +0 -0
- /package/dist/{notifications → commands/notifications}/define-notifications.js +0 -0
- /package/dist/{provider/connectivity-types.js → controls/uncontrolled.js} +0 -0
- /package/dist/{data → table}/data-table.module.css +0 -0
- /package/dist/{data → table}/data-table.module.css.d.ts +0 -0
- /package/dist/{data → table}/query-branch.d.ts +0 -0
- /package/dist/{data → table}/query-branch.js +0 -0
- /package/src/{agent-chat → agent/chat}/relative-time.ts +0 -0
- /package/src/{data → table}/data-table.module.css +0 -0
- /package/src/{data → table}/data-table.module.css.d.ts +0 -0
- /package/src/{data → table}/query-branch.ts +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useAgentThreadRuns — concurrent multi-thread stream run manager.
|
|
3
3
|
*
|
|
4
|
-
* Unlike useAgentStream (ONE in-flight turn — a new send() aborts the previous one),
|
|
5
4
|
* useAgentThreadRuns runs N CONCURRENT streams, one per thread id, so a user can fire many
|
|
6
5
|
* short chats that each stream and resolve independently in the background. Each thread's
|
|
7
6
|
* turn is appended to an injected ThreadsStore and distilled into an AgentOutcome via an
|
|
@@ -14,61 +13,94 @@
|
|
|
14
13
|
* per thread id and cached: `transport: (threadId) => aiSdkTransport(opts).forThread(threadId)`.
|
|
15
14
|
*
|
|
16
15
|
* @example
|
|
17
|
-
* import { useAgentThreadRuns,
|
|
16
|
+
* import { useAgentThreadRuns, aiSdkTransport } from 'basalt-ui/agent'
|
|
18
17
|
*
|
|
19
|
-
* const
|
|
20
|
-
* const transport = edenTransport((input, signal) =>
|
|
21
|
-
* api.chat.post({ body: { message: input }, fetch: { signal } }),
|
|
22
|
-
* )
|
|
18
|
+
* const transport = aiSdkTransport({ api: '/api/chat' })
|
|
23
19
|
*
|
|
24
20
|
* function ThreadFeed() {
|
|
25
|
-
* const store =
|
|
21
|
+
* const store = useMyThreadsStore() // consumer-owned ThreadsStore
|
|
26
22
|
* const { runs, start, stop } = useAgentThreadRuns({
|
|
27
23
|
* transport,
|
|
28
24
|
* store,
|
|
29
|
-
* resolveOutcome
|
|
25
|
+
* resolveOutcome, // your OutcomeResolver
|
|
30
26
|
* })
|
|
31
27
|
* const id = store.create()
|
|
28
|
+
* const status = runs.get(id)?.status // 'submitted' | 'streaming' | 'error' | undefined
|
|
29
|
+
* const isLive = status === 'submitted' || status === 'streaming'
|
|
32
30
|
* return (
|
|
33
31
|
* <div>
|
|
34
32
|
* <button onClick={() => start(id, 'Hello')}>Send</button>
|
|
35
|
-
* {
|
|
33
|
+
* {isLive && <button onClick={() => stop(id)}>Stop</button>}
|
|
36
34
|
* </div>
|
|
37
35
|
* )
|
|
38
36
|
* }
|
|
39
37
|
*/
|
|
40
|
-
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
38
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
|
41
39
|
import type { Dispatch, MutableRefObject, SetStateAction } from 'react'
|
|
42
40
|
import type { ChatMessage } from './history'
|
|
41
|
+
import { toAgentError } from './error'
|
|
42
|
+
import type { AgentError } from './error'
|
|
43
43
|
import { mintMessageId, mintThreadId, withPartIds } from './id'
|
|
44
44
|
import { mergePart } from './merge'
|
|
45
45
|
import type { PartLike } from './merge'
|
|
46
46
|
import type { AgentOutcome, OutcomeResolver } from './outcome'
|
|
47
47
|
import { isStartPart } from './parts'
|
|
48
48
|
import type { AgentPart } from './parts'
|
|
49
|
-
import
|
|
49
|
+
import { createRunCommitter, isContentPart } from './run-committer'
|
|
50
|
+
import {
|
|
51
|
+
beginRun,
|
|
52
|
+
endRun,
|
|
53
|
+
failedThreadIds,
|
|
54
|
+
dismissRun,
|
|
55
|
+
hasLiveRun,
|
|
56
|
+
isLatestRun,
|
|
57
|
+
isLive,
|
|
58
|
+
planStopWithTombstone,
|
|
59
|
+
sweepFlushed,
|
|
60
|
+
} from './run-records'
|
|
61
|
+
import type { RunRecord, RunRecords } from './run-records'
|
|
62
|
+
import type { AgentThread, LiveRunStatus, ThreadStatus, ThreadsStore } from './thread'
|
|
50
63
|
import { isResumable } from './transport'
|
|
51
64
|
import type { AgentTransport } from './transport'
|
|
65
|
+
import { TelemetryContext, createEmitter, errorName, now } from '../telemetry'
|
|
66
|
+
import type { BasaltEvent, Telemetry } from '../telemetry'
|
|
52
67
|
|
|
53
68
|
// ── ThreadRunState ────────────────────────────────────────────────────────────
|
|
54
69
|
|
|
55
70
|
/**
|
|
56
|
-
* The
|
|
71
|
+
* The stream state of a single thread's most recent turn, while it is in flight or has failed.
|
|
57
72
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* `
|
|
73
|
+
* `status` mirrors AI SDK's `ChatStatus`: `'submitted'` from send until the first content part
|
|
74
|
+
* (text, reasoning or tool) arrives, then `'streaming'`, or `'error'` once the turn failed. A turn
|
|
75
|
+
* that settles successfully (or is stopped) has its entry deleted from the `runs` map — the
|
|
76
|
+
* outcome lives on the persisted `AgentThread` instead.
|
|
77
|
+
*
|
|
78
|
+
* A turn that FAILS keeps its entry — `status: 'error'`, `error` set, the parts received so far —
|
|
79
|
+
* until the next `start()`/`retry()` on that thread replaces it or `stop()` dismisses it. So
|
|
80
|
+
* `runs.has(id)` means "in flight or failed"; `run.status` alone says which, and
|
|
81
|
+
* `liveStatus={runs.get(id)?.status}` is correct for the chat rows (`'error'` is not live).
|
|
62
82
|
*
|
|
63
83
|
* @example
|
|
64
84
|
* const state: ThreadRunState = { status: 'streaming', parts: [{ type: 'text', text: 'Hi' }] }
|
|
65
85
|
*/
|
|
66
|
-
export type ThreadRunState<TPart = AgentPart> =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
export type ThreadRunState<TPart = AgentPart> =
|
|
87
|
+
| {
|
|
88
|
+
/** `'submitted'` until the first content part arrives, then `'streaming'`. */
|
|
89
|
+
readonly status: LiveRunStatus
|
|
90
|
+
/** Accumulated parts from the current stream for this thread. */
|
|
91
|
+
readonly parts: TPart[]
|
|
92
|
+
readonly error?: undefined
|
|
93
|
+
}
|
|
94
|
+
| {
|
|
95
|
+
readonly status: 'error'
|
|
96
|
+
/**
|
|
97
|
+
* The parts received before the failure. Empty when the turn's final message already landed
|
|
98
|
+
* in the thread (a failure after `appendMessage`, e.g. in `resolveOutcome`), so the feed
|
|
99
|
+
* never renders the same content twice.
|
|
100
|
+
*/
|
|
101
|
+
readonly parts: TPart[]
|
|
102
|
+
readonly error: AgentError
|
|
103
|
+
}
|
|
72
104
|
|
|
73
105
|
// ── useAgentThreadRuns args ───────────────────────────────────────────────────
|
|
74
106
|
|
|
@@ -95,15 +127,33 @@ export type UseAgentThreadRunsArgs<TPart = AgentPart> = {
|
|
|
95
127
|
* Converts raw user input into the thread's part shape. Defaults to a single text part.
|
|
96
128
|
* Required when TPart is not the default AgentPart union.
|
|
97
129
|
*/
|
|
98
|
-
readonly toUserParts?: (input: string) => TPart[]
|
|
130
|
+
readonly toUserParts?: ((input: string) => TPart[]) | undefined
|
|
131
|
+
/**
|
|
132
|
+
* Called with the RAW thrown value when a turn's stream throws — never for the aborts this hook
|
|
133
|
+
* issues itself (`stop()`, supersede, unmount). An abort that comes from elsewhere (the
|
|
134
|
+
* transport, the network layer) is a failure and fires, as `kind: 'aborted'` on the run. Fires
|
|
135
|
+
* from the SAME catch branch that sets `onFailureStatus` on the thread, so a caller wanting a
|
|
136
|
+
* toast/notification for a genuine transport error (a 409/503/401, argo's hermes-chat S19) no
|
|
137
|
+
* longer has to wrap every transport call in its own generator to intercept the throw — see
|
|
138
|
+
* `consumeAndFinalize`'s catch block.
|
|
139
|
+
*/
|
|
140
|
+
readonly onError?:
|
|
141
|
+
| ((info: { readonly threadId: string; readonly error: unknown }) => void)
|
|
142
|
+
| undefined
|
|
143
|
+
/**
|
|
144
|
+
* Minimum ms between `runs` commits while a part stream is flowing — `useChat`'s stable
|
|
145
|
+
* `throttle` semantics: the first part commits immediately, later parts within the window are
|
|
146
|
+
* batched into one trailing commit. The `submitted → streaming` edge, a failure, the end of the
|
|
147
|
+
* stream and `stop()` never wait on it, so no final part is lost. Default `100`; `0` commits
|
|
148
|
+
* every part.
|
|
149
|
+
*/
|
|
150
|
+
readonly throttle?: number | undefined
|
|
99
151
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* transport error (a 409/503/401, argo's hermes-chat S19) no longer has to wrap every transport
|
|
104
|
-
* call in its own generator to intercept the throw — see `consumeAndFinalize`'s catch block.
|
|
152
|
+
* Receives one `chat.turn` event when a turn completes or fails (V2-SPEC § 7); a `stop()`ped
|
|
153
|
+
* turn emits nothing. Omitted, it defaults to the enclosing `BasaltProvider`'s `telemetry` sink,
|
|
154
|
+
* and outside a provider nothing is delivered. A throwing sink never breaks the turn.
|
|
105
155
|
*/
|
|
106
|
-
readonly
|
|
156
|
+
readonly telemetry?: Telemetry | undefined
|
|
107
157
|
}
|
|
108
158
|
|
|
109
159
|
// ── useAgentThreadRuns return type ────────────────────────────────────────────
|
|
@@ -132,21 +182,25 @@ export type UseAgentThreadRunsReturn<TPart = AgentPart> = {
|
|
|
132
182
|
*/
|
|
133
183
|
readonly retry: (threadId: string) => void
|
|
134
184
|
/**
|
|
135
|
-
* Abort the in-flight stream for `threadId
|
|
136
|
-
*
|
|
185
|
+
* Abort the in-flight stream for `threadId`. A no-op when the thread has no live run and no
|
|
186
|
+
* failed entry. Preserves whatever content
|
|
137
187
|
* had already arrived: if the run had accumulated any parts, they're persisted as an assistant
|
|
138
188
|
* ChatMessage with `finish: 'stopped'` (unless that message already landed — see finalizeStop),
|
|
139
189
|
* then distilled into an outcome and the thread is settled to 'done'.
|
|
140
190
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
191
|
+
* A FAILED turn's entry (`status: 'error'`) has no controller: `stop()` just dismisses it and
|
|
192
|
+
* leaves the thread's `'error'` status alone. A turn that has already settled is a no-op.
|
|
193
|
+
*
|
|
194
|
+
* Defense-in-depth: if `runs` reports `threadId` as live but no run record exists for it (a
|
|
195
|
+
* phantom entry — not reachable via any path in this hook today, since the unmount-cleanup
|
|
196
|
+
* effect and `finalizeStop` both tear down the records and `runs` together), `stop()` still
|
|
197
|
+
* settles it, once, instead of silently no-opping forever.
|
|
147
198
|
*/
|
|
148
199
|
readonly stop: (threadId: string) => void
|
|
149
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Abort every in-flight stream across all threads, settling each the same way stop() would, and
|
|
202
|
+
* dismiss every failed entry.
|
|
203
|
+
*/
|
|
150
204
|
readonly stopAll: () => void
|
|
151
205
|
}
|
|
152
206
|
|
|
@@ -199,56 +253,64 @@ function buildDoneSnapshot<TPart>(
|
|
|
199
253
|
* completes. Shared by both start() (a fresh turn) and the mount-time resume path (reconnecting
|
|
200
254
|
* an orphaned thread) so the ~40-line accumulate/finalize/error logic exists exactly once.
|
|
201
255
|
*
|
|
202
|
-
* `onFailureStatus` is the ThreadStatus to set when the generator throws (excluding
|
|
203
|
-
*
|
|
256
|
+
* `onFailureStatus` is the ThreadStatus to set when the generator throws (excluding the aborts
|
|
257
|
+
* this hook issues itself — stop(), supersede, unmount — which are always a no-op; an abort from
|
|
258
|
+
* elsewhere is a failure): start() passes 'error' (today's behavior for a fresh turn that fails);
|
|
204
259
|
* the resume path passes 'interrupted' (a failed resume falls back to the same terminal state a
|
|
205
260
|
* non-resumable orphaned thread already lands in).
|
|
206
261
|
*/
|
|
207
262
|
async function consumeAndFinalize<TPart extends PartLike>(args: {
|
|
208
263
|
threadId: string
|
|
209
|
-
|
|
264
|
+
record: RunRecord<TPart>
|
|
210
265
|
generator: AsyncGenerator<TPart>
|
|
211
266
|
userMessage: ChatMessage<TPart>
|
|
212
|
-
|
|
213
|
-
appendedRef: MutableRefObject<Map<string, AbortController>>
|
|
267
|
+
recordsRef: MutableRefObject<RunRecords<TPart>>
|
|
214
268
|
storeRef: MutableRefObject<ThreadsStore<TPart>>
|
|
215
269
|
resolveOutcome: OutcomeResolver<TPart>
|
|
216
270
|
setRuns: Dispatch<SetStateAction<Map<string, ThreadRunState<TPart>>>>
|
|
217
271
|
onFailureStatus: ThreadStatus
|
|
272
|
+
throttle: number
|
|
273
|
+
emit: Telemetry
|
|
218
274
|
onError?: (info: { readonly threadId: string; readonly error: unknown }) => void
|
|
219
275
|
}): Promise<void> {
|
|
220
276
|
const {
|
|
221
277
|
threadId,
|
|
222
|
-
|
|
278
|
+
record,
|
|
223
279
|
generator,
|
|
224
280
|
userMessage,
|
|
225
|
-
|
|
226
|
-
appendedRef,
|
|
281
|
+
recordsRef,
|
|
227
282
|
storeRef,
|
|
228
283
|
resolveOutcome,
|
|
229
284
|
setRuns,
|
|
230
285
|
onFailureStatus,
|
|
286
|
+
throttle,
|
|
287
|
+
emit,
|
|
231
288
|
onError,
|
|
232
289
|
} = args
|
|
290
|
+
const { controller } = record
|
|
291
|
+
const live = (): boolean => isLive(recordsRef.current, threadId, controller)
|
|
292
|
+
const owns = (): boolean => isLatestRun(record)
|
|
233
293
|
let parts: TPart[] = []
|
|
294
|
+
const startedAt = now()
|
|
295
|
+
let firstContentAt: number | undefined
|
|
296
|
+
// Every commit re-checks run ownership: a trailing timer that fires after stop()/unmount/a newer
|
|
297
|
+
// run took the thread over must not resurrect or clobber its `runs` entry.
|
|
298
|
+
const committer = createRunCommitter<TPart>(throttle, (committed, status) => {
|
|
299
|
+
if (!live()) return
|
|
300
|
+
setRuns((prev) => {
|
|
301
|
+
const next = new Map(prev)
|
|
302
|
+
next.set(threadId, { status, parts: committed })
|
|
303
|
+
return next
|
|
304
|
+
})
|
|
305
|
+
})
|
|
234
306
|
try {
|
|
235
307
|
for await (const part of generator) {
|
|
236
|
-
// Guard:
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
// and is live in `runs` right now". Only the first case should ever clear `runs[threadId]`,
|
|
243
|
-
// and only the abort's OWN originator can tell the two apart without a race — the unmount-
|
|
244
|
-
// cleanup effect (see its doc) does that teardown synchronously, in the same tick as the
|
|
245
|
-
// abort, before any newer run could exist to be confused with. If this guard also deleted
|
|
246
|
-
// `runs[threadId]`, a superseding resume's fresh entry would be clobbered out from under it
|
|
247
|
-
// by its OWN predecessor's late-settling loop iteration — reintroducing a wedge instead of
|
|
248
|
-
// fixing one. `stop()`'s own abort path has the same property (finalizeStop's teardown runs
|
|
249
|
-
// synchronously before `stop()` returns, never racing a subsequent call).
|
|
250
|
-
if (controllersRef.current.get(threadId) !== controller) return
|
|
251
|
-
if (controller.signal.aborted) return
|
|
308
|
+
// Guard: the run ended (stop(), unmount) or a newer run replaced its record — stop updating
|
|
309
|
+
// state. Deliberately NOT a `runs` teardown site: a mismatch is ambiguous between "aborted
|
|
310
|
+
// and nothing replaced it" and "a newer run (a mount-reconcile resume on the SAME threadId)
|
|
311
|
+
// is live in `runs` right now", and only the ender can tell which; stop() and the unmount
|
|
312
|
+
// cleanup tear `runs` down synchronously with the end, before a newer run can exist.
|
|
313
|
+
if (!live()) return
|
|
252
314
|
if (isStartPart(part)) {
|
|
253
315
|
storeRef.current.setResumeToken(threadId, part.resumeToken)
|
|
254
316
|
continue
|
|
@@ -257,16 +319,17 @@ async function consumeAndFinalize<TPart extends PartLike>(args: {
|
|
|
257
319
|
// array it returns), so the local accumulator and the rendered `runs` snapshot below stay
|
|
258
320
|
// identical: a replayed/resumed delta rewrites its part instead of duplicating it.
|
|
259
321
|
parts = mergePart(parts, part)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
// Only proceed if this stream is still the current one and wasn't aborted.
|
|
267
|
-
if (controllersRef.current.get(threadId) !== controller || controller.signal.aborted) {
|
|
268
|
-
return
|
|
322
|
+
record.parts = parts
|
|
323
|
+
// The submitted → streaming edge commits at once (it is the TTFT mark and must not wait on
|
|
324
|
+
// the throttle window); later parts ride the throttle.
|
|
325
|
+
const firstContent = firstContentAt === undefined && isContentPart(part)
|
|
326
|
+
if (firstContent) firstContentAt = now()
|
|
327
|
+
committer.push(parts, firstContentAt === undefined ? 'submitted' : 'streaming', firstContent)
|
|
269
328
|
}
|
|
329
|
+
// The throttle's trailing commit: the last parts must reach `runs` before the settle below.
|
|
330
|
+
committer.flush()
|
|
331
|
+
// Only proceed if this stream is still the current one.
|
|
332
|
+
if (!live()) return
|
|
270
333
|
|
|
271
334
|
const assistantMessage: ChatMessage<TPart> = {
|
|
272
335
|
// appendMessage's idempotency key — mintMessageId, never mintThreadId (see that helper's
|
|
@@ -278,53 +341,89 @@ async function consumeAndFinalize<TPart extends PartLike>(args: {
|
|
|
278
341
|
finish: 'complete',
|
|
279
342
|
}
|
|
280
343
|
storeRef.current.appendMessage(threadId, assistantMessage)
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
appendedRef.current.set(threadId, controller)
|
|
344
|
+
// The terminal-append fact for THIS run, on its record: stop() reads it to avoid a second
|
|
345
|
+
// message, and the failure path reads it to avoid re-rendering the landed content as live.
|
|
346
|
+
record.appended = true
|
|
285
347
|
|
|
286
348
|
const priorThread = storeRef.current.threads.find((thread) => thread.id === threadId)
|
|
287
349
|
const snapshot = buildDoneSnapshot(priorThread, threadId, userMessage, assistantMessage)
|
|
288
|
-
|
|
350
|
+
record.resolving = true
|
|
351
|
+
let outcome: AgentOutcome
|
|
352
|
+
try {
|
|
353
|
+
outcome = await resolveOutcome(snapshot)
|
|
354
|
+
} finally {
|
|
355
|
+
// Settled either way: the record may now be swept once a render shows its outcome.
|
|
356
|
+
record.resolving = false
|
|
357
|
+
}
|
|
289
358
|
|
|
290
|
-
// Re-check: resolveOutcome above is the only await in this
|
|
291
|
-
// stop()
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
359
|
+
// Re-check: resolveOutcome above is the only await in this success path. If stop() ran while
|
|
360
|
+
// it was pending, stop() already settled the thread to 'done' (appending nothing: the message
|
|
361
|
+
// had landed) and deliberately did NOT call resolveOutcome a second time; this result is the
|
|
362
|
+
// one outcome of the turn, so it is recorded here. Any other end (unmount, a newer run)
|
|
363
|
+
// leaves the thread to whoever ended it.
|
|
364
|
+
if (!live()) {
|
|
365
|
+
// Only while no newer run has taken the thread: a stale resolver must not overwrite it.
|
|
366
|
+
if (record.ended === 'stopped' && record.appended && owns()) {
|
|
367
|
+
storeRef.current.setOutcome(threadId, outcome)
|
|
368
|
+
}
|
|
297
369
|
return
|
|
298
370
|
}
|
|
299
371
|
|
|
300
372
|
storeRef.current.setOutcome(threadId, outcome)
|
|
301
373
|
storeRef.current.setStatus(threadId, outcome.status)
|
|
302
374
|
storeRef.current.setResumeToken(threadId, undefined)
|
|
303
|
-
|
|
304
|
-
appendedRef.current.delete(threadId)
|
|
375
|
+
endRun(record, 'settled')
|
|
305
376
|
setRuns((prev) => {
|
|
306
377
|
if (!prev.has(threadId)) return prev
|
|
307
378
|
const next = new Map(prev)
|
|
308
379
|
next.delete(threadId)
|
|
309
380
|
return next
|
|
310
381
|
})
|
|
382
|
+
emit(
|
|
383
|
+
buildTurnEvent({
|
|
384
|
+
threadId,
|
|
385
|
+
startedAt,
|
|
386
|
+
firstContentAt,
|
|
387
|
+
partCount: parts.length,
|
|
388
|
+
status: outcome.status,
|
|
389
|
+
}),
|
|
390
|
+
)
|
|
311
391
|
} catch (err) {
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
392
|
+
// Guard: the run ended (stop(), unmount — which abort the controller, so their AbortError lands
|
|
393
|
+
// here) or a newer stream took the thread over. Only an abort from somewhere else gets past.
|
|
394
|
+
if (!live()) {
|
|
395
|
+
// A resolveOutcome that stop() handed the outcome to and that then REJECTED: the stopped turn
|
|
396
|
+
// stays 'done', but the failure still reaches the caller (raw, like any other).
|
|
397
|
+
if (record.ended === 'stopped' && record.appended) onError?.({ threadId, error: err })
|
|
398
|
+
return
|
|
399
|
+
}
|
|
400
|
+
const agentError = toAgentError(err)
|
|
401
|
+
// The turn's final message already landed in the thread (resolveOutcome or a store call failed
|
|
402
|
+
// AFTER appendMessage): the feed renders it from the thread, so the error entry keeps no parts.
|
|
403
|
+
const messageLanded = record.appended
|
|
404
|
+
endRun(record, 'failed')
|
|
318
405
|
storeRef.current.setStatus(threadId, onFailureStatus)
|
|
319
|
-
|
|
320
|
-
|
|
406
|
+
// The failed entry stays visible, with the error and the parts received so far, until the next
|
|
407
|
+
// start()/retry() on this thread replaces it or stop() dismisses it.
|
|
408
|
+
const failedParts = messageLanded ? [] : parts
|
|
321
409
|
setRuns((prev) => {
|
|
322
|
-
if (!prev.has(threadId)) return prev
|
|
323
410
|
const next = new Map(prev)
|
|
324
|
-
next.
|
|
411
|
+
next.set(threadId, { status: 'error', parts: failedParts, error: agentError })
|
|
325
412
|
return next
|
|
326
413
|
})
|
|
414
|
+
emit(
|
|
415
|
+
buildTurnEvent({
|
|
416
|
+
threadId,
|
|
417
|
+
startedAt,
|
|
418
|
+
firstContentAt,
|
|
419
|
+
partCount: parts.length,
|
|
420
|
+
status: onFailureStatus,
|
|
421
|
+
failure: { error: err, agentError },
|
|
422
|
+
}),
|
|
423
|
+
)
|
|
327
424
|
onError?.({ threadId, error: err })
|
|
425
|
+
} finally {
|
|
426
|
+
committer.cancel()
|
|
328
427
|
}
|
|
329
428
|
}
|
|
330
429
|
|
|
@@ -332,15 +431,17 @@ async function consumeAndFinalize<TPart extends PartLike>(args: {
|
|
|
332
431
|
* Finalizes a stop() call for `threadId` — the counterpart to consumeAndFinalize's success path,
|
|
333
432
|
* reused so the persist/settle/resolve sequence exists exactly once. `parts` is the accumulated
|
|
334
433
|
* (possibly partial) content read out of the run's `ThreadRunState` at the moment stop() was
|
|
335
|
-
* called; the caller has already
|
|
336
|
-
* invoking this. `alreadyAppended` is
|
|
337
|
-
*
|
|
434
|
+
* called; the caller has already ended the run's record and aborted its controller before
|
|
435
|
+
* invoking this. `alreadyAppended` is read by the caller (stop()) off the record's `appended` flag
|
|
436
|
+
* — an explicit marker rather than an inferred comparison. `outcomeInFlight` is the record's
|
|
437
|
+
* `resolving` flag: `consumeAndFinalize`'s own `resolveOutcome` is pending and records the
|
|
438
|
+
* outcome, so this function must not resolve a second time.
|
|
338
439
|
*
|
|
339
440
|
* Ordering / the append-failure guard: the append is attempted FIRST (so a successful stop still
|
|
340
441
|
* reads as 'done' with its partial content intact — this is the common case and the one that
|
|
341
442
|
* matters most), but it is wrapped in its own try/catch that CANNOT prevent the terminal
|
|
342
443
|
* transition below it. This is deliberate, not incidental: by the time this function runs, `stop()`
|
|
343
|
-
* has already
|
|
444
|
+
* has already ended the run's record (see `stop()`'s own doc),
|
|
344
445
|
* so a second `stop()` call is unconditionally a no-op regardless of what happens here — if the
|
|
345
446
|
* append (or `mintMessageId`, which now also throws on rung 3 — see `./id.ts`) throws and nothing
|
|
346
447
|
* downstream ran, the thread would be wedged at 'streaming' FOREVER with no way for the user to
|
|
@@ -358,12 +459,12 @@ async function consumeAndFinalize<TPart extends PartLike>(args: {
|
|
|
358
459
|
* distilling into an outcome when the turn's own final message never made it into the store.
|
|
359
460
|
*
|
|
360
461
|
* `setStatus`/`setResumeToken` are guarded for the SAME reason, and this is not belt-and-braces:
|
|
361
|
-
* they are consumer code exactly as `appendMessage` is (
|
|
462
|
+
* they are consumer code exactly as `appendMessage` is (an adapter over a
|
|
362
463
|
* remote backend, a store that rejects a threadId removed mid-stream), and guarding only the
|
|
363
464
|
* append leaves the identical wedge one line further down. The single thing that must ALWAYS run
|
|
364
465
|
* is the `setRuns` teardown — that entry is the HOOK'S OWN state, it is what `runs.get(threadId)`
|
|
365
|
-
* reports to the UI as "a turn is in flight", and by this point `stop()` has already
|
|
366
|
-
*
|
|
466
|
+
* reports to the UI as "a turn is in flight", and by this point `stop()` has already ENDED the
|
|
467
|
+
* run's record, so no later `stop()` can ever reach here again to clean it up. A
|
|
367
468
|
* throw above it therefore strands a phantom run for the lifetime of the hook. Each store call
|
|
368
469
|
* gets its OWN try so a failing `setStatus` cannot also skip clearing the resume token — a
|
|
369
470
|
* surviving token is separately harmful (it is what a later resume replays from).
|
|
@@ -372,11 +473,26 @@ async function finalizeStop<TPart extends PartLike>(args: {
|
|
|
372
473
|
threadId: string
|
|
373
474
|
parts: TPart[]
|
|
374
475
|
alreadyAppended: boolean
|
|
476
|
+
/** `consumeAndFinalize`'s own `resolveOutcome` is pending and will record the outcome. */
|
|
477
|
+
outcomeInFlight: boolean
|
|
478
|
+
/** The stopped run's record: held `resolving` while this function's own resolver is pending. */
|
|
479
|
+
record: RunRecord<TPart>
|
|
375
480
|
storeRef: MutableRefObject<ThreadsStore<TPart>>
|
|
376
481
|
resolveOutcome: OutcomeResolver<TPart>
|
|
377
482
|
setRuns: Dispatch<SetStateAction<Map<string, ThreadRunState<TPart>>>>
|
|
483
|
+
onError?: (info: { readonly threadId: string; readonly error: unknown }) => void
|
|
378
484
|
}): Promise<void> {
|
|
379
|
-
const {
|
|
485
|
+
const {
|
|
486
|
+
threadId,
|
|
487
|
+
parts,
|
|
488
|
+
alreadyAppended,
|
|
489
|
+
outcomeInFlight,
|
|
490
|
+
record,
|
|
491
|
+
onError,
|
|
492
|
+
storeRef,
|
|
493
|
+
resolveOutcome,
|
|
494
|
+
setRuns,
|
|
495
|
+
} = args
|
|
380
496
|
|
|
381
497
|
let appendFailed = false
|
|
382
498
|
if (!alreadyAppended && parts.length > 0) {
|
|
@@ -420,12 +536,57 @@ async function finalizeStop<TPart extends PartLike>(args: {
|
|
|
420
536
|
|
|
421
537
|
// No outcome to resolve for a turn whose own terminal message never landed, or whose settle
|
|
422
538
|
// never took — see this function's doc on why this mirrors consumeAndFinalize's catch path.
|
|
423
|
-
if (settleFailed) return
|
|
539
|
+
if (settleFailed || outcomeInFlight) return
|
|
424
540
|
|
|
425
541
|
const snapshot = storeRef.current.threads.find((thread) => thread.id === threadId)
|
|
426
542
|
if (snapshot === undefined) return
|
|
427
|
-
|
|
428
|
-
|
|
543
|
+
// Held `resolving` like consumeAndFinalize's own call: `sweepFlushed` keeps the record while the
|
|
544
|
+
// resolver is pending, so a later start() still supersedes it and a stale outcome is dropped.
|
|
545
|
+
record.resolving = true
|
|
546
|
+
try {
|
|
547
|
+
const outcome: AgentOutcome = await resolveOutcome(snapshot)
|
|
548
|
+
if (isLatestRun(record)) storeRef.current.setOutcome(threadId, outcome)
|
|
549
|
+
} catch (error) {
|
|
550
|
+
// `stop()` calls this with `void`: a rejection must reach the caller, not become unhandled.
|
|
551
|
+
// The stopped turn stays 'done'.
|
|
552
|
+
onError?.({ threadId, error })
|
|
553
|
+
} finally {
|
|
554
|
+
record.resolving = false
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/** The `chat.turn` event for a turn that completed (`failure` absent) or failed. */
|
|
559
|
+
function buildTurnEvent(args: {
|
|
560
|
+
threadId: string
|
|
561
|
+
startedAt: number
|
|
562
|
+
firstContentAt: number | undefined
|
|
563
|
+
partCount: number
|
|
564
|
+
status: ThreadStatus
|
|
565
|
+
failure?: { readonly error: unknown; readonly agentError: AgentError }
|
|
566
|
+
}): BasaltEvent {
|
|
567
|
+
const { threadId, startedAt, firstContentAt, partCount, status, failure } = args
|
|
568
|
+
const endedAt = now()
|
|
569
|
+
const base = {
|
|
570
|
+
start: startedAt,
|
|
571
|
+
duration: endedAt - startedAt,
|
|
572
|
+
}
|
|
573
|
+
const attributes = {
|
|
574
|
+
'basalt.chat.thread_id': threadId,
|
|
575
|
+
'basalt.chat.ttft_ms': (firstContentAt ?? endedAt) - startedAt,
|
|
576
|
+
'basalt.chat.parts': partCount,
|
|
577
|
+
'basalt.chat.status': status,
|
|
578
|
+
}
|
|
579
|
+
if (failure === undefined) return { ...base, type: 'chat.turn', failed: false, attributes }
|
|
580
|
+
return {
|
|
581
|
+
...base,
|
|
582
|
+
type: 'chat.turn',
|
|
583
|
+
failed: true,
|
|
584
|
+
attributes: {
|
|
585
|
+
...attributes,
|
|
586
|
+
'exception.type': errorName(failure.error),
|
|
587
|
+
'basalt.error.kind': failure.agentError.kind,
|
|
588
|
+
},
|
|
589
|
+
}
|
|
429
590
|
}
|
|
430
591
|
|
|
431
592
|
// ── useAgentThreadRuns ────────────────────────────────────────────────────────
|
|
@@ -447,6 +608,8 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
447
608
|
resolveOutcome,
|
|
448
609
|
toUserParts,
|
|
449
610
|
onError,
|
|
611
|
+
throttle = 100,
|
|
612
|
+
telemetry,
|
|
450
613
|
}: UseAgentThreadRunsArgs<TPart>): UseAgentThreadRunsReturn<TPart> {
|
|
451
614
|
const [runs, setRuns] = useState<Map<string, ThreadRunState<TPart>>>(new Map())
|
|
452
615
|
// Mirrors the latest `runs` state every render so stop() (a plain callback, not a render) can
|
|
@@ -458,31 +621,16 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
458
621
|
// closure from an earlier render never fires instead of the consumer's current callback.
|
|
459
622
|
const onErrorRef = useRef(onError)
|
|
460
623
|
onErrorRef.current = onError
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
// This replaces an earlier reference-equality check (`lastMessage.parts === parts`) that
|
|
472
|
-
// compared the array runsRef mirrors against the array the message actually persisted. That
|
|
473
|
-
// comparison implicitly depended on THREE things staying true at once: the run-state mirror not
|
|
474
|
-
// re-copying `parts` on every delta, the accumulator reassigning (never mutating) `parts`, and —
|
|
475
|
-
// the one that actually broke it — React's flush timing: runsRef is only updated during a
|
|
476
|
-
// render (`runsRef.current = runs`, assigned unconditionally on every render, not in an effect),
|
|
477
|
-
// so a setRuns dispatch that hasn't yet flushed into a render leaves runsRef holding an EARLIER
|
|
478
|
-
// parts array. A stop() landing between consumeAndFinalize's post-loop appendMessage and its
|
|
479
|
-
// `await resolveOutcome` resolving could observe exactly that staleness and defeat the
|
|
480
|
-
// reference-equality guard, double-appending — see the regression test with "resolveOutcome"
|
|
481
|
-
// in its name. Keying by controller identity (an AbortController, freshly minted per run) makes
|
|
482
|
-
// a false-positive match across two different runs on the same thread structurally
|
|
483
|
-
// impossible — no render, no array-copy semantics, no accumulator-reassignment assumption is
|
|
484
|
-
// load-bearing anymore.
|
|
485
|
-
const appendedRef = useRef<Map<string, AbortController>>(new Map())
|
|
624
|
+
// Same for the telemetry sink, read at emit time so an inline function never re-subscribes.
|
|
625
|
+
const providerSink = useContext(TelemetryContext)
|
|
626
|
+
const telemetryRef = useRef(telemetry ?? providerSink)
|
|
627
|
+
telemetryRef.current = telemetry ?? providerSink
|
|
628
|
+
const throttleRef = useRef(throttle)
|
|
629
|
+
throttleRef.current = throttle
|
|
630
|
+
// One record per thread id holds the run's lifecycle — controller, unthrottled parts, whether its
|
|
631
|
+
// terminal message landed, how it ended — written together through `./run-records` (see there for
|
|
632
|
+
// why these are not separate maps). A run is live while its record's `ended` is undefined.
|
|
633
|
+
const recordsRef = useRef<RunRecords<TPart>>(new Map())
|
|
486
634
|
// Caches the last user input per thread so retry() can replay a failed turn without the
|
|
487
635
|
// caller having to hold onto (or re-collect) what was typed.
|
|
488
636
|
const lastInputRef = useRef<Map<string, string>>(new Map())
|
|
@@ -505,6 +653,12 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
505
653
|
const toUserPartsRef = useRef(resolvedToUserParts)
|
|
506
654
|
toUserPartsRef.current = resolvedToUserParts
|
|
507
655
|
|
|
656
|
+
// Stable over the sink ref, so an inline `telemetry` function never changes what `start` closes over.
|
|
657
|
+
const emitTelemetry = useMemo<Telemetry>(
|
|
658
|
+
() => createEmitter((event) => telemetryRef.current?.(event)),
|
|
659
|
+
[],
|
|
660
|
+
)
|
|
661
|
+
|
|
508
662
|
// Resolves the transport for `threadId`: the shared object as-is for the plain-object form
|
|
509
663
|
// (unchanged behavior), or the factory's result — resolved once per thread id and cached in
|
|
510
664
|
// transportsRef thereafter — for the factory form. Not itself memoized (cheap to recreate each
|
|
@@ -520,21 +674,21 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
520
674
|
}
|
|
521
675
|
|
|
522
676
|
// Abort every in-flight stream on unmount to stop the async generators. Also clears the map —
|
|
523
|
-
// leaving aborted
|
|
524
|
-
//
|
|
677
|
+
// leaving aborted records behind would make the mount-reconcile effect below see a live run
|
|
678
|
+
// for a thread whose only consumer was just aborted,
|
|
525
679
|
// skipping the orphan-resume path and wedging the thread in 'streaming' forever. Reachable
|
|
526
680
|
// whenever this fiber's effects re-run without the fiber itself unmounting (React 19 StrictMode
|
|
527
681
|
// double-invoke; `<Activity>` hide/show) — see this hook's `@example`-adjacent doc / the F3 note.
|
|
528
682
|
//
|
|
529
683
|
// Also tears down the matching `runs` entries — the F3 fix above stopped this from wedging
|
|
530
|
-
//
|
|
684
|
+
// the run records/the *persisted* thread status, but left `runs` (this hook's own transient
|
|
531
685
|
// state) untouched, and on a re-run-without-unmount the component (and its `runs` state) survive
|
|
532
686
|
// this cleanup. Without this, the entry the aborted controller was updating stays in `runs`
|
|
533
687
|
// reporting `status: 'streaming'` forever: the mount-reconcile effect below DOES correctly settle
|
|
534
688
|
// the persisted thread (to a resumed run, or to 'interrupted' if not resumable), but a consumer
|
|
535
689
|
// deriving "is this thread live" from `runs.has(id)` — the whole reason `runs` exists — keeps
|
|
536
690
|
// seeing a phantom in-flight turn no controller is driving anymore, and `stop()` on it was a
|
|
537
|
-
// permanent no-op (
|
|
691
|
+
// permanent no-op (no live record left to abort) — see `stop()`'s own doc. Scoped to
|
|
538
692
|
// exactly the threadIds this pass is aborting (not a blind `runs.clear()`): the mount-reconcile
|
|
539
693
|
// effect below runs AFTER this cleanup completes, in the same synchronous commit — StrictMode's
|
|
540
694
|
// dev-only double-invoke and `<Activity>` hide/show both destroy-then-recreate EVERY mount effect
|
|
@@ -548,10 +702,13 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
548
702
|
// itself aborting is exact, not merely defensive.
|
|
549
703
|
useEffect(
|
|
550
704
|
() => () => {
|
|
551
|
-
const abortedThreadIds =
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
705
|
+
const abortedThreadIds: string[] = []
|
|
706
|
+
for (const [threadId, record] of recordsRef.current) {
|
|
707
|
+
if (record.ended !== undefined) continue
|
|
708
|
+
abortedThreadIds.push(threadId)
|
|
709
|
+
record.controller.abort()
|
|
710
|
+
}
|
|
711
|
+
recordsRef.current.clear()
|
|
555
712
|
if (abortedThreadIds.length === 0) return
|
|
556
713
|
setRuns((prev) => {
|
|
557
714
|
let next: Map<string, ThreadRunState<TPart>> | undefined
|
|
@@ -566,8 +723,14 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
566
723
|
[],
|
|
567
724
|
)
|
|
568
725
|
|
|
726
|
+
// Once a render shows a run's outcome, its terminal record has done its job (covering the window
|
|
727
|
+
// before that render); drop it so thread ids are not retained for the life of the hook.
|
|
728
|
+
useEffect(() => {
|
|
729
|
+
sweepFlushed(recordsRef.current, runs)
|
|
730
|
+
})
|
|
731
|
+
|
|
569
732
|
// Reconcile orphaned in-flight threads once the store is HYDRATED: a persisted thread can be
|
|
570
|
-
// stuck 'pending' or 'streaming' after a reload/remount, since
|
|
733
|
+
// stuck 'pending' or 'streaming' after a reload/remount, since the run records and `runs` both
|
|
571
734
|
// start empty — nothing would otherwise resolve that skeleton. If the transport supports
|
|
572
735
|
// resumption and the thread has a resumeToken (from a StartPart emitted before the disconnect)
|
|
573
736
|
// plus a user message to resume from, attempt to reconnect the run instead. Any other case — no
|
|
@@ -600,7 +763,7 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
600
763
|
for (const thread of storeRef.current.threads) {
|
|
601
764
|
const orphaned =
|
|
602
765
|
(thread.status === 'pending' || thread.status === 'streaming') &&
|
|
603
|
-
!
|
|
766
|
+
!hasLiveRun(recordsRef.current, thread.id)
|
|
604
767
|
if (!orphaned) continue
|
|
605
768
|
|
|
606
769
|
const resumeToken = thread.resumeToken
|
|
@@ -620,16 +783,16 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
620
783
|
const resume = resolvedTransport.resume
|
|
621
784
|
|
|
622
785
|
const controller = new AbortController()
|
|
623
|
-
|
|
786
|
+
const record = beginRun(recordsRef.current, thread.id, controller)
|
|
624
787
|
setRuns((prev) => {
|
|
625
788
|
const next = new Map(prev)
|
|
626
|
-
next.set(thread.id, { status: '
|
|
789
|
+
next.set(thread.id, { status: 'submitted', parts: [] })
|
|
627
790
|
return next
|
|
628
791
|
})
|
|
629
792
|
|
|
630
793
|
void consumeAndFinalize({
|
|
631
794
|
threadId: thread.id,
|
|
632
|
-
|
|
795
|
+
record,
|
|
633
796
|
// Namespaced by thread.id (stable across a thread's whole lifetime, including this
|
|
634
797
|
// resume) rather than per-call — see withPartIds' own doc for why this is NOT a replay-
|
|
635
798
|
// convergence guarantee on its own: it only protects against two id-less drafts within
|
|
@@ -639,12 +802,13 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
639
802
|
// providing that safety.
|
|
640
803
|
generator: withPartIds(thread.id, resume(resumeToken, controller.signal)),
|
|
641
804
|
userMessage: lastUserMessage,
|
|
642
|
-
|
|
643
|
-
appendedRef,
|
|
805
|
+
recordsRef,
|
|
644
806
|
storeRef,
|
|
645
807
|
resolveOutcome,
|
|
646
808
|
setRuns,
|
|
647
809
|
onFailureStatus: 'interrupted',
|
|
810
|
+
throttle: throttleRef.current,
|
|
811
|
+
emit: emitTelemetry,
|
|
648
812
|
onError: (info) => onErrorRef.current?.(info),
|
|
649
813
|
})
|
|
650
814
|
}
|
|
@@ -657,7 +821,7 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
657
821
|
const start = useCallback(
|
|
658
822
|
(threadId: string, input: string): void => {
|
|
659
823
|
// Ignore a second concurrent turn on the SAME thread; different threads run concurrently.
|
|
660
|
-
if (
|
|
824
|
+
if (hasLiveRun(recordsRef.current, threadId)) return
|
|
661
825
|
|
|
662
826
|
lastInputRef.current.set(threadId, input)
|
|
663
827
|
|
|
@@ -672,19 +836,19 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
672
836
|
storeRef.current.setStatus(threadId, 'streaming')
|
|
673
837
|
setRuns((prev) => {
|
|
674
838
|
const next = new Map(prev)
|
|
675
|
-
next.set(threadId, { status: '
|
|
839
|
+
next.set(threadId, { status: 'submitted', parts: [] })
|
|
676
840
|
return next
|
|
677
841
|
})
|
|
678
842
|
|
|
679
843
|
const controller = new AbortController()
|
|
680
|
-
|
|
844
|
+
const record = beginRun(recordsRef.current, threadId, controller)
|
|
681
845
|
const resolvedTransport = resolveTransport(threadId)
|
|
682
846
|
|
|
683
847
|
// Non-awaited: this consumer runs in the background so start() returns immediately and
|
|
684
848
|
// multiple threads can stream at once.
|
|
685
849
|
void consumeAndFinalize({
|
|
686
850
|
threadId,
|
|
687
|
-
|
|
851
|
+
record,
|
|
688
852
|
// Namespaced by threadId — same choice, same caveat, as the mount-time resume call above.
|
|
689
853
|
// ctx.messageId is this turn's OWN idempotency key (userMessage.id, just minted above) —
|
|
690
854
|
// a transport that accepts it (aiSdkTransport) mints its wire-level message with the SAME
|
|
@@ -694,16 +858,17 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
694
858
|
resolvedTransport.stream(input, controller.signal, { messageId: userMessage.id }),
|
|
695
859
|
),
|
|
696
860
|
userMessage,
|
|
697
|
-
|
|
698
|
-
appendedRef,
|
|
861
|
+
recordsRef,
|
|
699
862
|
storeRef,
|
|
700
863
|
resolveOutcome,
|
|
701
864
|
setRuns,
|
|
702
865
|
onFailureStatus: 'error',
|
|
866
|
+
throttle: throttleRef.current,
|
|
867
|
+
emit: emitTelemetry,
|
|
703
868
|
onError: (info) => onErrorRef.current?.(info),
|
|
704
869
|
})
|
|
705
870
|
},
|
|
706
|
-
[resolveOutcome],
|
|
871
|
+
[emitTelemetry, resolveOutcome],
|
|
707
872
|
)
|
|
708
873
|
|
|
709
874
|
const retry = useCallback(
|
|
@@ -715,61 +880,61 @@ export function useAgentThreadRuns<TPart extends PartLike = AgentPart>({
|
|
|
715
880
|
[start],
|
|
716
881
|
)
|
|
717
882
|
|
|
883
|
+
const dismiss = useCallback((threadId: string): void => {
|
|
884
|
+
dismissRun(recordsRef.current, threadId)
|
|
885
|
+
setRuns((prev) => {
|
|
886
|
+
if (!prev.has(threadId)) return prev
|
|
887
|
+
const next = new Map(prev)
|
|
888
|
+
next.delete(threadId)
|
|
889
|
+
return next
|
|
890
|
+
})
|
|
891
|
+
}, [])
|
|
892
|
+
|
|
718
893
|
const stop = useCallback(
|
|
719
894
|
(threadId: string): void => {
|
|
720
|
-
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
//
|
|
724
|
-
// the
|
|
725
|
-
//
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
// path in THIS file today (the unmount-cleanup effect and finalizeStop both tear down
|
|
735
|
-
// `controllersRef` and `runs` in the same synchronous step — see their docs), but a true
|
|
736
|
-
// no-op here would mean "the UI shows this thread streaming forever, and Stop can never
|
|
737
|
-
// clear it" the instant that invariant is ever broken by a future change. Settle it via the
|
|
738
|
-
// same finalizeStop() the normal path uses, so there is still exactly one teardown routine
|
|
739
|
-
// for "a run is ending" regardless of how it got here.
|
|
740
|
-
if (!runsRef.current.has(threadId)) return
|
|
741
|
-
const parts = runsRef.current.get(threadId)?.parts ?? []
|
|
742
|
-
const alreadyAppended = appendedRef.current.has(threadId)
|
|
743
|
-
appendedRef.current.delete(threadId)
|
|
744
|
-
void finalizeStop({ threadId, parts, alreadyAppended, storeRef, resolveOutcome, setRuns })
|
|
895
|
+
// Decided from the run's record, which start() writes synchronously — NOT from `runsRef`,
|
|
896
|
+
// which only mirrors the last flushed render. `onClick={() => { start(id, text); stop(id) }}`
|
|
897
|
+
// has a live record but no rendered entry yet, and a settle's `setRuns` leaves the entry
|
|
898
|
+
// visible to `runsRef` until the next render; reading `runs` here stranded the first and
|
|
899
|
+
// double-appended the second. `planStop` falls back to the rendered entry only when no
|
|
900
|
+
// record exists.
|
|
901
|
+
const plan = planStopWithTombstone(
|
|
902
|
+
recordsRef.current,
|
|
903
|
+
threadId,
|
|
904
|
+
runsRef.current.get(threadId),
|
|
905
|
+
)
|
|
906
|
+
if (plan.kind === 'noop') return
|
|
907
|
+
if (plan.kind === 'dismiss') {
|
|
908
|
+
dismiss(threadId)
|
|
745
909
|
return
|
|
746
910
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
void finalizeStop({ threadId, parts, alreadyAppended, storeRef, resolveOutcome, setRuns })
|
|
911
|
+
const { record, parts } = plan
|
|
912
|
+
// Capture before endRun empties the record.
|
|
913
|
+
const { appended, resolving } = record
|
|
914
|
+
endRun(record, 'stopped')
|
|
915
|
+
record.controller.abort()
|
|
916
|
+
void finalizeStop({
|
|
917
|
+
threadId,
|
|
918
|
+
parts,
|
|
919
|
+
alreadyAppended: appended,
|
|
920
|
+
outcomeInFlight: resolving,
|
|
921
|
+
record,
|
|
922
|
+
storeRef,
|
|
923
|
+
resolveOutcome,
|
|
924
|
+
setRuns,
|
|
925
|
+
onError: (info) => onErrorRef.current?.(info),
|
|
926
|
+
})
|
|
765
927
|
},
|
|
766
|
-
[resolveOutcome],
|
|
928
|
+
[dismiss, resolveOutcome],
|
|
767
929
|
)
|
|
768
930
|
|
|
769
931
|
const stopAll = useCallback((): void => {
|
|
770
|
-
for (const threadId of Array.from(
|
|
771
|
-
stop(threadId)
|
|
932
|
+
for (const [threadId, record] of Array.from(recordsRef.current)) {
|
|
933
|
+
if (record.ended === undefined) stop(threadId)
|
|
772
934
|
}
|
|
935
|
+
// Failed entries have no live run. Sweep the records AND what `runs` shows: an unmount re-run
|
|
936
|
+
// (StrictMode, `<Activity>`) clears the records while the failed entries survive.
|
|
937
|
+
for (const threadId of failedThreadIds(recordsRef.current, runsRef.current)) stop(threadId)
|
|
773
938
|
}, [stop])
|
|
774
939
|
|
|
775
940
|
return { runs, start, retry, stop, stopAll }
|