@chat-js/cli 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/index.js +17065 -16667
  2. package/package.json +1 -1
  3. package/templates/chat-app/app/(auth)/login/page.tsx +3 -3
  4. package/templates/chat-app/app/(chat)/api/chat/route.ts +4 -60
  5. package/templates/chat-app/app/not-found.tsx +2 -2
  6. package/templates/chat-app/chat.config.ts +3 -0
  7. package/templates/chat-app/components/ai-elements/actions.tsx +44 -44
  8. package/templates/chat-app/components/ai-elements/artifact.tsx +92 -92
  9. package/templates/chat-app/components/ai-elements/code-block.tsx +143 -143
  10. package/templates/chat-app/components/ai-elements/context.tsx +313 -313
  11. package/templates/chat-app/components/ai-elements/conversation.tsx +65 -65
  12. package/templates/chat-app/components/ai-elements/extra/conversation-content-scroll-area.tsx +29 -29
  13. package/templates/chat-app/components/ai-elements/extra/mcp-tool-header.tsx +27 -27
  14. package/templates/chat-app/components/ai-elements/message.tsx +341 -344
  15. package/templates/chat-app/components/ai-elements/parseIncompleteMarkdown.tsx +122 -122
  16. package/templates/chat-app/components/ai-elements/prompt-input.tsx +1059 -1059
  17. package/templates/chat-app/components/ai-elements/reasoning.tsx +131 -131
  18. package/templates/chat-app/components/ai-elements/response.tsx +15 -12
  19. package/templates/chat-app/components/ai-elements/sandbox.tsx +84 -84
  20. package/templates/chat-app/components/ai-elements/shimmer.tsx +47 -47
  21. package/templates/chat-app/components/ai-elements/suggestion.tsx +33 -33
  22. package/templates/chat-app/components/ai-elements/tool.tsx +118 -118
  23. package/templates/chat-app/components/app-sidebar-history-conditional.tsx +3 -3
  24. package/templates/chat-app/components/app-sidebar.tsx +3 -3
  25. package/templates/chat-app/components/connectors-dropdown.tsx +6 -3
  26. package/templates/chat-app/components/deep-research-progress.tsx +1 -1
  27. package/templates/chat-app/components/header-breadcrumb.tsx +14 -11
  28. package/templates/chat-app/components/internal-link.tsx +73 -0
  29. package/templates/chat-app/components/login-form.tsx +5 -5
  30. package/templates/chat-app/components/message-parts.tsx +1 -71
  31. package/templates/chat-app/components/model-selector.tsx +3 -3
  32. package/templates/chat-app/components/new-chat-button.tsx +4 -4
  33. package/templates/chat-app/components/part/document-common.tsx +3 -3
  34. package/templates/chat-app/components/part/document-tool.tsx +3 -3
  35. package/templates/chat-app/components/part/message-annotations.tsx +2 -2
  36. package/templates/chat-app/components/part/tool-part.tsx +92 -0
  37. package/templates/chat-app/components/project-chat-item.tsx +2 -2
  38. package/templates/chat-app/components/research-progress.tsx +2 -2
  39. package/templates/chat-app/components/research-task.tsx +1 -1
  40. package/templates/chat-app/components/research-tasks.tsx +1 -1
  41. package/templates/chat-app/components/settings/connectors-settings.tsx +4 -4
  42. package/templates/chat-app/components/settings/mcp-details-page.tsx +5 -5
  43. package/templates/chat-app/components/settings/settings-nav.tsx +3 -3
  44. package/templates/chat-app/components/sidebar-chat-item.tsx +4 -12
  45. package/templates/chat-app/components/sidebar-project-item.tsx +4 -11
  46. package/templates/chat-app/components/sidebar-top-row.tsx +7 -7
  47. package/templates/chat-app/components/sidebar-user-nav.tsx +3 -3
  48. package/templates/chat-app/components/signup-form.tsx +8 -5
  49. package/templates/chat-app/components/source-badge.tsx +3 -9
  50. package/templates/chat-app/components/sources.tsx +1 -1
  51. package/templates/chat-app/components/ui/accordion.tsx +32 -32
  52. package/templates/chat-app/components/ui/alert-dialog.tsx +103 -103
  53. package/templates/chat-app/components/ui/alert.tsx +36 -36
  54. package/templates/chat-app/components/ui/avatar.tsx +28 -28
  55. package/templates/chat-app/components/ui/badge.tsx +22 -22
  56. package/templates/chat-app/components/ui/breadcrumb.tsx +72 -72
  57. package/templates/chat-app/components/ui/button-group.tsx +58 -58
  58. package/templates/chat-app/components/ui/button.tsx +45 -45
  59. package/templates/chat-app/components/ui/card.tsx +65 -65
  60. package/templates/chat-app/components/ui/checkbox.tsx +16 -16
  61. package/templates/chat-app/components/ui/collapsible.tsx +1 -1
  62. package/templates/chat-app/components/ui/command.tsx +137 -137
  63. package/templates/chat-app/components/ui/dialog.tsx +94 -94
  64. package/templates/chat-app/components/ui/drawer.tsx +68 -68
  65. package/templates/chat-app/components/ui/dropdown-menu.tsx +184 -184
  66. package/templates/chat-app/components/ui/empty.tsx +76 -76
  67. package/templates/chat-app/components/ui/extra/action-container.tsx +3 -3
  68. package/templates/chat-app/components/ui/extra/scroll-area-viewport-ref.tsx +24 -24
  69. package/templates/chat-app/components/ui/form.tsx +112 -112
  70. package/templates/chat-app/components/ui/hover-card.tsx +25 -25
  71. package/templates/chat-app/components/ui/input-group.tsx +126 -126
  72. package/templates/chat-app/components/ui/input.tsx +13 -13
  73. package/templates/chat-app/components/ui/label.tsx +12 -12
  74. package/templates/chat-app/components/ui/popover.tsx +25 -25
  75. package/templates/chat-app/components/ui/progress.tsx +19 -19
  76. package/templates/chat-app/components/ui/resizable.tsx +27 -27
  77. package/templates/chat-app/components/ui/scroll-area.tsx +30 -30
  78. package/templates/chat-app/components/ui/select.tsx +108 -108
  79. package/templates/chat-app/components/ui/separator.tsx +16 -16
  80. package/templates/chat-app/components/ui/sheet.tsx +91 -91
  81. package/templates/chat-app/components/ui/sidebar.tsx +615 -615
  82. package/templates/chat-app/components/ui/skeleton.tsx +7 -7
  83. package/templates/chat-app/components/ui/slider.tsx +50 -50
  84. package/templates/chat-app/components/ui/spinner.tsx +8 -8
  85. package/templates/chat-app/components/ui/switch.tsx +16 -16
  86. package/templates/chat-app/components/ui/table.tsx +71 -71
  87. package/templates/chat-app/components/ui/tabs.tsx +31 -31
  88. package/templates/chat-app/components/ui/textarea.tsx +10 -10
  89. package/templates/chat-app/components/ui/toggle.tsx +31 -31
  90. package/templates/chat-app/components/ui/tooltip.tsx +48 -48
  91. package/templates/chat-app/components/upgrade-cta/limit-display.tsx +7 -7
  92. package/templates/chat-app/components/upgrade-cta/login-cta-banner.tsx +3 -3
  93. package/templates/chat-app/components/upgrade-cta/login-prompt.tsx +3 -3
  94. package/templates/chat-app/hooks/use-mobile.ts +13 -13
  95. package/templates/chat-app/lib/ai/core-chat-agent.ts +25 -14
  96. package/templates/chat-app/lib/ai/eval-agent.ts +4 -5
  97. package/templates/chat-app/lib/ai/gateway-model-defaults.ts +24 -0
  98. package/templates/chat-app/lib/ai/installed-tools.ts +12 -0
  99. package/templates/chat-app/lib/ai/mcp/mcp-client.ts +2 -2
  100. package/templates/chat-app/lib/ai/models.generated.ts +4236 -4585
  101. package/templates/chat-app/lib/ai/tool-renderer-registry.ts +31 -0
  102. package/templates/chat-app/lib/ai/types.ts +15 -20
  103. package/templates/chat-app/lib/config-requirements.ts +11 -6
  104. package/templates/chat-app/lib/config-schema.ts +24 -0
  105. package/templates/chat-app/lib/stores/hooks-message-parts.ts +1 -1
  106. package/templates/chat-app/lib/utils.ts +157 -157
  107. package/templates/chat-app/package.json +1 -1
  108. package/templates/chat-app/scripts/check-env.ts +229 -2
  109. package/templates/chat-app/tools/chatjs/_shared/lib/tool-part.ts +5 -0
  110. package/templates/chat-app/{components/part/weather.tsx → tools/chatjs/get-weather/renderer.tsx} +24 -38
  111. package/templates/chat-app/{components/part/retrieve-url.tsx → tools/chatjs/retrieve-url/renderer.tsx} +20 -15
  112. package/templates/chat-app/{lib/ai/tools/retrieve-url.ts → tools/chatjs/retrieve-url/tool.ts} +46 -7
  113. package/templates/chat-app/tools/chatjs/tools.ts +16 -0
  114. package/templates/chat-app/tools/chatjs/ui.ts +17 -0
  115. package/templates/chat-app/tools/chatjs/word-count/renderer.tsx +50 -0
  116. package/templates/chat-app/tools/chatjs/word-count/tool.ts +30 -0
  117. package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.ts +3 -5
  118. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/deep-research.ts +2 -3
  119. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/pipeline.ts +1 -1
  120. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/types.ts +1 -1
  121. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/utils.ts +7 -7
  122. package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/types.ts +1 -1
  123. package/templates/chat-app/{lib/ai/tools → tools/platform}/generate-video.ts +4 -6
  124. package/templates/chat-app/{lib/ai/tools → tools/platform}/read-document.ts +2 -2
  125. package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/multi-query-web-search.ts +1 -1
  126. package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/web-search.ts +1 -1
  127. package/templates/chat-app/{lib/ai/tools → tools/platform}/tools.ts +54 -30
  128. package/templates/chat-app/{lib/ai/tools → tools/platform}/web-search.ts +7 -5
  129. package/templates/electron/CHANGELOG.md +16 -2
  130. package/templates/electron/package.json +1 -1
  131. package/templates/chat-app/lib/ai/tools/tools-definitions.ts +0 -83
  132. /package/templates/chat-app/{lib/ai/tools/get-weather.ts → tools/chatjs/get-weather/tool.ts} +0 -0
  133. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.javascript.ts +0 -0
  134. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.python.ts +0 -0
  135. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.shared.test.ts +0 -0
  136. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.shared.ts +0 -0
  137. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.types.ts +0 -0
  138. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/configuration.ts +0 -0
  139. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/prompts.ts +0 -0
  140. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/researcher-agent.ts +0 -0
  141. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/supervisor-agent.ts +0 -0
  142. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/code-guidelines.ts +0 -0
  143. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-code-document.ts +0 -0
  144. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-sheet-document.ts +0 -0
  145. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-text-document.ts +0 -0
  146. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-code-document.ts +0 -0
  147. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-sheet-document.ts +0 -0
  148. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-text-document.ts +0 -0
  149. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/sheet-guidelines.ts +0 -0
  150. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/text-guidelines.ts +0 -0
  151. /package/templates/chat-app/{lib/ai/tools → tools/platform}/generate-image.ts +0 -0
  152. /package/templates/chat-app/{lib/ai/tools → tools/platform}/research-updates-schema.ts +0 -0
  153. /package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/search-utils.ts +0 -0
  154. /package/templates/chat-app/{lib/ai/tools → tools/platform}/types.ts +0 -0
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
 
