@assistant-ui/mcp-docs-server 0.1.17 → 0.1.19
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/.docs/organized/code-examples/with-ag-ui.md +518 -234
- package/.docs/organized/code-examples/{with-ai-sdk-v5.md → with-ai-sdk-v6.md} +476 -189
- package/.docs/organized/code-examples/with-assistant-transport.md +503 -301
- package/.docs/organized/code-examples/with-cloud.md +524 -226
- package/.docs/organized/code-examples/with-custom-thread-list.md +433 -146
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +2241 -0
- package/.docs/organized/code-examples/with-external-store.md +517 -231
- package/.docs/organized/code-examples/with-ffmpeg.md +500 -220
- package/.docs/organized/code-examples/with-langgraph.md +630 -319
- package/.docs/organized/code-examples/with-parent-id-grouping.md +517 -231
- package/.docs/organized/code-examples/with-react-hook-form.md +517 -233
- package/.docs/organized/code-examples/with-react-router.md +2167 -0
- package/.docs/organized/code-examples/{store-example.md → with-store.md} +18 -22
- package/.docs/organized/code-examples/with-tanstack.md +23 -41
- package/.docs/raw/blog/2025-01-31-changelog/index.mdx +0 -2
- package/.docs/raw/docs/{about-assistantui.mdx → (docs)/about-assistantui.mdx} +2 -1
- package/.docs/raw/docs/{architecture.mdx → (docs)/architecture.mdx} +3 -2
- package/.docs/raw/docs/{cli.mdx → (docs)/cli.mdx} +1 -19
- package/.docs/raw/docs/{copilots → (docs)/copilots}/make-assistant-readable.mdx +1 -0
- package/.docs/raw/docs/{copilots → (docs)/copilots}/make-assistant-tool-ui.mdx +2 -1
- package/.docs/raw/docs/{copilots → (docs)/copilots}/make-assistant-tool.mdx +2 -1
- package/.docs/raw/docs/{copilots → (docs)/copilots}/model-context.mdx +1 -0
- package/.docs/raw/docs/{copilots → (docs)/copilots}/motivation.mdx +1 -0
- package/.docs/raw/docs/{copilots → (docs)/copilots}/use-assistant-instructions.mdx +1 -0
- package/.docs/raw/docs/{devtools.mdx → (docs)/devtools.mdx} +4 -4
- package/.docs/raw/docs/{guides/Attachments.mdx → (docs)/guides/attachments.mdx} +4 -5
- package/.docs/raw/docs/{guides/Branching.mdx → (docs)/guides/branching.mdx} +2 -1
- package/.docs/raw/docs/{guides → (docs)/guides}/context-api.mdx +1 -0
- package/.docs/raw/docs/(docs)/guides/dictation.mdx +370 -0
- package/.docs/raw/docs/{guides/Editing.mdx → (docs)/guides/editing.mdx} +1 -0
- package/.docs/raw/docs/{guides/Latex.mdx → (docs)/guides/latex.mdx} +1 -2
- package/.docs/raw/docs/{guides/Speech.mdx → (docs)/guides/speech.mdx} +9 -10
- package/.docs/raw/docs/{guides/ToolUI.mdx → (docs)/guides/tool-ui.mdx} +15 -14
- package/.docs/raw/docs/{guides/Tools.mdx → (docs)/guides/tools.mdx} +10 -7
- package/.docs/raw/docs/{getting-started.mdx → (docs)/index.mdx} +17 -22
- package/.docs/raw/docs/{mcp-docs-server.mdx → (docs)/mcp-docs-server.mdx} +1 -2
- package/.docs/raw/docs/{api-reference/context-providers/AssistantRuntimeProvider.mdx → (reference)/api-reference/context-providers/assistant-runtime-provider.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/context-providers/TextMessagePartProvider.mdx → (reference)/api-reference/context-providers/text-message-part-provider.mdx} +2 -1
- package/.docs/raw/docs/{api-reference → (reference)/api-reference}/integrations/react-data-stream.mdx +2 -1
- package/.docs/raw/docs/{api-reference → (reference)/api-reference}/integrations/react-hook-form.mdx +2 -1
- package/.docs/raw/docs/{api-reference → (reference)/api-reference}/integrations/vercel-ai-sdk.mdx +2 -2
- package/.docs/raw/docs/{api-reference → (reference)/api-reference}/overview.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +327 -0
- package/.docs/raw/docs/{api-reference/primitives/ActionBar.mdx → (reference)/api-reference/primitives/action-bar.mdx} +3 -1
- package/.docs/raw/docs/{api-reference/primitives/AssistantIf.mdx → (reference)/api-reference/primitives/assistant-if.mdx} +2 -2
- package/.docs/raw/docs/{api-reference/primitives/AssistantModal.mdx → (reference)/api-reference/primitives/assistant-modal.mdx} +3 -1
- package/.docs/raw/docs/{api-reference/primitives/Attachment.mdx → (reference)/api-reference/primitives/attachment.mdx} +3 -2
- package/.docs/raw/docs/{api-reference/primitives/BranchPicker.mdx → (reference)/api-reference/primitives/branch-picker.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/primitives/Composer.mdx → (reference)/api-reference/primitives/composer.mdx} +101 -2
- package/.docs/raw/docs/{api-reference → (reference)/api-reference}/primitives/composition.mdx +1 -0
- package/.docs/raw/docs/{api-reference/primitives/Error.mdx → (reference)/api-reference/primitives/error.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/primitives/MessagePart.mdx → (reference)/api-reference/primitives/message-part.mdx} +2 -2
- package/.docs/raw/docs/{api-reference/primitives/Message.mdx → (reference)/api-reference/primitives/message.mdx} +2 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +221 -0
- package/.docs/raw/docs/{api-reference/primitives/ThreadListItem.mdx → (reference)/api-reference/primitives/thread-list-item.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/primitives/ThreadList.mdx → (reference)/api-reference/primitives/thread-list.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/primitives/Thread.mdx → (reference)/api-reference/primitives/thread.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/runtimes/AssistantRuntime.mdx → (reference)/api-reference/runtimes/assistant-runtime.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/runtimes/AttachmentRuntime.mdx → (reference)/api-reference/runtimes/attachment-runtime.mdx} +3 -2
- package/.docs/raw/docs/{api-reference/runtimes/ComposerRuntime.mdx → (reference)/api-reference/runtimes/composer-runtime.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/runtimes/MessagePartRuntime.mdx → (reference)/api-reference/runtimes/message-part-runtime.mdx} +3 -2
- package/.docs/raw/docs/{api-reference/runtimes/MessageRuntime.mdx → (reference)/api-reference/runtimes/message-runtime.mdx} +3 -2
- package/.docs/raw/docs/{api-reference/runtimes/ThreadListItemRuntime.mdx → (reference)/api-reference/runtimes/thread-list-item-runtime.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/runtimes/ThreadListRuntime.mdx → (reference)/api-reference/runtimes/thread-list-runtime.mdx} +2 -1
- package/.docs/raw/docs/{api-reference/runtimes/ThreadRuntime.mdx → (reference)/api-reference/runtimes/thread-runtime.mdx} +3 -5
- package/.docs/raw/docs/{legacy/styled/AssistantModal.mdx → (reference)/legacy/styled/assistant-modal.mdx} +2 -3
- package/.docs/raw/docs/{legacy/styled/Decomposition.mdx → (reference)/legacy/styled/decomposition.mdx} +1 -0
- package/.docs/raw/docs/{legacy/styled/Markdown.mdx → (reference)/legacy/styled/markdown.mdx} +2 -4
- package/.docs/raw/docs/{legacy/styled/Scrollbar.mdx → (reference)/legacy/styled/scrollbar.mdx} +2 -1
- package/.docs/raw/docs/{legacy/styled/ThreadWidth.mdx → (reference)/legacy/styled/thread-width.mdx} +1 -0
- package/.docs/raw/docs/{legacy/styled/Thread.mdx → (reference)/legacy/styled/thread.mdx} +2 -3
- package/.docs/raw/docs/{migrations → (reference)/migrations}/deprecation-policy.mdx +1 -0
- package/.docs/raw/docs/{migrations → (reference)/migrations}/react-langgraph-v0-7.mdx +1 -2
- package/.docs/raw/docs/{migrations → (reference)/migrations}/v0-11.mdx +1 -0
- package/.docs/raw/docs/{migrations → (reference)/migrations}/v0-12.mdx +1 -0
- package/.docs/raw/docs/{react-compatibility.mdx → (reference)/react-compatibility.mdx} +2 -3
- package/.docs/raw/docs/cloud/authorization.mdx +1 -0
- package/.docs/raw/docs/cloud/overview.mdx +1 -0
- package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +2 -3
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +5 -7
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +9 -8
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +2 -3
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +7 -6
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +38 -3
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +6 -8
- package/.docs/raw/docs/runtimes/custom/local.mdx +43 -16
- package/.docs/raw/docs/runtimes/data-stream.mdx +32 -4
- package/.docs/raw/docs/runtimes/helicone.mdx +1 -0
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +3 -3
- package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +1 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +1 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +1 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +2 -1
- package/.docs/raw/docs/runtimes/langserve.mdx +2 -2
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +4 -5
- package/.docs/raw/docs/runtimes/mastra/overview.mdx +1 -0
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +3 -4
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +2 -4
- package/.docs/raw/docs/ui/assistant-modal.mdx +163 -0
- package/.docs/raw/docs/ui/assistant-sidebar.mdx +90 -0
- package/.docs/raw/docs/ui/attachment.mdx +227 -0
- package/.docs/raw/docs/ui/{Markdown.mdx → markdown.mdx} +11 -6
- package/.docs/raw/docs/ui/{Mermaid.mdx → mermaid.mdx} +12 -5
- package/.docs/raw/docs/ui/{PartGrouping.mdx → part-grouping.mdx} +4 -6
- package/.docs/raw/docs/ui/reasoning.mdx +148 -0
- package/.docs/raw/docs/ui/{Scrollbar.mdx → scrollbar.mdx} +9 -7
- package/.docs/raw/docs/ui/sources.mdx +87 -0
- package/.docs/raw/docs/ui/{SyntaxHighlighting.mdx → syntax-highlighting.mdx} +9 -5
- package/.docs/raw/docs/ui/thread-list.mdx +275 -0
- package/.docs/raw/docs/ui/{Thread.mdx → thread.mdx} +5 -6
- package/.docs/raw/docs/ui/tool-fallback.mdx +112 -0
- package/.docs/raw/docs/ui/tool-group.mdx +214 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -0
- package/dist/prepare-docs/code-examples.d.ts +2 -0
- package/dist/prepare-docs/code-examples.d.ts.map +1 -0
- package/dist/prepare-docs/code-examples.js +129 -0
- package/dist/prepare-docs/code-examples.js.map +1 -0
- package/dist/prepare-docs/copy-raw.d.ts +2 -0
- package/dist/prepare-docs/copy-raw.d.ts.map +1 -0
- package/dist/prepare-docs/copy-raw.js +50 -0
- package/dist/prepare-docs/copy-raw.js.map +1 -0
- package/dist/prepare-docs/prepare.d.ts +2 -0
- package/dist/prepare-docs/prepare.d.ts.map +1 -0
- package/dist/prepare-docs/prepare.js +18 -195
- package/dist/prepare-docs/prepare.js.map +1 -0
- package/dist/stdio.d.ts +3 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +4 -5
- package/dist/stdio.js.map +1 -0
- package/dist/tools/docs.d.ts +23 -0
- package/dist/tools/docs.d.ts.map +1 -0
- package/dist/tools/docs.js +168 -0
- package/dist/tools/docs.js.map +1 -0
- package/dist/tools/examples.d.ts +23 -0
- package/dist/tools/examples.d.ts.map +1 -0
- package/dist/tools/examples.js +95 -0
- package/dist/tools/examples.js.map +1 -0
- package/dist/tools/tests/test-setup.d.ts +4 -0
- package/dist/tools/tests/test-setup.d.ts.map +1 -0
- package/dist/tools/tests/test-setup.js +36 -0
- package/dist/tools/tests/test-setup.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +20 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mcp-format.d.ts +7 -0
- package/dist/utils/mcp-format.d.ts.map +1 -0
- package/dist/utils/mcp-format.js +11 -0
- package/dist/utils/mcp-format.js.map +1 -0
- package/dist/utils/mdx.d.ts +9 -0
- package/dist/utils/mdx.d.ts.map +1 -0
- package/dist/utils/mdx.js +27 -0
- package/dist/utils/mdx.js.map +1 -0
- package/dist/utils/paths.d.ts +8 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +84 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/security.d.ts +2 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +43 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +37 -19
- package/src/constants.ts +22 -0
- package/src/index.ts +51 -0
- package/src/prepare-docs/code-examples.ts +158 -0
- package/src/prepare-docs/copy-raw.ts +55 -0
- package/src/prepare-docs/prepare.ts +24 -0
- package/src/stdio.ts +7 -0
- package/src/tools/docs.ts +207 -0
- package/src/tools/examples.ts +107 -0
- package/src/tools/tests/docs.test.ts +124 -0
- package/src/tools/tests/examples.test.ts +94 -0
- package/src/tools/tests/integration.test.ts +46 -0
- package/src/tools/tests/json-parsing.test.ts +23 -0
- package/src/tools/tests/mcp-protocol.test.ts +133 -0
- package/src/tools/tests/path-traversal.test.ts +81 -0
- package/src/tools/tests/test-setup.ts +40 -0
- package/src/utils/logger.ts +20 -0
- package/src/utils/mcp-format.ts +12 -0
- package/src/utils/mdx.ts +39 -0
- package/src/utils/paths.ts +114 -0
- package/src/utils/security.ts +52 -0
- package/src/utils/tests/security.test.ts +119 -0
- package/.docs/raw/docs/index.mdx +0 -7
- package/.docs/raw/docs/ui/AssistantModal.mdx +0 -45
- package/.docs/raw/docs/ui/AssistantSidebar.mdx +0 -41
- package/.docs/raw/docs/ui/Attachment.mdx +0 -84
- package/.docs/raw/docs/ui/Reasoning.mdx +0 -152
- package/.docs/raw/docs/ui/ThreadList.mdx +0 -90
- package/.docs/raw/docs/ui/ToolFallback.mdx +0 -63
- package/.docs/raw/docs/ui/ToolGroup.mdx +0 -96
- package/dist/chunk-M2RKUM66.js +0 -38
- package/dist/chunk-NVNFQ5ZO.js +0 -423
- /package/.docs/raw/docs/{copilots → (docs)/copilots}/assistant-frame.mdx +0 -0
package/src/utils/mdx.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import matter from "gray-matter";
|
|
3
|
+
import { logger } from "./logger.js";
|
|
4
|
+
|
|
5
|
+
interface MDXContent {
|
|
6
|
+
content: string;
|
|
7
|
+
frontmatter: Record<string, any>;
|
|
8
|
+
excerpt?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function readMDXFile(
|
|
12
|
+
filePath: string,
|
|
13
|
+
): Promise<MDXContent | null> {
|
|
14
|
+
try {
|
|
15
|
+
const fileContent = await readFile(filePath, "utf-8");
|
|
16
|
+
const { content, data } = matter(fileContent);
|
|
17
|
+
|
|
18
|
+
const excerptMatch = content.match(/^(.+?)(?:\n\n|$)/);
|
|
19
|
+
const excerpt =
|
|
20
|
+
excerptMatch?.[1] !== undefined
|
|
21
|
+
? excerptMatch[1].replace(/^#+ /, "")
|
|
22
|
+
: undefined;
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
content,
|
|
26
|
+
frontmatter: data,
|
|
27
|
+
...(excerpt !== undefined && { excerpt }),
|
|
28
|
+
};
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error(`Failed to read MDX file: ${filePath}`, error);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function formatMDXContent(mdxContent: MDXContent): string {
|
|
36
|
+
const { content, frontmatter } = mdxContent;
|
|
37
|
+
|
|
38
|
+
return matter.stringify(content, frontmatter);
|
|
39
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join, extname } from "node:path";
|
|
3
|
+
import { DOCS_PATH, MDX_EXTENSION, MD_EXTENSION } from "../constants.js";
|
|
4
|
+
import { logger } from "./logger.js";
|
|
5
|
+
|
|
6
|
+
const SIMILARITY_THRESHOLDS = {
|
|
7
|
+
EXACT_MATCH: 1,
|
|
8
|
+
CONTAINS_MATCH: 0.8,
|
|
9
|
+
PARTIAL_MATCH: 0.5,
|
|
10
|
+
MIN_SUGGESTION: 0.3,
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
export async function listDirContents(dirPath: string): Promise<{
|
|
14
|
+
directories: string[];
|
|
15
|
+
files: string[];
|
|
16
|
+
}> {
|
|
17
|
+
try {
|
|
18
|
+
const items = await readdir(dirPath, { withFileTypes: true });
|
|
19
|
+
|
|
20
|
+
const directories = items
|
|
21
|
+
.filter((item) => item.isDirectory())
|
|
22
|
+
.map((item) => item.name)
|
|
23
|
+
.filter((name) => !name.startsWith("."))
|
|
24
|
+
.sort();
|
|
25
|
+
|
|
26
|
+
const files = items
|
|
27
|
+
.filter(
|
|
28
|
+
(item) =>
|
|
29
|
+
item.isFile() &&
|
|
30
|
+
(extname(item.name) === MDX_EXTENSION ||
|
|
31
|
+
extname(item.name) === MD_EXTENSION),
|
|
32
|
+
)
|
|
33
|
+
.map((item) => item.name)
|
|
34
|
+
.sort();
|
|
35
|
+
|
|
36
|
+
return { directories, files };
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.error(`Failed to list directory contents: ${dirPath}`, error);
|
|
39
|
+
return { directories: [], files: [] };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function getAvailablePaths(): Promise<string[]> {
|
|
44
|
+
const paths: string[] = [];
|
|
45
|
+
|
|
46
|
+
async function scanDirectory(
|
|
47
|
+
dir: string,
|
|
48
|
+
prefix: string = "",
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
const { directories, files } = await listDirContents(dir);
|
|
51
|
+
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
const name = file.replace(MDX_EXTENSION, "").replace(MD_EXTENSION, "");
|
|
54
|
+
paths.push(prefix ? `${prefix}/${name}` : name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const subdir of directories) {
|
|
58
|
+
const subdirPath = join(dir, subdir);
|
|
59
|
+
const newPrefix = prefix ? `${prefix}/${subdir}` : subdir;
|
|
60
|
+
paths.push(newPrefix);
|
|
61
|
+
await scanDirectory(subdirPath, newPrefix);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await scanDirectory(DOCS_PATH);
|
|
66
|
+
return paths.sort();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function findNearestPaths(
|
|
70
|
+
requestedPath: string,
|
|
71
|
+
availablePaths: string[],
|
|
72
|
+
): string[] {
|
|
73
|
+
const normalizedRequest = requestedPath
|
|
74
|
+
.toLowerCase()
|
|
75
|
+
.replace(/[^a-z0-9]/g, "");
|
|
76
|
+
|
|
77
|
+
const scored = availablePaths.map((path) => {
|
|
78
|
+
const normalizedPath = path.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
79
|
+
|
|
80
|
+
if (normalizedPath.includes(normalizedRequest)) {
|
|
81
|
+
return { path, score: SIMILARITY_THRESHOLDS.EXACT_MATCH };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (normalizedRequest.includes(normalizedPath)) {
|
|
85
|
+
return { path, score: SIMILARITY_THRESHOLDS.CONTAINS_MATCH };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const overlap = [...normalizedRequest].filter((char) =>
|
|
89
|
+
normalizedPath.includes(char),
|
|
90
|
+
).length;
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
path,
|
|
94
|
+
score:
|
|
95
|
+
(overlap / normalizedRequest.length) *
|
|
96
|
+
SIMILARITY_THRESHOLDS.PARTIAL_MATCH,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return scored
|
|
101
|
+
.filter((item) => item.score > SIMILARITY_THRESHOLDS.MIN_SUGGESTION)
|
|
102
|
+
.sort((a, b) => b.score - a.score)
|
|
103
|
+
.slice(0, 3)
|
|
104
|
+
.map((item) => item.path);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function pathExists(path: string): Promise<boolean> {
|
|
108
|
+
try {
|
|
109
|
+
await stat(path);
|
|
110
|
+
return true;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export function sanitizePath(userPath: string): string {
|
|
4
|
+
if (!userPath || typeof userPath !== "string") {
|
|
5
|
+
throw new Error("Invalid path: Path must be a non-empty string");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Check for traversal patterns before normalization - defense in depth
|
|
9
|
+
if (userPath.includes("../") || userPath.includes("..\\")) {
|
|
10
|
+
throw new Error("Invalid path: Directory traversal attempt detected");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const normalized = path.normalize(userPath);
|
|
14
|
+
|
|
15
|
+
if (path.isAbsolute(normalized)) {
|
|
16
|
+
throw new Error("Invalid path: Absolute paths are not allowed");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const relativePath = path.relative("", normalized);
|
|
20
|
+
|
|
21
|
+
if (relativePath.startsWith("..")) {
|
|
22
|
+
throw new Error("Invalid path: Directory traversal attempt detected");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (relativePath.includes("..")) {
|
|
26
|
+
throw new Error("Invalid path: Path contains invalid traversal sequences");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (relativePath.includes("\0")) {
|
|
30
|
+
throw new Error("Invalid path: Path contains null bytes");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (process.platform !== "win32") {
|
|
34
|
+
if (normalized.includes("\\")) {
|
|
35
|
+
throw new Error("Invalid path: Backslashes not allowed");
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
if (normalized.includes(":") || normalized.startsWith("\\\\")) {
|
|
39
|
+
throw new Error("Invalid path: Path contains invalid Windows characters");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const segments = relativePath.split(path.sep);
|
|
44
|
+
for (const segment of segments) {
|
|
45
|
+
if (segment.startsWith(".") && segment !== ".") {
|
|
46
|
+
throw new Error("Invalid path: Hidden files are not allowed");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Always return Unix-style paths for consistency across platforms
|
|
51
|
+
return relativePath.replace(/\\/g, "/");
|
|
52
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { sanitizePath } from "../security.js";
|
|
3
|
+
|
|
4
|
+
describe("sanitizePath", () => {
|
|
5
|
+
describe("should reject directory traversal attempts", () => {
|
|
6
|
+
const maliciousPaths = [
|
|
7
|
+
"../../../etc/passwd",
|
|
8
|
+
"..\\..\\windows\\system32",
|
|
9
|
+
"/etc/passwd",
|
|
10
|
+
"/absolute/path",
|
|
11
|
+
"docs/../../../etc/passwd",
|
|
12
|
+
"valid/../../../../../../etc/passwd",
|
|
13
|
+
"../",
|
|
14
|
+
"..",
|
|
15
|
+
".../.../",
|
|
16
|
+
"foo/../../bar",
|
|
17
|
+
"./../../etc/passwd",
|
|
18
|
+
"path/with/../../../traversal",
|
|
19
|
+
"path/to/\0/null",
|
|
20
|
+
"path\\to\\..\\..\\file",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
if (process.platform === "win32") {
|
|
24
|
+
maliciousPaths.push("C:\\Windows\\System32", "\\\\server\\share");
|
|
25
|
+
} else {
|
|
26
|
+
maliciousPaths.push("C:\\Windows\\System32", "\\\\server\\share");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
maliciousPaths.forEach((path) => {
|
|
30
|
+
it(`should reject: ${path}`, () => {
|
|
31
|
+
expect(() => sanitizePath(path)).toThrow();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("should reject invalid inputs", () => {
|
|
37
|
+
it("should reject empty string", () => {
|
|
38
|
+
expect(() => sanitizePath("")).toThrow(
|
|
39
|
+
"Invalid path: Path must be a non-empty string",
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should reject null", () => {
|
|
44
|
+
expect(() => sanitizePath(null as any)).toThrow(
|
|
45
|
+
"Invalid path: Path must be a non-empty string",
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should reject undefined", () => {
|
|
50
|
+
expect(() => sanitizePath(undefined as any)).toThrow(
|
|
51
|
+
"Invalid path: Path must be a non-empty string",
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should reject numbers", () => {
|
|
56
|
+
expect(() => sanitizePath(123 as any)).toThrow(
|
|
57
|
+
"Invalid path: Path must be a non-empty string",
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("should reject hidden files", () => {
|
|
63
|
+
const hiddenPaths = [
|
|
64
|
+
".hidden",
|
|
65
|
+
".git/config",
|
|
66
|
+
"docs/.secret",
|
|
67
|
+
"path/to/.env",
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
hiddenPaths.forEach((path) => {
|
|
71
|
+
it(`should reject: ${path}`, () => {
|
|
72
|
+
expect(() => sanitizePath(path)).toThrow(
|
|
73
|
+
"Invalid path: Hidden files are not allowed",
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("should allow valid paths", () => {
|
|
80
|
+
const validPaths = [
|
|
81
|
+
{ input: "getting-started", expected: "getting-started" },
|
|
82
|
+
{
|
|
83
|
+
input: "api-reference/primitives/thread",
|
|
84
|
+
expected: "api-reference/primitives/thread",
|
|
85
|
+
},
|
|
86
|
+
{ input: "guides/tools", expected: "guides/tools" },
|
|
87
|
+
{ input: "docs/index", expected: "docs/index" },
|
|
88
|
+
{ input: "examples/with-ai-sdk-v6", expected: "examples/with-ai-sdk-v6" },
|
|
89
|
+
{ input: "./current-dir-file", expected: "current-dir-file" },
|
|
90
|
+
{
|
|
91
|
+
input: "deeply/nested/path/to/file",
|
|
92
|
+
expected: "deeply/nested/path/to/file",
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
validPaths.forEach(({ input, expected }) => {
|
|
97
|
+
it(`should allow: ${input} => ${expected}`, () => {
|
|
98
|
+
expect(sanitizePath(input)).toBe(expected);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (process.platform === "win32") {
|
|
104
|
+
describe("Windows-specific tests", () => {
|
|
105
|
+
it("should reject Windows drive letters", () => {
|
|
106
|
+
expect(() => sanitizePath("C:")).toThrow(
|
|
107
|
+
"Invalid path: Path contains invalid Windows characters",
|
|
108
|
+
);
|
|
109
|
+
expect(() => sanitizePath("D:\\file")).toThrow();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should reject UNC paths", () => {
|
|
113
|
+
expect(() => sanitizePath("\\\\server\\share")).toThrow(
|
|
114
|
+
"Invalid path: Absolute paths are not allowed",
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
package/.docs/raw/docs/index.mdx
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: AssistantModal
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
import { Steps, Step } from "fumadocs-ui/components/steps";
|
|
6
|
-
import { InstallCommand } from "@/components/docs/install-command";
|
|
7
|
-
import { AssistantModalSample } from "@/components/samples/assistant-modal-sample";
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
A chat bubble shown in the bottom right corner of the screen. Useful for support or Q&A use cases.
|
|
12
|
-
|
|
13
|
-
<AssistantModalSample />
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## Getting Started
|
|
17
|
-
|
|
18
|
-
<Steps>
|
|
19
|
-
<Step>
|
|
20
|
-
|
|
21
|
-
### Add `assistant-modal`
|
|
22
|
-
|
|
23
|
-
<InstallCommand shadcn={["assistant-modal"]} />
|
|
24
|
-
|
|
25
|
-
This adds `/components/assistant-ui/assistant-modal.tsx` to your project, which you can adjust as needed.
|
|
26
|
-
|
|
27
|
-
</Step>
|
|
28
|
-
<Step>
|
|
29
|
-
|
|
30
|
-
### Use in your application
|
|
31
|
-
|
|
32
|
-
```tsx title="/app/page.tsx" {1,6}
|
|
33
|
-
import { AssistantModal } from "@/components/assistant-ui/assistant-modal";
|
|
34
|
-
|
|
35
|
-
export default function Home() {
|
|
36
|
-
return (
|
|
37
|
-
<div className="h-full">
|
|
38
|
-
<AssistantModal />
|
|
39
|
-
</div>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
</Step>
|
|
45
|
-
</Steps>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: AssistantSidebar
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
import { Steps, Step } from "fumadocs-ui/components/steps";
|
|
6
|
-
import { InstallCommand } from "@/components/docs/install-command";
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
A chat sidebar show on the right side of the screen. Useful for co-pilot use cases.
|
|
11
|
-
|
|
12
|
-
## Getting Started
|
|
13
|
-
|
|
14
|
-
<Steps>
|
|
15
|
-
<Step>
|
|
16
|
-
|
|
17
|
-
### Add `assistant-sidebar`
|
|
18
|
-
|
|
19
|
-
<InstallCommand shadcn={["assistant-sidebar"]} />
|
|
20
|
-
|
|
21
|
-
This adds `/components/assistant-ui/assistant-sidebar.tsx` to your project, which you can adjust as needed.
|
|
22
|
-
|
|
23
|
-
</Step>
|
|
24
|
-
<Step>
|
|
25
|
-
|
|
26
|
-
### Use in your application
|
|
27
|
-
|
|
28
|
-
```tsx title="/app/page.tsx" {1,6}
|
|
29
|
-
import { AssistantSidebar } from "@/components/assistant-ui/assistant-sidebar";
|
|
30
|
-
|
|
31
|
-
export default function Home() {
|
|
32
|
-
return (
|
|
33
|
-
<div className="h-full">
|
|
34
|
-
<AssistantSidebar>{/* your app */}</AssistantSidebar>
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
</Step>
|
|
41
|
-
</Steps>
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Attachment
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
import { Steps, Step } from "fumadocs-ui/components/steps";
|
|
6
|
-
import { AttachmentSample } from "@/components/samples/attachment-sample";
|
|
7
|
-
import { Callout } from "fumadocs-ui/components/callout";
|
|
8
|
-
import { InstallCommand } from "@/components/docs/install-command";
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
The Attachment components let the user attach files and view the attachments.
|
|
13
|
-
|
|
14
|
-
<AttachmentSample />
|
|
15
|
-
|
|
16
|
-
<Callout type="info">
|
|
17
|
-
**Note:** These components provide the UI for attachments, but you also need
|
|
18
|
-
to configure attachment adapters in your runtime to handle file uploads and
|
|
19
|
-
processing. See the [Attachments Guide](/docs/guides/Attachments) for complete
|
|
20
|
-
setup instructions.
|
|
21
|
-
</Callout>
|
|
22
|
-
|
|
23
|
-
## Getting Started
|
|
24
|
-
|
|
25
|
-
<Steps>
|
|
26
|
-
<Step>
|
|
27
|
-
|
|
28
|
-
### Add `attachment`
|
|
29
|
-
|
|
30
|
-
<InstallCommand shadcn={["attachment"]} />
|
|
31
|
-
|
|
32
|
-
This adds a `/components/assistant-ui/attachment.tsx` file to your project, which you can adjust as needed.
|
|
33
|
-
|
|
34
|
-
</Step>
|
|
35
|
-
<Step>
|
|
36
|
-
|
|
37
|
-
### Use in your application
|
|
38
|
-
|
|
39
|
-
```tsx title="/components/assistant-ui/thread.tsx" {1-4,9-10}
|
|
40
|
-
import {
|
|
41
|
-
ComposerAttachments,
|
|
42
|
-
ComposerAddAttachment,
|
|
43
|
-
} from "@/components/assistant-ui/attachment";
|
|
44
|
-
|
|
45
|
-
const Composer: FC = () => {
|
|
46
|
-
return (
|
|
47
|
-
<ComposerPrimitive.Root className="...">
|
|
48
|
-
<ComposerAttachments />
|
|
49
|
-
<ComposerAddAttachment />
|
|
50
|
-
|
|
51
|
-
<ComposerPrimitive.Input
|
|
52
|
-
autoFocus
|
|
53
|
-
placeholder="Write a message..."
|
|
54
|
-
rows={1}
|
|
55
|
-
className="..."
|
|
56
|
-
/>
|
|
57
|
-
<ComposerAction />
|
|
58
|
-
</ComposerPrimitive.Root>
|
|
59
|
-
);
|
|
60
|
-
};
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
```tsx title="/components/assistant-ui/thread.tsx" {1,8}
|
|
64
|
-
import { UserMessageAttachments } from "@/components/assistant-ui/attachment";
|
|
65
|
-
|
|
66
|
-
const UserMessage: FC = () => {
|
|
67
|
-
return (
|
|
68
|
-
<MessagePrimitive.Root className="...">
|
|
69
|
-
<UserActionBar />
|
|
70
|
-
|
|
71
|
-
<UserMessageAttachments />
|
|
72
|
-
|
|
73
|
-
<div className="...">
|
|
74
|
-
<MessagePrimitive.Parts />
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
<BranchPicker className="..." />
|
|
78
|
-
</MessagePrimitive.Root>
|
|
79
|
-
);
|
|
80
|
-
};
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
</Step>
|
|
84
|
-
</Steps>
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Reasoning
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
import { Steps, Step } from "fumadocs-ui/components/steps";
|
|
6
|
-
import { InstallCommand } from "@/components/docs/install-command";
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
The Reasoning component displays AI reasoning or thinking messages in a collapsible UI. Consecutive reasoning message parts are automatically grouped together with smooth animations and a shimmer effect while streaming.
|
|
11
|
-
|
|
12
|
-
## Getting Started
|
|
13
|
-
|
|
14
|
-
<Steps>
|
|
15
|
-
<Step>
|
|
16
|
-
|
|
17
|
-
### Add `reasoning`
|
|
18
|
-
|
|
19
|
-
<InstallCommand shadcn={["reasoning"]} />
|
|
20
|
-
|
|
21
|
-
This adds a `/components/assistant-ui/reasoning.tsx` file to your project, which you can adjust as needed.
|
|
22
|
-
|
|
23
|
-
</Step>
|
|
24
|
-
<Step>
|
|
25
|
-
|
|
26
|
-
### Use in your application
|
|
27
|
-
|
|
28
|
-
Pass the `Reasoning` and `ReasoningGroup` components to the `MessagePrimitive.Parts` component:
|
|
29
|
-
|
|
30
|
-
```tsx title="/app/components/assistant-ui/thread.tsx" {2,10-11}
|
|
31
|
-
import { MessagePrimitive } from "@assistant-ui/react";
|
|
32
|
-
import { Reasoning, ReasoningGroup } from "@/components/assistant-ui/reasoning";
|
|
33
|
-
|
|
34
|
-
const AssistantMessage: FC = () => {
|
|
35
|
-
return (
|
|
36
|
-
<MessagePrimitive.Root className="...">
|
|
37
|
-
<div className="...">
|
|
38
|
-
<MessagePrimitive.Parts
|
|
39
|
-
components={{
|
|
40
|
-
Reasoning: Reasoning,
|
|
41
|
-
ReasoningGroup: ReasoningGroup
|
|
42
|
-
}}
|
|
43
|
-
/>
|
|
44
|
-
</div>
|
|
45
|
-
<AssistantActionBar />
|
|
46
|
-
|
|
47
|
-
<BranchPicker className="..." />
|
|
48
|
-
</MessagePrimitive.Root>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
</Step>
|
|
54
|
-
</Steps>
|
|
55
|
-
|
|
56
|
-
## How It Works
|
|
57
|
-
|
|
58
|
-
The component consists of two parts:
|
|
59
|
-
|
|
60
|
-
1. **Reasoning**: Renders individual reasoning message part content
|
|
61
|
-
2. **ReasoningGroup**: Wraps consecutive reasoning parts in a collapsible container
|
|
62
|
-
|
|
63
|
-
Consecutive reasoning parts are automatically grouped together by the `ReasoningGroup` component, similar to how `ToolGroup` handles tool calls.
|
|
64
|
-
|
|
65
|
-
### Reasoning
|
|
66
|
-
|
|
67
|
-
The Reasoning component doesn't accept additional props—it renders the reasoning text content with markdown support.
|
|
68
|
-
|
|
69
|
-
## Examples
|
|
70
|
-
|
|
71
|
-
### Basic Usage
|
|
72
|
-
|
|
73
|
-
```tsx title="/app/components/assistant-ui/thread.tsx"
|
|
74
|
-
<MessagePrimitive.Parts
|
|
75
|
-
components={{
|
|
76
|
-
Reasoning,
|
|
77
|
-
ReasoningGroup
|
|
78
|
-
}}
|
|
79
|
-
/>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Custom Styling
|
|
83
|
-
|
|
84
|
-
Since the component is copied to your project, you can customize it directly by modifying the `reasoning.tsx` file. The internal components (`ReasoningRoot`, `ReasoningTrigger`, `ReasoningContent`, `ReasoningText`) accept `className` props for styling:
|
|
85
|
-
|
|
86
|
-
```tsx title="/components/assistant-ui/reasoning.tsx"
|
|
87
|
-
const ReasoningGroupImpl: ReasoningGroupComponent = ({
|
|
88
|
-
// ... existing code ...
|
|
89
|
-
return (
|
|
90
|
-
<ReasoningRoot className="rounded-lg border bg-muted/50 p-4">
|
|
91
|
-
<ReasoningTrigger
|
|
92
|
-
active={isReasoningStreaming}
|
|
93
|
-
className="font-semibold text-foreground"
|
|
94
|
-
/>
|
|
95
|
-
<ReasoningContent
|
|
96
|
-
aria-busy={isReasoningStreaming}
|
|
97
|
-
className="mt-2"
|
|
98
|
-
>
|
|
99
|
-
<ReasoningText className="text-base">{children}</ReasoningText>
|
|
100
|
-
</ReasoningContent>
|
|
101
|
-
</ReasoningRoot>
|
|
102
|
-
);
|
|
103
|
-
};
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
You can also customize the individual internal components:
|
|
107
|
-
|
|
108
|
-
```tsx title="/components/assistant-ui/reasoning.tsx"
|
|
109
|
-
const ReasoningRoot: FC<PropsWithChildren<{ className?: string }>> = ({
|
|
110
|
-
// ... existing code ...
|
|
111
|
-
return (
|
|
112
|
-
<Collapsible
|
|
113
|
-
// ...
|
|
114
|
-
className={cn("aui-reasoning-root mb-4 w-full rounded-lg border bg-muted/50 p-4", className)}
|
|
115
|
-
// ...
|
|
116
|
-
>
|
|
117
|
-
{children}
|
|
118
|
-
</Collapsible>
|
|
119
|
-
);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const ReasoningTrigger: FC<{ active: boolean; className?: string }> = ({
|
|
123
|
-
// ... existing code ...
|
|
124
|
-
<CollapsibleTrigger
|
|
125
|
-
className={cn(
|
|
126
|
-
"aui-reasoning-trigger group/trigger -mb-2 flex max-w-[75%] items-center gap-2 py-2 text-sm font-semibold text-foreground transition-colors hover:text-foreground",
|
|
127
|
-
className,
|
|
128
|
-
)}
|
|
129
|
-
>
|
|
130
|
-
{/* ... existing content ... */}
|
|
131
|
-
</CollapsibleTrigger>
|
|
132
|
-
);
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Technical Details
|
|
136
|
-
|
|
137
|
-
### Scroll Lock
|
|
138
|
-
|
|
139
|
-
The component uses the `useScrollLock` hook (exported from `@assistant-ui/react`) to prevent page jumps when collapsing the reasoning section. This maintains the scroll position during the collapse animation.
|
|
140
|
-
|
|
141
|
-
### Animation Timing
|
|
142
|
-
|
|
143
|
-
The component uses CSS custom properties for animation timing:
|
|
144
|
-
- `--animation-duration`: Controls expand/collapse animation (default: 200ms)
|
|
145
|
-
- `--shimmer-duration`: Controls the shimmer effect speed (default: 1000ms)
|
|
146
|
-
|
|
147
|
-
These can be customized by modifying the CSS variables in your component.
|
|
148
|
-
|
|
149
|
-
## Related Components
|
|
150
|
-
|
|
151
|
-
- [ToolGroup](/docs/ui/ToolGroup) - Similar grouping pattern for tool calls
|
|
152
|
-
- [PartGrouping](/docs/ui/PartGrouping) - Experimental API for grouping message parts
|