basalt-ui 1.9.0 → 1.10.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 +1 -0
- package/CHANGELOG.md +8 -0
- package/README.md +64 -34
- package/agent/rules/basalt-agent.md +49 -6
- package/configs/oxlint-plugin.js +279 -0
- package/configs/oxlint-plugin.test.ts +216 -1
- package/configs/oxlint.json +5 -2
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +1 -0
- package/dist/agent-chat/index.d.ts +4 -3
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +131 -0
- package/dist/surfaces.d.ts +10 -0
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +21 -0
- package/llms.txt +9 -2
- package/package.json +6 -1
- package/src/agent/ai-sdk-transport.test.ts +269 -0
- package/src/agent/parts.test.ts +261 -0
- package/src/agent/thread.test.ts +147 -0
- package/src/agent/use-agent-stream.test.tsx +231 -0
- package/src/agent/use-agent-thread-runs.resume.test.tsx +261 -0
- package/src/agent/use-agent-thread-runs.test.tsx +398 -0
- package/src/agent/use-agent-thread-runs.ts +7 -1
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +299 -0
- package/src/agent-chat/index.ts +4 -3
- package/src/charts/primitives/ChartFrame.test.tsx +8 -5
- package/src/cli/index.ts +285 -0
- package/src/dashboard/stat-card.test.tsx +5 -3
- package/src/provider/index.test.tsx +6 -5
- package/src/surfaces.ts +22 -0
- package/src/theme/use-basalt-spacing.test.tsx +7 -4
- package/src/tokens/build-fonts-css.test.ts +3 -2
package/AGENTS.md
CHANGED
|
@@ -23,6 +23,7 @@ Toolchain: Bun runtime, oxlint + oxfmt, conventional commits (empty scope), `mas
|
|
|
23
23
|
| `basalt-ui/data/table` | mantine-coupled | BasaltDataTable: a sortable data table over TanStack Table, rendered with Mantine (Mantine-rendered) |
|
|
24
24
|
| `basalt-ui/data/virtual` | mantine-coupled | BasaltVirtualList: a windowed virtual list over TanStack Virtual, rendered with Mantine (Mantine-rendered) |
|
|
25
25
|
| `basalt-ui/agent` | headless | Headless streaming-chat layer: useAgentStream, aiSdkTransport (recommended default) + edenTransport, PartList, plus the multi-thread createThreadsStore + useAgentThreadRuns + outcome-resolver seam (Mantine-free) |
|
|
26
|
+
| `basalt-ui/agent-chat` | mantine-coupled | Mantine-styled thread-chat components over basalt-ui/agent: ThreadWorkspace, ThreadFeed, ThreadOutcomeCard, ThreadDetailPanel, Composer, ThreadTranscript, threadPartRenderers (Mantine-coupled). remend and motion are required, not optional — ThreadTranscript imports content/markdown.tsx (remend eagerly), and ThreadFeed/ThreadDetailPanel import motion/react eagerly, so this subpath fails to resolve without both installed even though peerDependenciesMeta marks them optional (npm has no per-subpath optionality). |
|
|
26
27
|
| `basalt-ui/content` | mantine-coupled | Prose (article/chat typography), CodeBlock (shiki, optional peer), Callout, TableOfContents, ReadingProgress, Markdown (react-markdown + remark-gfm, optional peers, streaming-aware), MermaidDiagram (beautiful-mermaid, optional peer), mdxComponents/createMdxComponents, ArticleLayout (docs-page frame), ArticleCard/ArticleGrid (overview cards), Article model (sortArticles/filterArticles/formatArticleDate), ArticleFilterBar (category/tags filter UI), toArticleActions (Spotlight projector, @mantine/spotlight type-only), GuideLink/GuideDrawer (contextual-help drawer) — the content/prose surface |
|
|
27
28
|
| `basalt-ui/state` | headless | createPersistedState (versioned localStorage) + useOnlineStatus — Mantine-free state primitives |
|
|
28
29
|
| `basalt-ui/connectivity` | mantine-coupled | ConnectivityProvider (aggregates browser online/offline, React Query onlineManager, SSE, and health-check pings into one status), useConnectivity, and ConnectivityIndicator — auto-mounted by BasaltProvider |
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# [1.10.0](https://github.com/jkrumm/basalt-ui/compare/v1.9.0...v1.10.0) (2026-08-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* open ./agent-chat as its own door, and put the agent layer under test ([2005aa6](https://github.com/jkrumm/basalt-ui/commit/2005aa6db90d02e9310188873027ecf8b93027ff))
|
|
7
|
+
* prove the agent-chat subpath and the streaming wedge in the playground ([caff9c4](https://github.com/jkrumm/basalt-ui/commit/caff9c4f4a3d92259a1e8a88348474c90dc4c18a))
|
|
8
|
+
|
|
1
9
|
# [1.9.0](https://github.com/jkrumm/basalt-ui/compare/v1.8.0...v1.9.0) (2026-08-02)
|
|
2
10
|
|
|
3
11
|
|
package/README.md
CHANGED
|
@@ -195,34 +195,55 @@ key in your `package.json`:
|
|
|
195
195
|
scanned files. Other `"basalt"` config keys (`exempt`,
|
|
196
196
|
`spacingSteps`, `forbiddenAccents`, …) are documented on the `BasaltConfig` type.
|
|
197
197
|
|
|
198
|
+
`doctor`'s `ai-major-parity` hard check fails a monorepo where workspace packages declare different
|
|
199
|
+
`ai` package majors — unless the split is intentional and written down. A producer pinned to an
|
|
200
|
+
older major with a client on a newer one, neutralized by a transform, is a locked decision, not a
|
|
201
|
+
defect:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
// package.json (repo root)
|
|
205
|
+
{
|
|
206
|
+
"basalt": {
|
|
207
|
+
"aiMajorSkewReason": "apps/api streams on ai@5, apps/dashboard parses on ai@7 — the one enum value that differs is neutralized by a producer-side TransformStream in apps/api/src/stream-transform.ts"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The value **is** the reason — omitting the key, or setting it to `true`/`""`, does not weaken the
|
|
213
|
+
check; `doctor` still hard-fails a detected skew exactly as if `aiMajorSkewReason` were absent. Once
|
|
214
|
+
the reason is written, `doctor` passes and echoes both the detected skew and the reason back. If the
|
|
215
|
+
majors later come back into agreement, `doctor` warns that the declaration is stale so it gets
|
|
216
|
+
deleted rather than silently surviving into the next real skew.
|
|
217
|
+
|
|
198
218
|
---
|
|
199
219
|
|
|
200
220
|
## Subpath exports
|
|
201
221
|
|
|
202
|
-
| Subpath | Mantine? | Purpose
|
|
203
|
-
| ------------------- | -------- |
|
|
204
|
-
| `.` | coupled | `BasaltProvider`, `createBasaltTheme` / `baseTheme` / `cssVariablesResolver`, `BasaltShell` + sidebar / mobile-nav / breadcrumbs / page-header, `NavCountBadge`, `SidebarAccount` + the provider-agnostic account contract (`BasaltAccountProps`/`State`/`Actions`), `ThreadWorkspace` + thread-chat components, shell types |
|
|
205
|
-
| `./charts` | **free** | visx chart primitives, sparklines, hooks, and token re-exports
|
|
206
|
-
| `./tokens` | **free** | `VX` token refs, `buildPaletteCss`, `defineSeries`, `seriesTokens`, `groupTokens`, `alpha`, `ColorPair` / `SeriesMap` types
|
|
207
|
-
| `./theme-lab` | coupled | `ThemeLabControls`, `applyOverrides`, `loadOverrides`, `COLOR_GROUPS` for live theme inspection
|
|
208
|
-
| `./vite` | — | `basaltViteConfig(opts)` — Vite preset for basalt-ui consumer apps; `basaltAppPlugin(opts)` — PWA head, manifest, and icon metadata derived from the token palette
|
|
209
|
-
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core
|
|
210
|
-
| `./query` | **free** | `createBasaltQueryClient`, transport-agnostic `unwrap`, lazy `BasaltQueryDevtools`
|
|
211
|
-
| `./router-tanstack` | **free** | TanStack Router bridge: `useBasaltNav` (active route) + `useRouterBreadcrumbs`
|
|
212
|
-
| `./forms` | coupled | Mantine form adapter: `useBasaltForm`, `field`, `FormErrorSummary`, `useFormDraft` (Standard Schema)
|
|
213
|
-
| `./notifications` | coupled | Mantine notifications: `notify` helpers, typed registry, persisted history, `NotificationBell`
|
|
214
|
-
| `./commands` | coupled | Typed command bus + overlay controller, `toSpotlightActions`, `ShortcutsHelp`, `BasaltOverlays`
|
|
215
|
-
| `./data` | coupled | Convenience barrel pulling both peer groups: `BasaltDataTable`, `BasaltVirtualList` (Mantine-rendered) — prefer `./data/table` / `./data/virtual` for per-feature opt-in
|
|
216
|
-
| `./data/table` | coupled | `BasaltDataTable` — sortable data table over TanStack Table, rendered with Mantine
|
|
217
|
-
| `./data/virtual` | coupled | `BasaltVirtualList` — windowed virtual list over TanStack Virtual, rendered with Mantine
|
|
218
|
-
| `./agent` | **free** | Headless streaming layer (`useAgentStream`, `PartList`) + multi-thread `createThreadsStore` / `useAgentThreadRuns` / outcome seam
|
|
219
|
-
| `./
|
|
220
|
-
| `./
|
|
221
|
-
| `./
|
|
222
|
-
| `./
|
|
223
|
-
| `./
|
|
224
|
-
| `./
|
|
225
|
-
| `./
|
|
222
|
+
| Subpath | Mantine? | Purpose |
|
|
223
|
+
| ------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
224
|
+
| `.` | coupled | `BasaltProvider`, `createBasaltTheme` / `baseTheme` / `cssVariablesResolver`, `BasaltShell` + sidebar / mobile-nav / breadcrumbs / page-header, `NavCountBadge`, `SidebarAccount` + the provider-agnostic account contract (`BasaltAccountProps`/`State`/`Actions`), `ThreadWorkspace` + thread-chat components, shell types — the root entry still re-exports the thread-chat components; `./agent-chat` takes the same components standalone, without `BasaltProvider`, the shell, the dashboard composites, or `./connectivity` |
|
|
225
|
+
| `./charts` | **free** | visx chart primitives, sparklines, hooks, and token re-exports |
|
|
226
|
+
| `./tokens` | **free** | `VX` token refs, `buildPaletteCss`, `defineSeries`, `seriesTokens`, `groupTokens`, `alpha`, `ColorPair` / `SeriesMap` types |
|
|
227
|
+
| `./theme-lab` | coupled | `ThemeLabControls`, `applyOverrides`, `loadOverrides`, `COLOR_GROUPS` for live theme inspection |
|
|
228
|
+
| `./vite` | — | `basaltViteConfig(opts)` — Vite preset for basalt-ui consumer apps; `basaltAppPlugin(opts)` — PWA head, manifest, and icon metadata derived from the token palette |
|
|
229
|
+
| `./guard` | **free** | `checkSource`, `GUARD_RULES`, `Finding` types — the headless theme-guard core |
|
|
230
|
+
| `./query` | **free** | `createBasaltQueryClient`, transport-agnostic `unwrap`, lazy `BasaltQueryDevtools` |
|
|
231
|
+
| `./router-tanstack` | **free** | TanStack Router bridge: `useBasaltNav` (active route) + `useRouterBreadcrumbs` |
|
|
232
|
+
| `./forms` | coupled | Mantine form adapter: `useBasaltForm`, `field`, `FormErrorSummary`, `useFormDraft` (Standard Schema) |
|
|
233
|
+
| `./notifications` | coupled | Mantine notifications: `notify` helpers, typed registry, persisted history, `NotificationBell` |
|
|
234
|
+
| `./commands` | coupled | Typed command bus + overlay controller, `toSpotlightActions`, `ShortcutsHelp`, `BasaltOverlays` |
|
|
235
|
+
| `./data` | coupled | Convenience barrel pulling both peer groups: `BasaltDataTable`, `BasaltVirtualList` (Mantine-rendered) — prefer `./data/table` / `./data/virtual` for per-feature opt-in |
|
|
236
|
+
| `./data/table` | coupled | `BasaltDataTable` — sortable data table over TanStack Table, rendered with Mantine |
|
|
237
|
+
| `./data/virtual` | coupled | `BasaltVirtualList` — windowed virtual list over TanStack Virtual, rendered with Mantine |
|
|
238
|
+
| `./agent` | **free** | Headless streaming layer (`useAgentStream`, `PartList`) + multi-thread `createThreadsStore` / `useAgentThreadRuns` / outcome seam |
|
|
239
|
+
| `./agent-chat` | coupled | Mantine-styled thread-chat components over `./agent`: `ThreadWorkspace`, `ThreadFeed`, `ThreadOutcomeCard`, `ThreadDetailPanel`, `Composer`, `ThreadTranscript`, `threadPartRenderers` — **requires `remend` and `motion`**, not merely optional peers; see [Requirements](#requirements) |
|
|
240
|
+
| `./state` | **free** | `createPersistedState` (versioned localStorage) + `useOnlineStatus` — Mantine-free state primitives |
|
|
241
|
+
| `./connectivity` | coupled | `ConnectivityProvider` (aggregates browser/React-Query/SSE/health-check status), `useConnectivity`, `ConnectivityIndicator` — auto-mounted by `BasaltProvider` |
|
|
242
|
+
| `./content` | coupled | `Prose`, `CodeBlock`, `Callout`, `TableOfContents`, `ReadingProgress`, `Markdown`, `MermaidDiagram`, `ArticleLayout`, `ArticleCard` / `ArticleGrid`, `GuideLink` / `GuideDrawer`, `mdxComponents` |
|
|
243
|
+
| `./styles.css` | — | `@layer basalt` base styles, iOS input safety net, font stack |
|
|
244
|
+
| `./tokens.css` | **free** | Prebuilt `--vx-*` stylesheet — the default `buildPaletteCss()` output as a plain file, for a consumer with no bundler, React or Mantine; `basalt-ui tokens:css` re-emits it with a custom scheme selector |
|
|
245
|
+
| `./configs/*` | — | Raw toolchain presets — oxlint, oxfmt, tsconfig (base/react-app/node), lefthook |
|
|
246
|
+
| `./llms.txt` | — | Machine-readable surface map — one entry per published subpath with import specifier, description, layer, and optional peers |
|
|
226
247
|
|
|
227
248
|
Named exports only — no default exports.
|
|
228
249
|
|
|
@@ -429,7 +450,11 @@ bun add beautiful-mermaid # MermaidDiagram / ```mermaid fences
|
|
|
429
450
|
````
|
|
430
451
|
|
|
431
452
|
`remend` is the one peer here that is not lazy: `Markdown` imports it at the top of the module, so
|
|
432
|
-
`basalt-ui/content` fails to resolve without it even if you never render `Markdown` yourself.
|
|
453
|
+
`basalt-ui/content` fails to resolve without it even if you never render `Markdown` yourself. That
|
|
454
|
+
eager import also reaches past this subpath: both the root `.` entry and `./agent-chat`'s
|
|
455
|
+
`ThreadWorkspace` / `ThreadTranscript` import `Markdown` directly (not lazily) as their text
|
|
456
|
+
renderer, so `remend` is listed as **required** in the [Requirements](#requirements) table too, not
|
|
457
|
+
only here.
|
|
433
458
|
Everything else degrades gracefully when absent (markdown falls back to plain text, fences to
|
|
434
459
|
plain mono, mermaid renders nothing). `Markdown` is the package's **only** markdown
|
|
435
460
|
renderer — it backs long-form content and AI chat alike:
|
|
@@ -468,10 +493,11 @@ const { parts, send, status } = useAgentStream({ transport })
|
|
|
468
493
|
```
|
|
469
494
|
|
|
470
495
|
Many short chats — the `ThreadWorkspace` composite (a distilled-outcome feed + a detail panel).
|
|
471
|
-
The headless multi-thread layer lives in `./agent`; the Mantine chrome ships from
|
|
496
|
+
The headless multi-thread layer lives in `./agent`; the Mantine chrome ships from
|
|
497
|
+
`basalt-ui/agent-chat` (also re-exported from the root entry):
|
|
472
498
|
|
|
473
499
|
```tsx
|
|
474
|
-
import { ThreadWorkspace } from 'basalt-ui'
|
|
500
|
+
import { ThreadWorkspace } from 'basalt-ui/agent-chat'
|
|
475
501
|
import { createThreadsStore, heuristicOutcome } from 'basalt-ui/agent'
|
|
476
502
|
|
|
477
503
|
// Once at module scope:
|
|
@@ -541,7 +567,7 @@ bunx basalt-ui init # scaffold doctrine into a consumer repo
|
|
|
541
567
|
bunx basalt-ui sync # three-way diff against .basalt/manifest.json after a basalt-ui upgrade
|
|
542
568
|
bunx basalt-ui sync --check # CI drift gate — non-zero exit on any managed-file drift
|
|
543
569
|
bunx basalt-ui check-theme # palette guard — fails on colors that bypass the central --vx-* system
|
|
544
|
-
bunx basalt-ui doctor # check consumer repo basalt integration health
|
|
570
|
+
bunx basalt-ui doctor # check consumer repo basalt integration health: manifest presence, node_modules-vs-manifest version, spacing-scale drift, and a cross-package `ai` major-version parity check (HARD failure — walks every workspace manifest, exemptable via `basalt.aiMajorSkewReason` — see above) + warn on missing basaltAppPlugin icon files under public/
|
|
545
571
|
bunx basalt-ui info # human-readable surface map: subpath, layer, rule, skills, optional peers
|
|
546
572
|
bunx basalt-ui info --json # same map as stable JSON (InfoOutput shape)
|
|
547
573
|
```
|
|
@@ -580,12 +606,16 @@ Wire the drift gate to catch doctrine falling behind after a basalt-ui upgrade:
|
|
|
580
606
|
|
|
581
607
|
## Requirements
|
|
582
608
|
|
|
583
|
-
| Peer | Version
|
|
584
|
-
| ----------------------- |
|
|
585
|
-
| `react` / `react-dom` | `^19`
|
|
586
|
-
| `@mantine/core` | `^9.3`
|
|
587
|
-
| `@mantine/hooks` | `^9.3`
|
|
588
|
-
| `@tanstack/react-query` | —
|
|
609
|
+
| Peer | Version | Notes |
|
|
610
|
+
| ----------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
611
|
+
| `react` / `react-dom` | `^19` | required |
|
|
612
|
+
| `@mantine/core` | `^9.3` | required |
|
|
613
|
+
| `@mantine/hooks` | `^9.3` | required |
|
|
614
|
+
| `@tanstack/react-query` | — | required for the root `.` entry — `BasaltProvider` hard-requires it at build time; NOT required by `./agent-chat`, which doesn't touch it |
|
|
615
|
+
| `remend` | `1.3.0` | required for both the root `.` entry and `./agent-chat` — both export `ThreadWorkspace`/`ThreadTranscript` (`agent-chat/thread-message.tsx`), which statically imports `content/markdown.tsx`, which imports `remend` eagerly; see [`./content`](#content--prose--markdown) below |
|
|
616
|
+
| `motion` | `12.42.0` | required for both the root `.` entry and `./agent-chat` — both export `ThreadFeed`/`ThreadDetailPanel` (`agent-chat/thread-feed.tsx` / `thread-detail-panel.tsx`), which import `motion/react` eagerly; the root's `ThemeToggle` also uses it |
|
|
617
|
+
|
|
618
|
+
`./agent-chat`'s `optionalPeers` in `llms.txt`/`surfaces.ts` still lists `remend` and `motion` (npm has no per-subpath optionality in `peerDependenciesMeta`), but treat both as required when installing just this subpath — see the subpath table above.
|
|
589
619
|
|
|
590
620
|
Optional peer batteries and their packages are listed per battery above.
|
|
591
621
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: basalt-ui
|
|
3
|
-
description: Streaming-agent layer for basalt-ui apps — Eden-native transport, AgentPart exhaustive handling, StickToBottom, chat history, AND the multi-thread ThreadWorkspace (concurrent runs + a distilled-outcome feed + detail panel). Headless layer in ./agent; the Mantine chrome
|
|
3
|
+
description: Streaming-agent layer for basalt-ui apps — Eden-native transport, AgentPart exhaustive handling, StickToBottom, chat history, AND the multi-thread ThreadWorkspace (concurrent runs + a distilled-outcome feed + detail panel). Headless layer in ./agent (Mantine-free); the Mantine-coupled chrome ships from its own basalt-ui/agent-chat subpath (also re-exported from the root entry) and uses basalt-ui/content's Markdown for rendering. Covers authoring doctrine, the Eden #231 footgun, and the agent-chat lint guards (agent-resume-guard, agent-no-raw-usechat, ai-sdk-major) with their basalt-agent-allow escape token.
|
|
4
4
|
paths:
|
|
5
5
|
- 'src/**/*agent*'
|
|
6
6
|
- 'src/**/chat*'
|
|
@@ -200,7 +200,8 @@ Built on `createPersistedState` — SSR-safe, cross-tab via the `storage` event.
|
|
|
200
200
|
For the "many short chats" pattern — each prompt a short-lived thread, the feed showing only a
|
|
201
201
|
distilled outcome (title + summary), a right-hand detail panel to open and continue — basalt-ui
|
|
202
202
|
ships a full workspace. The headless multi-thread layer lives in `./agent` (Mantine-free); the
|
|
203
|
-
ready-built Mantine chrome ships from
|
|
203
|
+
ready-built Mantine chrome ships from `basalt-ui/agent-chat` (also re-exported from the root
|
|
204
|
+
entry).
|
|
204
205
|
|
|
205
206
|
### Headless (`basalt-ui/agent`)
|
|
206
207
|
|
|
@@ -231,10 +232,16 @@ Promise<AgentOutcome>` — the app supplies it. In production derive `{ title, s
|
|
|
231
232
|
finished run (e.g. a structured final step of your own model) and return it. `heuristicOutcome` is
|
|
232
233
|
a demo-only fallback that truncates the last assistant text — never the production path.
|
|
233
234
|
|
|
234
|
-
### Ready-built UI (`basalt-ui`, Mantine)
|
|
235
|
+
### Ready-built UI (`basalt-ui/agent-chat`, Mantine)
|
|
236
|
+
|
|
237
|
+
The Mantine-coupled chrome (`ThreadWorkspace` and the lower-level pieces below it) ships its own
|
|
238
|
+
subpath, `basalt-ui/agent-chat` — added in 1.10.0. The root `basalt-ui` entry still re-exports the
|
|
239
|
+
same components (nothing that imported from `basalt-ui` before 1.10.0 needs to change), but a
|
|
240
|
+
consumer who only needs the thread-chat UI can import from `./agent-chat` directly, without pulling
|
|
241
|
+
in `BasaltProvider`, `BasaltShell`, the dashboard composites, or `basalt-ui/connectivity`:
|
|
235
242
|
|
|
236
243
|
```tsx
|
|
237
|
-
import { ThreadWorkspace } from 'basalt-ui'
|
|
244
|
+
import { ThreadWorkspace } from 'basalt-ui/agent-chat'
|
|
238
245
|
;<ThreadWorkspace
|
|
239
246
|
useThreads={useThreads}
|
|
240
247
|
transport={transport}
|
|
@@ -251,8 +258,9 @@ composer) on the right, collapsing to a single pane below 768px. The lower-level
|
|
|
251
258
|
on the shared `MOTION_*` tokens and honours `useReducedMotion`.
|
|
252
259
|
|
|
253
260
|
**Boundary:** the headless layer (`createThreadsStore`, `useAgentThreadRuns`, the outcome types)
|
|
254
|
-
stays Mantine-free in `./agent`; the components are Mantine-coupled and ship from
|
|
255
|
-
Never add `@mantine/*` under `src/agent/**` — it is
|
|
261
|
+
stays Mantine-free in `./agent`; the components are Mantine-coupled and ship from `./agent-chat`
|
|
262
|
+
(also re-exported from the root entry). Never add `@mantine/*` under `src/agent/**` — it is
|
|
263
|
+
oxlint-enforced Mantine-free.
|
|
256
264
|
|
|
257
265
|
**No custom part types at the workspace level.** `ThreadWorkspace`, `ThreadDetailPanel`, and
|
|
258
266
|
`ThreadTranscript` render the framework's `AgentPart` union only — they are not generic over a
|
|
@@ -260,6 +268,41 @@ consumer-extended part type. If you need custom rendering for the existing part
|
|
|
260
268
|
the headless layer instead: compose your own transcript from `PartList` (`basalt-ui/agent`) with a
|
|
261
269
|
custom `components` renderer map, rather than passing a wider type through `ThreadWorkspace`.
|
|
262
270
|
|
|
271
|
+
## Lint guards (agent-chat) — a SEPARATE escape token from `theme-allow`
|
|
272
|
+
|
|
273
|
+
Three oxlint rules (1.10.0) protect the streaming/resume discipline above. All three share one
|
|
274
|
+
escape comment, `basalt-agent-allow` — deliberately NOT `theme-allow`. The two tokens are unrelated:
|
|
275
|
+
a `theme-allow` comment on a color/spacing exception never suppresses one of these, and a
|
|
276
|
+
`basalt-agent-allow` comment never suppresses a `theme-allow` finding. Don't reach for the color
|
|
277
|
+
exemption to quiet a streaming guard — mark the correct token, on the correct line, and only when
|
|
278
|
+
you actually own the guard you're bypassing.
|
|
279
|
+
|
|
280
|
+
- **`basalt/agent-resume-guard`** — flags an unguarded `useChat({ resume: true })` or a bare
|
|
281
|
+
`resumeStream()` call outside `useAgentThreadRuns`. `useAgentThreadRuns` owns single-consumer
|
|
282
|
+
discipline and StrictMode-safe reconnection; a raw resume call re-fires on every effect re-run
|
|
283
|
+
(vercel/ai#7891, no merged fix upstream). Mark `basalt-agent-allow` only if you own the guard
|
|
284
|
+
yourself.
|
|
285
|
+
- **`basalt/agent-no-raw-usechat`** — flags importing `useChat`/`useCompletion` directly from
|
|
286
|
+
`@ai-sdk/react` or `ai/react`. Use `useAgentStream`/`useAgentThreadRuns` over `aiSdkTransport`
|
|
287
|
+
instead — they add unmount abort, supersede guards, and single-consumer resume that the raw hook
|
|
288
|
+
doesn't. Type-only imports (`import type { useChat }`) are exempt; nothing to guard against at
|
|
289
|
+
runtime.
|
|
290
|
+
- **`basalt/ai-sdk-major`** — flags a file whose nearest `package.json` declares a different `ai`
|
|
291
|
+
major than the one basalt-ui itself declares as a peer. A producer and consumer on different `ai`
|
|
292
|
+
majors can throw `'Unknown chunk type'` at runtime — this is the exact defect one production
|
|
293
|
+
consumer hit (one app package on `ai@5`, a sibling on `ai@7`). This rule is PER-FILE — it only
|
|
294
|
+
sees the nearest `package.json` to the linted file, so a lint run scoped to one workspace package
|
|
295
|
+
can't catch a skew against a _different_ package. `bunx basalt-ui doctor`'s `ai-major-parity`
|
|
296
|
+
check is the cross-package counterpart: it walks every workspace manifest and is a HARD failure,
|
|
297
|
+
not a lint warning — run both, they check different things. Like its two siblings above, it
|
|
298
|
+
honours `basalt-agent-allow` on the flagged import (or the line above it) — mark it on an
|
|
299
|
+
intentional producer/consumer file. The repo-wide equivalent for `doctor`'s check is
|
|
300
|
+
`basalt.aiMajorSkewReason` in package.json (a mandatory written reason, not a line comment) — see
|
|
301
|
+
the README and the `BasaltConfig` JSDoc.
|
|
302
|
+
|
|
303
|
+
None of these three rules honour `theme-allow` — only `basalt-agent-allow`, on the flagged line or
|
|
304
|
+
the line above it.
|
|
305
|
+
|
|
263
306
|
## Stream resumption — the reconnect seam
|
|
264
307
|
|
|
265
308
|
basalt-ui ships the CLIENT-SIDE contract for resuming a run after a disconnect (reload, network
|
package/configs/oxlint-plugin.js
CHANGED
|
@@ -14,6 +14,19 @@
|
|
|
14
14
|
* immediately above it. The three boundary rules deliberately do NOT — see their own comment for
|
|
15
15
|
* why.
|
|
16
16
|
*
|
|
17
|
+
* ── The two agent-chat guard rules (`agent-resume-guard` / `agent-no-raw-usechat`) + `ai-sdk-major` ──
|
|
18
|
+
* These are also correctness/architecture boundaries, not design guidance, so they do NOT honour
|
|
19
|
+
* `theme-allow`. All three instead honour their own line-comment escape, `basalt-agent-allow`
|
|
20
|
+
* (`hasAgentAllow`, a copy of `hasThemeAllow` with a different needle) — deliberately a separate
|
|
21
|
+
* token, so a color exemption can never double as a license to switch off a streaming guard or an
|
|
22
|
+
* intentional `ai` major skew. `ai-sdk-major`'s escape is line-level and per-import, distinct from
|
|
23
|
+
* `basalt.aiMajorSkewReason` — the repo-wide, mandatory-reason config exemption for the SAME skew
|
|
24
|
+
* read by `doctor`'s `ai-major-parity` check (`src/cli/index.ts`): a lint run only ever sees one
|
|
25
|
+
* file, so there's no reason string to echo here, just a marker on the import that intentionally
|
|
26
|
+
* crosses the declared skew. `ai-sdk-major` is the one rule in this file that touches the
|
|
27
|
+
* filesystem (reads ancestor `package.json`s, memoized per directory so a lint run does one walk
|
|
28
|
+
* per directory, not per file) — every other rule here is pure AST + filename-string matching.
|
|
29
|
+
*
|
|
17
30
|
* ── The three boundary rules (`visx-boundary` / `visx-tooltip` / `token-layer-boundary`) ─────────
|
|
18
31
|
* These used to be one bundled `import-boundary` rule sharing a single on/off toggle — that meant
|
|
19
32
|
* a consumer disabling the one check they disagreed with silently dropped the other two as well.
|
|
@@ -58,6 +71,9 @@
|
|
|
58
71
|
* oxlint's own parser accepts (see `ALLOWED_TOP_LEVEL_KEYS` in that script); do not add ad hoc keys
|
|
59
72
|
* to that file.
|
|
60
73
|
*/
|
|
74
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
75
|
+
import { dirname, resolve as resolvePath } from 'node:path'
|
|
76
|
+
import { fileURLToPath } from 'node:url'
|
|
61
77
|
|
|
62
78
|
// ── Shared helpers ──────────────────────────────────────────────────────────────────────────────
|
|
63
79
|
|
|
@@ -73,6 +89,23 @@ function hasThemeAllow(context, node) {
|
|
|
73
89
|
)
|
|
74
90
|
}
|
|
75
91
|
|
|
92
|
+
/**
|
|
93
|
+
* True when a `basalt-agent-allow` line comment sits on `node`'s own source line or the line above
|
|
94
|
+
* it. A copy of `hasThemeAllow` with a different needle — deliberately NOT the same token, so a
|
|
95
|
+
* color exemption can never double as a license to switch off a streaming guard (see the file-header
|
|
96
|
+
* comment for the two agent-chat rules).
|
|
97
|
+
*/
|
|
98
|
+
function hasAgentAllow(context, node) {
|
|
99
|
+
const sourceCode = context.sourceCode ?? context.getSourceCode?.()
|
|
100
|
+
const comments = sourceCode?.getAllComments?.() ?? []
|
|
101
|
+
const nodeLine = node.loc.start.line
|
|
102
|
+
return comments.some(
|
|
103
|
+
(comment) =>
|
|
104
|
+
comment.value.includes('basalt-agent-allow') &&
|
|
105
|
+
(comment.loc.end.line === nodeLine || comment.loc.end.line === nodeLine - 1),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
76
109
|
function getFilename(context) {
|
|
77
110
|
return context.filename ?? context.getFilename?.() ?? ''
|
|
78
111
|
}
|
|
@@ -523,6 +556,249 @@ const tokenLayerBoundary = {
|
|
|
523
556
|
},
|
|
524
557
|
}
|
|
525
558
|
|
|
559
|
+
// ── Rule 8 — agent-resume-guard ─────────────────────────────────────────────────────────────────
|
|
560
|
+
|
|
561
|
+
const AGENT_RESUME_GUARD_MESSAGE =
|
|
562
|
+
'Unguarded stream resume — useAgentThreadRuns owns single-consumer discipline and ' +
|
|
563
|
+
'StrictMode-safe reconnection; a raw resume re-fires on every effect re-run (vercel/ai#7891, no ' +
|
|
564
|
+
"merged fix). Mark the line 'basalt-agent-allow' if you own the guard. (basalt/agent-resume-guard)"
|
|
565
|
+
|
|
566
|
+
/** True for `resumeStream()` and `<anything>.resumeStream()` call expressions. */
|
|
567
|
+
function isResumeStreamCall(callee) {
|
|
568
|
+
if (callee.type === 'Identifier') return callee.name === 'resumeStream'
|
|
569
|
+
return (
|
|
570
|
+
callee.type === 'MemberExpression' &&
|
|
571
|
+
callee.property.type === 'Identifier' &&
|
|
572
|
+
callee.property.name === 'resumeStream'
|
|
573
|
+
)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** The `key` of an object Property as a string, covering both Identifier and Literal keys. */
|
|
577
|
+
function propertyKeyName(node) {
|
|
578
|
+
const key = node.key
|
|
579
|
+
if (key.type === 'Identifier') return key.name
|
|
580
|
+
if (key.type === 'Literal') return key.value
|
|
581
|
+
return undefined
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Deliberately NO `hasThemeAllow` escape — see basalt/visx-boundary's comment for why these
|
|
585
|
+
// guards use their own token (`basalt-agent-allow`, see the file-header comment) instead.
|
|
586
|
+
const agentResumeGuard = {
|
|
587
|
+
meta: {
|
|
588
|
+
type: 'problem',
|
|
589
|
+
docs: {
|
|
590
|
+
description:
|
|
591
|
+
'Disallow unguarded useChat({ resume: true }) / resumeStream() calls outside useAgentThreadRuns.',
|
|
592
|
+
},
|
|
593
|
+
schema: [],
|
|
594
|
+
},
|
|
595
|
+
create(context) {
|
|
596
|
+
return {
|
|
597
|
+
CallExpression(node) {
|
|
598
|
+
const callee = node.callee
|
|
599
|
+
|
|
600
|
+
if (callee.type === 'Identifier' && callee.name === 'useChat') {
|
|
601
|
+
const arg0 = node.arguments[0]
|
|
602
|
+
if (arg0 === undefined || arg0.type !== 'ObjectExpression') return
|
|
603
|
+
for (const prop of arg0.properties) {
|
|
604
|
+
if (prop.type !== 'Property') continue
|
|
605
|
+
if (propertyKeyName(prop) !== 'resume') continue
|
|
606
|
+
if (prop.value.type !== 'Literal' || prop.value.value !== true) continue
|
|
607
|
+
if (hasAgentAllow(context, prop)) continue
|
|
608
|
+
context.report({ node: prop, message: AGENT_RESUME_GUARD_MESSAGE })
|
|
609
|
+
}
|
|
610
|
+
return
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (!isResumeStreamCall(callee)) return
|
|
614
|
+
if (hasAgentAllow(context, node)) return
|
|
615
|
+
context.report({ node, message: AGENT_RESUME_GUARD_MESSAGE })
|
|
616
|
+
},
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// ── Rule 9 — agent-no-raw-usechat ───────────────────────────────────────────────────────────────
|
|
622
|
+
|
|
623
|
+
const AGENT_NO_RAW_USECHAT_MESSAGE =
|
|
624
|
+
'Raw @ai-sdk/react useChat — use useAgentStream / useAgentThreadRuns over aiSdkTransport ' +
|
|
625
|
+
"(unmount abort, supersede guards, single-consumer resume). Mark the line 'basalt-agent-allow' to " +
|
|
626
|
+
'opt out. (basalt/agent-no-raw-usechat)'
|
|
627
|
+
|
|
628
|
+
const RAW_USE_CHAT_SOURCES = new Set(['@ai-sdk/react', 'ai/react'])
|
|
629
|
+
const RAW_USE_CHAT_NAMES = new Set(['useChat', 'useCompletion'])
|
|
630
|
+
|
|
631
|
+
// Deliberately NO `hasThemeAllow` escape — see the file-header comment.
|
|
632
|
+
const agentNoRawUseChat = {
|
|
633
|
+
meta: {
|
|
634
|
+
type: 'problem',
|
|
635
|
+
docs: {
|
|
636
|
+
description:
|
|
637
|
+
'Disallow importing useChat/useCompletion directly from @ai-sdk/react or ai/react.',
|
|
638
|
+
},
|
|
639
|
+
schema: [],
|
|
640
|
+
},
|
|
641
|
+
create(context) {
|
|
642
|
+
return {
|
|
643
|
+
ImportDeclaration(node) {
|
|
644
|
+
const source = node.source?.value
|
|
645
|
+
if (typeof source !== 'string' || !RAW_USE_CHAT_SOURCES.has(source)) return
|
|
646
|
+
// A whole `import type { … } from '…'` is erased at compile time — nothing to guard.
|
|
647
|
+
if (node.importKind === 'type') return
|
|
648
|
+
|
|
649
|
+
for (const specifier of node.specifiers) {
|
|
650
|
+
if (specifier.type !== 'ImportSpecifier') continue
|
|
651
|
+
// `import { type useChat } from '…'` — the individual specifier is type-only.
|
|
652
|
+
if (specifier.importKind === 'type') continue
|
|
653
|
+
const imported = specifier.imported
|
|
654
|
+
const importedName = imported.type === 'Identifier' ? imported.name : imported.value
|
|
655
|
+
if (typeof importedName !== 'string' || !RAW_USE_CHAT_NAMES.has(importedName)) continue
|
|
656
|
+
if (hasAgentAllow(context, specifier)) continue
|
|
657
|
+
context.report({ node: specifier, message: AGENT_NO_RAW_USECHAT_MESSAGE })
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// ── Rule 10 — ai-sdk-major ──────────────────────────────────────────────────────────────────────
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* First run of digits in a semver range string (`^7.0.15` → `7`), or null if there is none.
|
|
668
|
+
*
|
|
669
|
+
* Duplicated verbatim in `src/cli/index.ts` — kept separate deliberately: this file must stay
|
|
670
|
+
* import-free from the package (it loads via `jsPlugins` out of a consumer's node_modules), so it
|
|
671
|
+
* cannot import the CLI's copy. Keep both copies in sync by hand; a future parsing fix (pre-release
|
|
672
|
+
* suffixes, say) must land in both.
|
|
673
|
+
*/
|
|
674
|
+
function majorOf(range) {
|
|
675
|
+
if (typeof range !== 'string') return null
|
|
676
|
+
const match = range.match(/\d+/)
|
|
677
|
+
return match === null ? null : Number(match[0])
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* `ai`'s declared major off one parsed package.json, checked in ONE fixed order shared by every
|
|
682
|
+
* reader in this file (basalt's own major below, and the linted file's nearest package.json in
|
|
683
|
+
* `aiMajorAtPackageJson`) — `dependencies` → `devDependencies` → `peerDependencies`. Chosen
|
|
684
|
+
* deliberately: `dependencies` is what actually gets installed and resolved at runtime, so it wins
|
|
685
|
+
* over a `devDependencies` pin (test-only) or a `peerDependencies` range (a floor, not a pin). Two
|
|
686
|
+
* readers disagreeing on this order is exactly the kind of skew this rule exists to catch — see
|
|
687
|
+
* `cli/index.ts`'s `aiMajorAt`, which uses the same order for doctor's cross-package walk.
|
|
688
|
+
*/
|
|
689
|
+
function aiMajorFromPkg(pkg) {
|
|
690
|
+
return majorOf(pkg.dependencies?.ai ?? pkg.devDependencies?.ai ?? pkg.peerDependencies?.ai)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* basalt-ui's OWN declared `ai` peer major, read once at module load from the package.json
|
|
695
|
+
* sitting next to this file's `configs/` directory — `../package.json` relative to this module,
|
|
696
|
+
* which resolves to `packages/basalt-ui/package.json` repo-locally AND to
|
|
697
|
+
* `node_modules/basalt-ui/package.json` when this file runs from a consumer's installed copy
|
|
698
|
+
* (`configs/oxlint-plugin.js` is one directory below the package root in both places). `null` when
|
|
699
|
+
* unreadable or undeclared — the rule then has nothing to compare against and stays silent.
|
|
700
|
+
*/
|
|
701
|
+
const PLUGIN_DIR = dirname(fileURLToPath(import.meta.url))
|
|
702
|
+
const BASALT_AI_MAJOR = (() => {
|
|
703
|
+
try {
|
|
704
|
+
const pkg = JSON.parse(readFileSync(resolvePath(PLUGIN_DIR, '..', 'package.json'), 'utf8'))
|
|
705
|
+
// Peer-first, unlike aiMajorFromPkg's dependencies-first order: `ai` is an optional PEER of
|
|
706
|
+
// basalt-ui, never its runtime dependency, so the "dependencies wins because runtime" rationale
|
|
707
|
+
// above does not apply to this self-read — a devDependencies test pin must not masquerade as
|
|
708
|
+
// the declared peer major.
|
|
709
|
+
return majorOf(pkg.peerDependencies?.ai ?? pkg.devDependencies?.ai ?? pkg.dependencies?.ai)
|
|
710
|
+
} catch {
|
|
711
|
+
return null
|
|
712
|
+
}
|
|
713
|
+
})()
|
|
714
|
+
|
|
715
|
+
// One JSON.parse per package.json path, and one directory-walk result per starting directory — so
|
|
716
|
+
// a lint run over N files in the same package does one filesystem walk total, not N.
|
|
717
|
+
const packageJsonMajorCache = new Map()
|
|
718
|
+
const nearestAiMajorCache = new Map()
|
|
719
|
+
|
|
720
|
+
function aiMajorAtPackageJson(pkgPath) {
|
|
721
|
+
if (packageJsonMajorCache.has(pkgPath)) return packageJsonMajorCache.get(pkgPath)
|
|
722
|
+
let major = null
|
|
723
|
+
try {
|
|
724
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
725
|
+
major = aiMajorFromPkg(pkg)
|
|
726
|
+
} catch {
|
|
727
|
+
major = null
|
|
728
|
+
}
|
|
729
|
+
packageJsonMajorCache.set(pkgPath, major)
|
|
730
|
+
return major
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/** Walks up from `startDir` to the nearest ancestor `package.json`'s declared `ai` major. */
|
|
734
|
+
function nearestAiMajor(startDir) {
|
|
735
|
+
if (nearestAiMajorCache.has(startDir)) return nearestAiMajorCache.get(startDir)
|
|
736
|
+
|
|
737
|
+
const visited = []
|
|
738
|
+
let dir = startDir
|
|
739
|
+
let result = null
|
|
740
|
+
for (;;) {
|
|
741
|
+
if (nearestAiMajorCache.has(dir)) {
|
|
742
|
+
result = nearestAiMajorCache.get(dir)
|
|
743
|
+
break
|
|
744
|
+
}
|
|
745
|
+
visited.push(dir)
|
|
746
|
+
const pkgPath = resolvePath(dir, 'package.json')
|
|
747
|
+
if (existsSync(pkgPath)) {
|
|
748
|
+
result = aiMajorAtPackageJson(pkgPath)
|
|
749
|
+
break
|
|
750
|
+
}
|
|
751
|
+
const parent = dirname(dir)
|
|
752
|
+
if (parent === dir) break // filesystem root — no package.json found
|
|
753
|
+
dir = parent
|
|
754
|
+
}
|
|
755
|
+
for (const d of visited) nearestAiMajorCache.set(d, result)
|
|
756
|
+
return result
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function aiSdkMajorMessage(consumerMajor) {
|
|
760
|
+
return (
|
|
761
|
+
`This file's nearest package.json declares ai@${consumerMajor}, but basalt-ui declares the ` +
|
|
762
|
+
`ai@${BASALT_AI_MAJOR} peer major — a producer/consumer pair on different ai majors can throw ` +
|
|
763
|
+
"'Unknown chunk type' at runtime (argo defect 1: apps/api on ai@5 vs apps/dashboard on ai@7). " +
|
|
764
|
+
'(basalt/ai-sdk-major)'
|
|
765
|
+
)
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Honours `basalt-agent-allow` (NOT `theme-allow`) like its two agent-chat siblings above — a
|
|
769
|
+
// version skew is still a fact about two package.json files, but a written, intentional
|
|
770
|
+
// producer/consumer pair (see `doctor`'s `aiMajorSkewReason`) needs a way to say so at the import
|
|
771
|
+
// site too, not just be permanently blocked.
|
|
772
|
+
const aiSdkMajor = {
|
|
773
|
+
meta: {
|
|
774
|
+
type: 'problem',
|
|
775
|
+
docs: {
|
|
776
|
+
description:
|
|
777
|
+
"Disallow importing 'ai'/'@ai-sdk/*' from a package whose declared ai major differs from basalt-ui's.",
|
|
778
|
+
},
|
|
779
|
+
schema: [],
|
|
780
|
+
},
|
|
781
|
+
create(context) {
|
|
782
|
+
return {
|
|
783
|
+
ImportDeclaration(node) {
|
|
784
|
+
if (BASALT_AI_MAJOR === null) return
|
|
785
|
+
const source = node.source?.value
|
|
786
|
+
if (typeof source !== 'string') return
|
|
787
|
+
if (source !== 'ai' && !source.startsWith('@ai-sdk/')) return
|
|
788
|
+
|
|
789
|
+
const filename = getFilename(context)
|
|
790
|
+
if (filename.length === 0) return
|
|
791
|
+
const consumerMajor = nearestAiMajor(dirname(filename))
|
|
792
|
+
if (consumerMajor === null) return // no `ai` in the nearest package.json — nothing to compare
|
|
793
|
+
if (consumerMajor === BASALT_AI_MAJOR) return
|
|
794
|
+
if (hasAgentAllow(context, node)) return
|
|
795
|
+
|
|
796
|
+
context.report({ node, message: aiSdkMajorMessage(consumerMajor) })
|
|
797
|
+
},
|
|
798
|
+
}
|
|
799
|
+
},
|
|
800
|
+
}
|
|
801
|
+
|
|
526
802
|
// ── Plugin export ───────────────────────────────────────────────────────────────────────────────
|
|
527
803
|
|
|
528
804
|
export default {
|
|
@@ -536,5 +812,8 @@ export default {
|
|
|
536
812
|
'visx-boundary': visxBoundary,
|
|
537
813
|
'visx-tooltip': visxTooltip,
|
|
538
814
|
'token-layer-boundary': tokenLayerBoundary,
|
|
815
|
+
'agent-resume-guard': agentResumeGuard,
|
|
816
|
+
'agent-no-raw-usechat': agentNoRawUseChat,
|
|
817
|
+
'ai-sdk-major': aiSdkMajor,
|
|
539
818
|
},
|
|
540
819
|
}
|