3
- import Link from "next/link";
4
3
  import { useSearchParams } from "next/navigation";
5
4
  import { Suspense, useEffect, useState } from "react";
6
5
  import { SocialAuthProviders } from "@/components/auth-providers";
6
+ import { InternalLink } from "@/components/internal-link";
7
7
  import {
8
8
  Card,
9
9
  CardContent,
@@ -65,12 +65,12 @@ export function LoginForm({
65
65
  ) : (
66
66
  <div className="text-center text-sm">
67
67
  Don&apos;t have an account?{" "}
68
- <Link
68
+ <InternalLink
69
69
  className="underline underline-offset-4"
70
70
  href={registerHref}
71
71
  >
72
72
  Sign up
73
- </Link>
73
+ </InternalLink>
74
74
  </div>
75
75
  )}
76
76
  </div>
@@ -78,8 +78,8 @@ export function LoginForm({
78
78
  </Card>
79
79
  <div className="text-balance text-center text-muted-foreground text-xs [&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-primary">
80
80
  By clicking continue, you agree to our{" "}
81
- <Link href="/terms">Terms of Service</Link> and{" "}
82
- <Link href="/privacy">Privacy Policy</Link>.
81
+ <InternalLink href="/terms">Terms of Service</InternalLink> and{" "}
82
+ <InternalLink href="/privacy">Privacy Policy</InternalLink>.
83
83
  </div>
84
84
  </div>
85
85
  );
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
 
3
- import type { ToolUIPart } from "ai";
4
3
  import {
5
4
  isDataUIPart,
6
5
  isReasoningUIPart,
@@ -9,24 +8,14 @@ import {
9
8
  isToolUIPart,
10
9
  } from "ai";
11
10
  import { memo } from "react";
12
- import type { ChatTools } from "@/lib/ai/types";
13
11
  import {
14
12
  useMessagePartByPartIdx,
15
13
  useMessagePartTypesById,
16
14
  } from "@/lib/stores/hooks-message-parts";
17
- import { CodeExecution } from "./part/code-execution";
18
- import { DeepResearch } from "./part/deep-research";
19
- import { DocumentTool } from "./part/document-tool";
20
15
  import { DynamicToolPart } from "./part/dynamic-tool";
21
- import { GenerateImage } from "./part/generate-image";
22
- import { GenerateVideo } from "./part/generate-video";
23
16
  import { ReasoningPart } from "./part/message-reasoning";
24
- import { ReadDocument } from "./part/read-document";
25
-
26
- import { RetrieveUrl } from "./part/retrieve-url";
27
17
  import { TextMessagePart } from "./part/text-message-part";
28
- import { Weather } from "./part/weather";
29
- import { WebSearch } from "./part/web-search";
18
+ import { ToolPart } from "./part/tool-part";
30
19
 
31
20
  interface MessagePartsProps {
32
21
  isLoading: boolean;
@@ -34,64 +23,6 @@ interface MessagePartsProps {
34
23
  messageId: string;
35
24
  }
36
25
 
37
- function ToolPart({
38
- part,
39
- messageId,
40
- isReadonly,
41
- }: {
42
- part: ToolUIPart<ChatTools>;
43
- messageId: string;
44
- isReadonly: boolean;
45
- }) {
46
- const type = part.type;
47
-
48
- if (type === "tool-getWeather") {
49
- return <Weather tool={part} />;
50
- }
51
-
52
- if (
53
- type === "tool-createTextDocument" ||
54
- type === "tool-createCodeDocument" ||
55
- type === "tool-createSheetDocument" ||
56
- type === "tool-editTextDocument" ||
57
- type === "tool-editCodeDocument" ||
58
- type === "tool-editSheetDocument"
59
- ) {
60
- return (
61
- <DocumentTool isReadonly={isReadonly} messageId={messageId} tool={part} />
62
- );
63
- }
64
-
65
- if (type === "tool-retrieveUrl") {
66
- return <RetrieveUrl tool={part} />;
67
- }
68
-
69
- if (type === "tool-readDocument") {
70
- return <ReadDocument tool={part} />;
71
- }
72
-
73
- if (type === "tool-codeExecution") {
74
- return <CodeExecution tool={part} />;
75
- }
76
-
77
- if (type === "tool-generateImage") {
78
- return <GenerateImage tool={part} />;
79
- }
80
-
81
- if (type === "tool-generateVideo") {
82
- return <GenerateVideo tool={part} />;
83
- }
84
-
85
- if (type === "tool-deepResearch") {
86
- return <DeepResearch messageId={messageId} part={part} />;
87
- }
88
-
89
- if (type === "tool-webSearch") {
90
- return <WebSearch messageId={messageId} part={part} />;
91
- }
92
- return null;
93
- }
94
-
95
26
  // Render a single part by index with minimal subscriptions
96
27
  function PureMessagePart({
97
28
  messageId,
@@ -124,7 +55,6 @@ function PureMessagePart({
124
55
  <ToolPart isReadonly={isReadonly} messageId={messageId} part={part} />
125
56
  );
126
57
  }
127
- // At this point it's a Dynamic Tool, tools are handled beforehand by the ToolPart component
128
58
  return (
129
59
  <DynamicToolPart
130
60
  isReadonly={isReadonly}
@@ -7,7 +7,6 @@ import {
7
7
  ChevronUpIcon,
8
8
  FilterIcon,
9
9
  } from "lucide-react";
10
- import Link from "next/link";
11
10
  import {
12
11
  memo,
13
12
  type ReactNode,
@@ -19,6 +18,7 @@ import {
19
18
  useRef,
20
19
  useState,
21
20
  } from "react";
21
+ import { InternalLink } from "@/components/internal-link";
22
22
  import { Badge } from "@/components/ui/badge";
23
23
  import { Button } from "@/components/ui/button";
24
24
  import { Checkbox } from "@/components/ui/checkbox";
@@ -697,10 +697,10 @@ function PureModelSelector({
697
697
  size="sm"
698
698
  variant="ghost"
699
699
  >
700
- <Link aria-label="Add Models" href="/settings/models">
700
+ <InternalLink aria-label="Add Models" href="/settings/models">
701
701
  Add Models
702
702
  <ChevronRightIcon className="h-4 w-4" />
703
- </Link>
703
+ </InternalLink>
704
704
  </Button>
705
705
  </div>
706
706
  )}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { Plus } from "lucide-react";
4
- import Link from "next/link";
5
4
  import { useEffect, useState } from "react";
5
+ import { InternalLink } from "@/components/internal-link";
6
6
  import { getNewChatShortcutText } from "@/components/keyboard-shortcuts";
7
7
  import { SidebarMenuButton, useSidebar } from "@/components/ui/sidebar";
8
8
  import { useChatId } from "@/providers/chat-id-provider";
@@ -18,10 +18,10 @@ export function NewChatButton() {
18
18
 
19
19
  return (
20
20
  <SidebarMenuButton asChild className="mt-4" tooltip="New Chat">
21
- <Link
21
+ <InternalLink
22
22
  className="flex w-full items-center gap-2"
23
23
  href="/"
24
- onClick={() => {
24
+ onNavigate={() => {
25
25
  setOpenMobile(false);
26
26
  refreshChatID();
27
27
  }}
@@ -31,7 +31,7 @@ export function NewChatButton() {
31
31
  <span className="ml-auto text-muted-foreground text-xs">
32
32
  {shortcutText}
33
33
  </span>
34
- </Link>
34
+ </InternalLink>
35
35
  </SidebarMenuButton>
36
36
  );
37
37
  }
@@ -1,12 +1,12 @@
1
1
  import { File, Loader2, Pencil } from "lucide-react";
2
2
  import { memo } from "react";
3
3
  import { useArtifact } from "@/hooks/use-artifact";
4
+ import type { ChatMessage } from "@/lib/ai/types";
5
+ import type { ArtifactKind } from "@/lib/artifacts/artifact-kind";
4
6
  import type {
5
7
  CreateDocumentToolType,
6
8
  EditDocumentToolType,
7
- } from "@/lib/ai/tools/documents/types";
8
- import type { ChatMessage } from "@/lib/ai/types";
9
- import type { ArtifactKind } from "@/lib/artifacts/artifact-kind";
9
+ } from "@/tools/platform/documents/types";
10
10
 
11
11
  export type CreateDocumentTool = Extract<
12
12
  ChatMessage["parts"][number],
@@ -2,13 +2,13 @@
2
2
 
3
3
  import { memo, useEffect } from "react";
4
4
  import { useArtifact } from "@/hooks/use-artifact";
5
+ import type { ChatMessage } from "@/lib/ai/types";
6
+ import { useIsLastArtifact } from "@/lib/stores/hooks-message-parts";
5
7
  import {
6
8
  type DocumentToolType,
7
9
  getToolKind,
8
10
  isEditTool,
9
- } from "@/lib/ai/tools/documents/types";
10
- import type { ChatMessage } from "@/lib/ai/types";
11
- import { useIsLastArtifact } from "@/lib/stores/hooks-message-parts";
11
+ } from "@/tools/platform/documents/types";
12
12
  import { DocumentPreview } from "./document-preview";
13
13
 
14
14
  type DocumentTool = Extract<
@@ -1,9 +1,9 @@
1
1
  import { memo } from "react";
2
+ import { useMessageResearchUpdatePartsById } from "@/lib/stores/hooks-base";
2
3
  import type {
3
4
  ResearchUpdate,
4
5
  WebSearchUpdate,
5
- } from "@/lib/ai/tools/research-updates-schema";
6
- import { useMessageResearchUpdatePartsById } from "@/lib/stores/hooks-base";
6
+ } from "@/tools/platform/research-updates-schema";
7
7
  import { ReasonSearchResearchProgress } from "../deep-research-progress";
8
8
  import { Sources } from "../sources";
9
9
 
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import type { ToolUIPart } from "ai";
4
+ import type { ComponentType } from "react";
5
+ import {
6
+ type InstalledToolPart,
7
+ type InstalledToolType,
8
+ isInstalledToolType,
9
+ type ToolRendererProps,
10
+ toolRendererRegistry,
11
+ } from "@/lib/ai/tool-renderer-registry";
12
+ import type { ChatTools } from "@/lib/ai/types";
13
+ import { CodeExecution } from "./code-execution";
14
+ import { DeepResearch } from "./deep-research";
15
+ import { DocumentTool } from "./document-tool";
16
+ import { GenerateImage } from "./generate-image";
17
+ import { GenerateVideo } from "./generate-video";
18
+ import { ReadDocument } from "./read-document";
19
+ import { WebSearch } from "./web-search";
20
+
21
+ interface ToolPartProps {
22
+ isReadonly: boolean;
23
+ messageId: string;
24
+ part: ToolUIPart<ChatTools>;
25
+ }
26
+
27
+ function renderInstalledTool<T extends InstalledToolType>({
28
+ part,
29
+ messageId,
30
+ isReadonly,
31
+ }: {
32
+ part: InstalledToolPart<T>;
33
+ messageId: string;
34
+ isReadonly: boolean;
35
+ }) {
36
+ const Renderer = toolRendererRegistry[part.type] as unknown as ComponentType<
37
+ ToolRendererProps<T>
38
+ >;
39
+
40
+ return <Renderer isReadonly={isReadonly} messageId={messageId} tool={part} />;
41
+ }
42
+
43
+ export function ToolPart({ part, messageId, isReadonly }: ToolPartProps) {
44
+ const type = part.type;
45
+
46
+ if (
47
+ type === "tool-createTextDocument" ||
48
+ type === "tool-createCodeDocument" ||
49
+ type === "tool-createSheetDocument" ||
50
+ type === "tool-editTextDocument" ||
51
+ type === "tool-editCodeDocument" ||
52
+ type === "tool-editSheetDocument"
53
+ ) {
54
+ return (
55
+ <DocumentTool isReadonly={isReadonly} messageId={messageId} tool={part} />
56
+ );
57
+ }
58
+
59
+ if (type === "tool-readDocument") {
60
+ return <ReadDocument tool={part} />;
61
+ }
62
+
63
+ if (type === "tool-codeExecution") {
64
+ return <CodeExecution tool={part} />;
65
+ }
66
+
67
+ if (type === "tool-generateImage") {
68
+ return <GenerateImage tool={part} />;
69
+ }
70
+
71
+ if (type === "tool-generateVideo") {
72
+ return <GenerateVideo tool={part} />;
73
+ }
74
+
75
+ if (type === "tool-deepResearch") {
76
+ return <DeepResearch messageId={messageId} part={part} />;
77
+ }
78
+
79
+ if (type === "tool-webSearch") {
80
+ return <WebSearch messageId={messageId} part={part} />;
81
+ }
82
+
83
+ if (isInstalledToolType(type)) {
84
+ return renderInstalledTool({
85
+ part: part as InstalledToolPart<typeof type>,
86
+ messageId,
87
+ isReadonly,
88
+ });
89
+ }
90
+
91
+ return null;
92
+ }
@@ -2,9 +2,9 @@
2
2
 
3
3
  import { formatDistance } from "date-fns";
4
4
  import { MoreHorizontal, Pencil, Trash2 } from "lucide-react";
5
- import Link from "next/link";
6
5
  import { useState } from "react";
7
6
  import { ChatRenameDialog } from "@/components/chat-rename-dialog";
7
+ import { InternalLink } from "@/components/internal-link";
8
8
  import { ShareDialog } from "@/components/share-button";
9
9
  import { Button } from "@/components/ui/button";
10
10
  import {
@@ -42,7 +42,7 @@ export function ProjectChatItem({
42
42
  <>
43
43
  <div className="group relative">
44
44
  <div className="relative flex items-center gap-3 px-4 py-3 transition-colors hover:bg-muted/50">
45
- <Link className="absolute inset-0 z-10" href={chatHref} />
45
+ <InternalLink className="absolute inset-0 z-10" href={chatHref} />
46
46
  <div className="min-w-0 flex-1">
47
47
  <div className="truncate font-medium text-sm">{chat.title}</div>
48
48
  <div className="text-muted-foreground text-xs">
@@ -1,9 +1,9 @@
1
1
  import { Maximize2, Minimize2 } from "lucide-react";
2
2
  import React from "react";
3
3
  import { UpdateTitle } from "@/components/update-title";
4
- // Type-only imports
5
- import type { ResearchUpdate } from "@/lib/ai/tools/research-updates-schema";
6
4
  import { cn } from "@/lib/utils";
5
+ // Type-only imports
6
+ import type { ResearchUpdate } from "@/tools/platform/research-updates-schema";
7
7
  import { ResearchTask } from "./research-task";
8
8
  import { ResearchTasks } from "./research-tasks";
9
9
 
@@ -2,7 +2,7 @@ import { Loader2, SearchIcon } from "lucide-react";
2
2
  import { motion } from "motion/react";
3
3
  import { Badge } from "@/components/ui/badge";
4
4
  import { UpdateTitle } from "@/components/update-title";
5
- import type { ResearchUpdate } from "@/lib/ai/tools/research-updates-schema";
5
+ import type { ResearchUpdate } from "@/tools/platform/research-updates-schema";
6
6
  import { WebSourceBadge } from "./source-badge";
7
7
 
8
8
  export const ResearchTask = ({
@@ -2,8 +2,8 @@ import { CircleCheck, Dot, FileText, Pencil, Sparkles } from "lucide-react";
2
2
  import { motion } from "motion/react";
3
3
  import type React from "react";
4
4
  import type { ReactNode } from "react";
5
- import type { ResearchUpdate } from "@/lib/ai/tools/research-updates-schema";
6
5
  import { cn } from "@/lib/utils";
6
+ import type { ResearchUpdate } from "@/tools/platform/research-updates-schema";
7
7
  import { ResearchTask } from "./research-task";
8
8
 
9
9
  export const ResearchTasks = ({ updates }: { updates: ResearchUpdate[] }) => (
@@ -10,10 +10,10 @@ import {
10
10
  Radio,
11
11
  Trash2,
12
12
  } from "lucide-react";
13
- import Link from "next/link";
14
13
  import { useQueryStates } from "nuqs";
15
14
  import { Fragment, useCallback, useMemo } from "react";
16
15
  import { toast } from "sonner";
16
+ import { InternalLink } from "@/components/internal-link";
17
17
  import { ConnectorHeader } from "@/components/settings/connector-header";
18
18
  import { McpConnectDialog } from "@/components/settings/mcp-connect-dialog";
19
19
  import { McpCreateDialog } from "@/components/settings/mcp-create-dialog";
@@ -335,7 +335,7 @@ function CustomConnectorRow({
335
335
  size="sm"
336
336
  variant="outline"
337
337
  >
338
- <Link href={href}>
338
+ <InternalLink href={href}>
339
339
  {isTestingConnection ? (
340
340
  <span className="inline-flex items-center gap-2">
341
341
  <Loader2 className="size-4 animate-spin" />
@@ -346,7 +346,7 @@ function CustomConnectorRow({
346
346
  {actionLabel}
347
347
  </span>
348
348
  )}
349
- </Link>
349
+ </InternalLink>
350
350
  </Button>
351
351
  ) : null}
352
352
  <DropdownMenu>
@@ -405,7 +405,7 @@ function BuiltInConnectorRow({ connector }: { connector: McpConnector }) {
405
405
  url={connector.url}
406
406
  />
407
407
  <Button asChild size="sm" variant="outline">
408
- <Link href={href}>View</Link>
408
+ <InternalLink href={href}>View</InternalLink>
409
409
  </Button>
410
410
  </div>
411
411
  );
@@ -10,10 +10,10 @@ import {
10
10
  Trash2,
11
11
  Wrench,
12
12
  } from "lucide-react";
13
- import Link from "next/link";
14
13
  import { useRouter, useSearchParams } from "next/navigation";
15
14
  import { useCallback, useEffect, useMemo, useState } from "react";
16
15
  import { toast } from "sonner";
16
+ import { InternalLink } from "@/components/internal-link";
17
17
  import { Button } from "@/components/ui/button";
18
18
  import { Label } from "@/components/ui/label";
19
19
  import { ScrollArea } from "@/components/ui/scroll-area";
@@ -237,10 +237,10 @@ export function McpDetailsPage({ connectorId }: { connectorId: string }) {
237
237
  return (
238
238
  <SettingsPageContent className="gap-4">
239
239
  <Button asChild className="w-fit" size="sm" variant="ghost">
240
- <Link href="/settings/connectors">
240
+ <InternalLink href="/settings/connectors">
241
241
  <ChevronLeft className="size-4" />
242
242
  Back
243
- </Link>
243
+ </InternalLink>
244
244
  </Button>
245
245
  <div className="flex flex-col items-center justify-center py-12 text-center">
246
246
  <AlertCircle className="size-6 text-destructive" />
@@ -259,10 +259,10 @@ export function McpDetailsPage({ connectorId }: { connectorId: string }) {
259
259
  return (
260
260
  <SettingsPageContent className="gap-4">
261
261
  <Button asChild className="w-fit" size="sm" variant="ghost">
262
- <Link href="/settings/connectors">
262
+ <InternalLink href="/settings/connectors">
263
263
  <ChevronLeft className="size-4" />
264
264
  Back
265
- </Link>
265
+ </InternalLink>
266
266
  </Button>
267
267
 
268
268
  <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
 
3
3
  import { Cpu, Plug, Settings } from "lucide-react";
4
- import Link from "next/link";
5
4
  import { usePathname } from "next/navigation";
6
5
  import { useMemo } from "react";
6
+ import { InternalLink } from "@/components/internal-link";
7
7
  import { config } from "@/lib/config";
8
8
  import { cn } from "@/lib/utils";
9
9
 
@@ -46,7 +46,7 @@ export function SettingsNav({
46
46
  : pathname.startsWith(href);
47
47
 
48
48
  return (
49
- <Link
49
+ <InternalLink
50
50
  className={cn(
51
51
  "flex items-center gap-2 rounded-md px-3 py-2.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
52
52
  isActive && "bg-muted text-foreground"
@@ -56,7 +56,7 @@ export function SettingsNav({
56
56
  >
57
57
  <Icon className="size-4" />
58
58
  {label}
59
- </Link>
59
+ </InternalLink>
60
60
  );
61
61
  })}
62
62
  </nav>
@@ -1,10 +1,9 @@
1
1
  "use client";
2
2
  import { MoreHorizontal } from "lucide-react";
3
- import Link from "next/link";
4
- import { useRouter } from "next/navigation";
5
3
  import { memo, useState } from "react";
6
4
  import { toast } from "sonner";
7
5
  import { ChatMenuItems } from "@/components/chat-menu-items";
6
+ import { InternalLink } from "@/components/internal-link";
8
7
  import { ShareDialog } from "@/components/share-button";
9
8
  import {
10
9
  DropdownMenu,
@@ -40,7 +39,6 @@ const PureSidebarChatItem = ({
40
39
  chat.projectId
41
40
  ? `/project/${chat.projectId}/chat/${chat.id}`
42
41
  : `/chat/${chat.id}`;
43
- const router = useRouter();
44
42
  const [isEditing, setIsEditing] = useState(false);
45
43
  const [editTitle, setEditTitle] = useState(chat.title);
46
44
  const [shareDialogOpen, setShareDialogOpen] = useState(false);
@@ -87,21 +85,15 @@ const PureSidebarChatItem = ({
87
85
  </div>
88
86
  ) : (
89
87
  <SidebarMenuButton asChild isActive={isActive}>
90
- <Link
88
+ <InternalLink
91
89
  href={chatHref}
92
- onClick={(e) => {
93
- // Allow middle-click and ctrl+click to open in new tab
94
- if (e.button === 1 || e.ctrlKey || e.metaKey) {
95
- return;
96
- }
97
- e.preventDefault();
98
- router.push(chatHref);
90
+ onNavigate={() => {
99
91
  setOpenMobile(false);
100
92
  }}
101
93
  prefetch={prefetch}
102
94
  >
103
95
  <span>{chat.title}</span>
104
- </Link>
96
+ </InternalLink>
105
97
  </SidebarMenuButton>
106
98
  )}
107
99
 
@@ -1,10 +1,9 @@
1
1
  "use client";
2
2
 
3
3
  import { MoreHorizontal } from "lucide-react";
4
- import Link from "next/link";
5
- import { useRouter } from "next/navigation";
6
4
  import { useState } from "react";
7
5
  import { DeleteProjectDialog } from "@/components/delete-project-dialog";
6
+ import { InternalLink } from "@/components/internal-link";
8
7
  import {
9
8
  type ProjectDetailsData,
10
9
  ProjectDetailsDialog,
@@ -36,7 +35,6 @@ export function SidebarProjectItem({
36
35
  }) {
37
36
  const [showEditDialog, setShowEditDialog] = useState(false);
38
37
  const [showDeleteDialog, setShowDeleteDialog] = useState(false);
39
- const router = useRouter();
40
38
 
41
39
  const { mutateAsync: renameProject, isPending } = useRenameProject();
42
40
 
@@ -56,14 +54,9 @@ export function SidebarProjectItem({
56
54
  return (
57
55
  <SidebarMenuItem>
58
56
  <SidebarMenuButton asChild className="cursor-pointer" isActive={isActive}>
59
- <Link
57
+ <InternalLink
60
58
  href={projectHref}
61
- onClick={(e) => {
62
- if (e.button === 1 || e.ctrlKey || e.metaKey) {
63
- return;
64
- }
65
- e.preventDefault();
66
- router.push(projectHref);
59
+ onNavigate={() => {
67
60
  setOpenMobile(false);
68
61
  }}
69
62
  prefetch={false}
@@ -74,7 +67,7 @@ export function SidebarProjectItem({
74
67
  size={16}
75
68
  />
76
69
  <span>{project.name}</span>
77
- </Link>
70
+ </InternalLink>
78
71
  </SidebarMenuButton>
79
72
 
80
73
  <DropdownMenu modal={true}>
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { PanelLeft } from "lucide-react";
4
4
  import Image from "next/image";
5
- import Link from "next/link";
5
+ import { InternalLink } from "@/components/internal-link";
6
6
  import { SidebarToggle } from "@/components/sidebar-toggle";
7
7
  import { useSidebar } from "@/components/ui/sidebar";
8
8
  import { config } from "@/lib/config";
@@ -17,10 +17,10 @@ export function SidebarTopRow() {
17
17
  return (
18
18
  <div className="flex w-full items-center justify-between gap-2">
19
19
  {isExpanded ? (
20
- <Link
20
+ <InternalLink
21
21
  className="flex flex-row items-center gap-2"
22
22
  href="/"
23
- onClick={() => {
23
+ onNavigate={() => {
24
24
  setOpenMobile(false);
25
25
  refreshChatID();
26
26
  }}
@@ -35,22 +35,22 @@ export function SidebarTopRow() {
35
35
  />
36
36
  {config.appName}
37
37
  </span>
38
- </Link>
38
+ </InternalLink>
39
39
  ) : (
40
40
  <button
41
41
  aria-label="Expand sidebar"
42
- className="group/logo relative flex size-8 items-center justify-center rounded-md hover:bg-muted"
42
+ className="relative flex size-8 items-center justify-center rounded-md transition-colors group-hover/sidebar:bg-muted"
43
43
  onClick={toggleSidebar}
44
44
  type="button"
45
45
  >
46
46
  <Image
47
47
  alt={config.appName}
48
- className="h-5 w-5 transition-opacity duration-150 group-hover/logo:opacity-0"
48
+ className="h-5 w-5 transition-opacity duration-150 group-hover/sidebar:opacity-0"
49
49
  height={20}
50
50
  src="/icon.svg"
51
51
  width={20}
52
52
  />
53
- <PanelLeft className="absolute size-4 opacity-0 transition-opacity duration-150 group-hover/logo:opacity-100" />
53
+ <PanelLeft className="absolute size-4 opacity-0 transition-opacity duration-150 group-hover/sidebar:opacity-100" />
54
54
  </button>
55
55
  )}
56
56
 
@@ -9,9 +9,9 @@ import {
9
9
  Settings,
10
10
  Sun,
11
11
  } from "lucide-react";
12
- import Link from "next/link";
13
12
  import { useRouter } from "next/navigation";
14
13
  import { useTheme } from "next-themes";
14
+ import { InternalLink } from "@/components/internal-link";
15
15
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
16
16
  import {
17
17
  DropdownMenu,
@@ -129,10 +129,10 @@ export function SidebarUserNav() {
129
129
  <DropdownMenuSeparator />
130
130
  <DropdownMenuGroup>
131
131
  <DropdownMenuItem asChild>
132
- <Link href="/settings">
132
+ <InternalLink href="/settings">
133
133
  <Settings className="mr-2 size-4" />
134
134
  Settings
135
- </Link>
135
+ </InternalLink>
136
136
  </DropdownMenuItem>
137
137
  <DropdownMenuItem
138
138
  onClick={() =>