@chat-js/cli 0.1.3 → 0.2.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/dist/index.js +400 -246
- package/package.json +1 -1
- package/templates/chat-app/.claude/skiller.toml +18 -0
- package/templates/chat-app/.claude/skills/chat-context/SKILL.md +6 -0
- package/templates/chat-app/.claude/skills/chat-context/chat-context.mdc +36 -0
- package/templates/chat-app/.claude/skills/lazy-prefetch-pattern/lazy-prefetch-pattern.mdc +27 -0
- package/templates/chat-app/.claude/skills/react/react.mdc +29 -0
- package/templates/chat-app/.claude/skills/trpc-patterns/trpc-patterns.mdc +77 -0
- package/templates/chat-app/.claude/skills/typescript/typescript.mdc +53 -0
- package/templates/chat-app/.claude/skills/ultracite/ultracite.mdc +129 -0
- package/templates/chat-app/.cursor/skills/chat-context/SKILL.md +37 -0
- package/templates/chat-app/.cursor/skills/lazy-prefetch-pattern/SKILL.md +26 -0
- package/templates/chat-app/.cursor/skills/react/SKILL.md +28 -0
- package/templates/chat-app/.cursor/skills/trpc-patterns/SKILL.md +76 -0
- package/templates/chat-app/.cursor/skills/typescript/SKILL.md +52 -0
- package/templates/chat-app/.cursor/skills/ultracite/SKILL.md +128 -0
- package/templates/chat-app/app/(chat)/actions.ts +1 -1
- package/templates/chat-app/app/(chat)/api/chat/[id]/stream/route.ts +6 -5
- package/templates/chat-app/app/(chat)/api/chat/route.ts +14 -15
- package/templates/chat-app/app/(chat)/chat-providers.tsx +2 -2
- package/templates/chat-app/app/(chat)/layout.tsx +7 -6
- package/templates/chat-app/app/api/cron/cleanup/route.ts +4 -3
- package/templates/chat-app/app/globals.css +22 -22
- package/templates/chat-app/app/layout.tsx +1 -1
- package/templates/chat-app/biome.jsonc +3 -3
- package/templates/chat-app/chat.config.ts +47 -20
- package/templates/chat-app/components/anonymous-session-init.tsx +4 -12
- package/templates/chat-app/components/artifact-actions.tsx +5 -5
- package/templates/chat-app/components/artifact-panel.tsx +6 -6
- package/templates/chat-app/components/assistant-message.tsx +1 -1
- package/templates/chat-app/components/chat/chat-layout.tsx +2 -2
- package/templates/chat-app/components/chat/chat-welcome.tsx +1 -0
- package/templates/chat-app/components/chat-features-definitions.ts +11 -8
- package/templates/chat-app/components/chat-menu-items.tsx +4 -4
- package/templates/chat-app/components/chat-sync.tsx +1 -2
- package/templates/chat-app/components/clone-chat-button.tsx +2 -2
- package/templates/chat-app/components/code-editor.tsx +5 -5
- package/templates/chat-app/components/connectors-dropdown.tsx +2 -2
- package/templates/chat-app/components/console.tsx +5 -5
- package/templates/chat-app/components/create-artifact.tsx +28 -28
- package/templates/chat-app/components/data-stream-provider.tsx +2 -2
- package/templates/chat-app/components/deep-research-progress.tsx +2 -2
- package/templates/chat-app/components/delete-chat-dialog.tsx +3 -3
- package/templates/chat-app/components/delete-project-dialog.tsx +3 -3
- package/templates/chat-app/components/diffview.tsx +3 -3
- package/templates/chat-app/components/favicon-group.tsx +7 -7
- package/templates/chat-app/components/header-breadcrumb.tsx +11 -11
- package/templates/chat-app/components/image-editor.tsx +5 -5
- package/templates/chat-app/components/image-modal.tsx +4 -4
- package/templates/chat-app/components/interactive-chart-impl.tsx +269 -0
- package/templates/chat-app/components/interactive-charts.tsx +18 -246
- package/templates/chat-app/components/lexical-chat-input.tsx +10 -10
- package/templates/chat-app/components/message-editor.tsx +3 -3
- package/templates/chat-app/components/message-parts.tsx +8 -3
- package/templates/chat-app/components/messages-pane.tsx +4 -4
- package/templates/chat-app/components/messages.tsx +5 -5
- package/templates/chat-app/components/model-selector.tsx +4 -1
- package/templates/chat-app/components/multimodal-input.tsx +14 -5
- package/templates/chat-app/components/part/code-execution.tsx +4 -1
- package/templates/chat-app/components/part/document-common.tsx +8 -8
- package/templates/chat-app/components/part/document-preview.tsx +34 -16
- package/templates/chat-app/components/part/document-tool.tsx +3 -3
- package/templates/chat-app/components/part/dynamic-tool.tsx +3 -3
- package/templates/chat-app/components/part/generate-video.tsx +54 -0
- package/templates/chat-app/components/part/message-reasoning.tsx +3 -3
- package/templates/chat-app/components/project-details-dialog.tsx +4 -4
- package/templates/chat-app/components/project-home.tsx +1 -0
- package/templates/chat-app/components/project-icon-picker.tsx +5 -5
- package/templates/chat-app/components/project-icon.tsx +4 -4
- package/templates/chat-app/components/project-menu-items.tsx +3 -3
- package/templates/chat-app/components/research-tasks.tsx +3 -3
- package/templates/chat-app/components/sandbox.tsx +4 -4
- package/templates/chat-app/components/search-chats-dialog.tsx +11 -11
- package/templates/chat-app/components/settings/connectors-settings.tsx +1 -1
- package/templates/chat-app/components/settings/settings-nav.tsx +1 -1
- package/templates/chat-app/components/sheet-editor.tsx +5 -5
- package/templates/chat-app/components/sidebar-chats-list.tsx +5 -5
- package/templates/chat-app/components/suggested-actions.tsx +3 -3
- package/templates/chat-app/components/text-editor.tsx +5 -5
- package/templates/chat-app/components/toolbar.tsx +6 -6
- package/templates/chat-app/components/upgrade-cta/login-cta-banner.tsx +5 -5
- package/templates/chat-app/components/upgrade-cta/login-prompt.tsx +4 -4
- package/templates/chat-app/components/upgrade-cta/share-menu-item.tsx +3 -3
- package/templates/chat-app/components/user-message.tsx +3 -3
- package/templates/chat-app/components/version-footer.tsx +4 -4
- package/templates/chat-app/hooks/chat-sync-hooks.ts +0 -55
- package/templates/chat-app/hooks/use-artifact.tsx +3 -3
- package/templates/chat-app/hooks/use-auto-focus.ts +37 -7
- package/templates/chat-app/hooks/use-media-query.tsx +2 -4
- package/templates/chat-app/lib/ai/active-gateway.ts +1 -1
- package/templates/chat-app/lib/ai/ai-gateway-models-schemas.ts +30 -6
- package/templates/chat-app/lib/ai/app-model-id.ts +1 -1
- package/templates/chat-app/lib/ai/app-models.ts +4 -4
- package/templates/chat-app/lib/ai/eval-agent.ts +5 -5
- package/templates/chat-app/lib/ai/followup-suggestions.ts +1 -1
- package/templates/chat-app/lib/ai/gateway-model-defaults.ts +131 -41
- package/templates/chat-app/lib/ai/gateways/gateway-provider.ts +10 -6
- package/templates/chat-app/lib/ai/gateways/openai-compatible-gateway.ts +9 -4
- package/templates/chat-app/lib/ai/gateways/openai-gateway.ts +9 -4
- package/templates/chat-app/lib/ai/gateways/openrouter-gateway.ts +17 -12
- package/templates/chat-app/lib/ai/gateways/registry.ts +9 -0
- package/templates/chat-app/lib/ai/gateways/vercel-gateway.ts +36 -4
- package/templates/chat-app/lib/ai/mcp/cache.ts +13 -13
- package/templates/chat-app/lib/ai/model-data.ts +21 -20
- package/templates/chat-app/lib/ai/models.generated.ts +4397 -3592
- package/templates/chat-app/lib/ai/models.ts +1 -1
- package/templates/chat-app/lib/ai/providers.ts +10 -0
- package/templates/chat-app/lib/ai/text-splitter.ts +3 -4
- package/templates/chat-app/lib/ai/to-model-data.ts +1 -0
- package/templates/chat-app/lib/ai/tools/code-execution.ts +122 -53
- package/templates/chat-app/lib/ai/tools/deep-research/configuration.ts +35 -32
- package/templates/chat-app/lib/ai/tools/deep-research/pipeline.ts +2 -2
- package/templates/chat-app/lib/ai/tools/deep-research/types.ts +9 -9
- package/templates/chat-app/lib/ai/tools/documents/types.ts +4 -4
- package/templates/chat-app/lib/ai/tools/generate-image.ts +42 -20
- package/templates/chat-app/lib/ai/tools/generate-video.ts +166 -0
- package/templates/chat-app/lib/ai/tools/get-weather.ts +20 -20
- package/templates/chat-app/lib/ai/tools/read-document.ts +3 -3
- package/templates/chat-app/lib/ai/tools/steps/multi-query-web-search.ts +11 -11
- package/templates/chat-app/lib/ai/tools/steps/web-search.ts +6 -6
- package/templates/chat-app/lib/ai/tools/tools-definitions.ts +10 -5
- package/templates/chat-app/lib/ai/tools/tools.ts +15 -6
- package/templates/chat-app/lib/ai/tools/types.ts +2 -2
- package/templates/chat-app/lib/ai/types.ts +22 -13
- package/templates/chat-app/lib/artifacts/code/client.tsx +5 -5
- package/templates/chat-app/lib/artifacts/sheet/client.tsx +2 -2
- package/templates/chat-app/lib/artifacts/text/client.tsx +18 -3
- package/templates/chat-app/lib/clone-messages.test.ts +6 -1
- package/templates/chat-app/lib/config-requirements.ts +19 -10
- package/templates/chat-app/lib/config-schema.ts +189 -103
- package/templates/chat-app/lib/config.ts +4 -4
- package/templates/chat-app/lib/credits/cost-accumulator.ts +11 -8
- package/templates/chat-app/lib/env-schema.ts +1 -1
- package/templates/chat-app/lib/features-config.ts +6 -6
- package/templates/chat-app/lib/stores/with-threads.ts +3 -3
- package/templates/chat-app/lib/thread-utils.ts +2 -2
- package/templates/chat-app/lib/types/anonymous.ts +4 -4
- package/templates/chat-app/lib/types/ui-chat.ts +7 -7
- package/templates/chat-app/lib/utils/download-assets.ts +3 -3
- package/templates/chat-app/lib/utils/rate-limit.ts +8 -8
- package/templates/chat-app/next.config.ts +0 -25
- package/templates/chat-app/package.json +15 -15
- package/templates/chat-app/playwright.config.ts +5 -5
- package/templates/chat-app/providers/chat-id-provider.tsx +5 -5
- package/templates/chat-app/providers/chat-input-provider.tsx +15 -15
- package/templates/chat-app/providers/chat-models-provider.tsx +3 -3
- package/templates/chat-app/providers/default-model-provider.tsx +5 -5
- package/templates/chat-app/providers/parse-chat-id-from-pathname.test.ts +16 -0
- package/templates/chat-app/providers/session-provider.tsx +2 -2
- package/templates/chat-app/scripts/check-env.ts +36 -4
- package/templates/chat-app/tests/artifacts.e2e.ts +7 -0
- package/templates/chat-app/tests/auth.setup.e2e.ts +10 -0
- package/templates/chat-app/tests/chat.e2e.ts +7 -0
- package/templates/chat-app/tests/reasoning.e2e.ts +7 -0
- package/templates/chat-app/tests/reasoning.setup.e2e.ts +10 -0
- package/templates/chat-app/trpc/routers/chat.router.ts +1 -1
- package/templates/chat-app/trpc/routers/mcp.router.ts +3 -3
- package/templates/chat-app/vitest.config.ts +7 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ultracite
|
|
3
|
+
description: 'Skill: ultracite'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ultracite Code Standards
|
|
7
|
+
|
|
8
|
+
This project uses **Ultracite**, a zero-config Biome preset that enforces strict code quality standards through automated formatting and linting.
|
|
9
|
+
|
|
10
|
+
## Quick Reference
|
|
11
|
+
|
|
12
|
+
- **Format code**: `npx ultracite@6.3.3 fix`
|
|
13
|
+
- **Check for issues**: `npx ultracite@6.3.3 check`
|
|
14
|
+
- **Diagnose setup**: `npx ultracite@6.3.3 doctor`
|
|
15
|
+
|
|
16
|
+
Biome (the underlying engine) provides extremely fast Rust-based linting and formatting. Most issues are automatically fixable.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Core Principles
|
|
21
|
+
|
|
22
|
+
Write code that is **accessible, performant, type-safe, and maintainable**. Focus on clarity and explicit intent over brevity.
|
|
23
|
+
|
|
24
|
+
### Type Safety & Explicitness
|
|
25
|
+
|
|
26
|
+
- Use explicit types for function parameters and return values when they enhance clarity
|
|
27
|
+
- Prefer `unknown` over `any` when the type is genuinely unknown
|
|
28
|
+
- Use const assertions (`as const`) for immutable values and literal types
|
|
29
|
+
- Leverage TypeScript's type narrowing instead of type assertions
|
|
30
|
+
- Use meaningful variable names instead of magic numbers - extract constants with descriptive names
|
|
31
|
+
|
|
32
|
+
### Modern JavaScript/TypeScript
|
|
33
|
+
|
|
34
|
+
- Use arrow functions for callbacks and short functions
|
|
35
|
+
- Prefer `for...of` loops over `.forEach()` and indexed `for` loops
|
|
36
|
+
- Use optional chaining (`?.`) and nullish coalescing (`??`) for safer property access
|
|
37
|
+
- Prefer template literals over string concatenation
|
|
38
|
+
- Use destructuring for object and array assignments
|
|
39
|
+
- Use `const` by default, `let` only when reassignment is needed, never `var`
|
|
40
|
+
|
|
41
|
+
### Async & Promises
|
|
42
|
+
|
|
43
|
+
- Always `await` promises in async functions - don't forget to use the return value
|
|
44
|
+
- Use `async/await` syntax instead of promise chains for better readability
|
|
45
|
+
- Handle errors appropriately in async code with try-catch blocks
|
|
46
|
+
- Don't use async functions as Promise executors
|
|
47
|
+
|
|
48
|
+
### React & JSX
|
|
49
|
+
|
|
50
|
+
- Use function components over class components
|
|
51
|
+
- Call hooks at the top level only, never conditionally
|
|
52
|
+
- Specify all dependencies in hook dependency arrays correctly
|
|
53
|
+
- Use the `key` prop for elements in iterables (prefer unique IDs over array indices)
|
|
54
|
+
- Nest children between opening and closing tags instead of passing as props
|
|
55
|
+
- Don't define components inside other components
|
|
56
|
+
- Use semantic HTML and ARIA attributes for accessibility:
|
|
57
|
+
- Provide meaningful alt text for images
|
|
58
|
+
- Use proper heading hierarchy
|
|
59
|
+
- Add labels for form inputs
|
|
60
|
+
- Include keyboard event handlers alongside mouse events
|
|
61
|
+
- Use semantic elements (`<button>`, `<nav>`, etc.) instead of divs with roles
|
|
62
|
+
|
|
63
|
+
### Error Handling & Debugging
|
|
64
|
+
|
|
65
|
+
- Remove `console.log`, `debugger`, and `alert` statements from production code
|
|
66
|
+
- Throw `Error` objects with descriptive messages, not strings or other values
|
|
67
|
+
- Use `try-catch` blocks meaningfully - don't catch errors just to rethrow them
|
|
68
|
+
- Prefer early returns over nested conditionals for error cases
|
|
69
|
+
|
|
70
|
+
### Code Organization
|
|
71
|
+
|
|
72
|
+
- Keep functions focused and under reasonable cognitive complexity limits
|
|
73
|
+
- Extract complex conditions into well-named boolean variables
|
|
74
|
+
- Use early returns to reduce nesting
|
|
75
|
+
- Prefer simple conditionals over nested ternary operators
|
|
76
|
+
- Group related code together and separate concerns
|
|
77
|
+
|
|
78
|
+
### Security
|
|
79
|
+
|
|
80
|
+
- Add `rel="noopener"` when using `target="_blank"` on links
|
|
81
|
+
- Avoid `dangerouslySetInnerHTML` unless absolutely necessary
|
|
82
|
+
- Don't use `eval()` or assign directly to `document.cookie`
|
|
83
|
+
- Validate and sanitize user input
|
|
84
|
+
|
|
85
|
+
### Performance
|
|
86
|
+
|
|
87
|
+
- Avoid spread syntax in accumulators within loops
|
|
88
|
+
- Use top-level regex literals instead of creating them in loops
|
|
89
|
+
- Prefer specific imports over namespace imports
|
|
90
|
+
- Avoid barrel files (index files that re-export everything)
|
|
91
|
+
- Use proper image components (e.g., Next.js `<Image>`) over `<img>` tags
|
|
92
|
+
|
|
93
|
+
### Framework-Specific Guidance
|
|
94
|
+
|
|
95
|
+
**Next.js:**
|
|
96
|
+
- Use Next.js `<Image>` component for images
|
|
97
|
+
- Use `next/head` or App Router metadata API for head elements
|
|
98
|
+
- Use Server Components for async data fetching instead of async Client Components
|
|
99
|
+
|
|
100
|
+
**React 19+:**
|
|
101
|
+
- Use ref as a prop instead of `React.forwardRef`
|
|
102
|
+
|
|
103
|
+
**Solid/Svelte/Vue/Qwik:**
|
|
104
|
+
- Use `class` and `for` attributes (not `className` or `htmlFor`)
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Testing
|
|
109
|
+
|
|
110
|
+
- Write assertions inside `it()` or `test()` blocks
|
|
111
|
+
- Avoid done callbacks in async tests - use async/await instead
|
|
112
|
+
- Don't use `.only` or `.skip` in committed code
|
|
113
|
+
- Keep test suites reasonably flat - avoid excessive `describe` nesting
|
|
114
|
+
|
|
115
|
+
## When Biome Can't Help
|
|
116
|
+
|
|
117
|
+
Biome's linter will catch most issues automatically. Focus your attention on:
|
|
118
|
+
|
|
119
|
+
1. **Business logic correctness** - Biome can't validate your algorithms
|
|
120
|
+
2. **Meaningful naming** - Use descriptive names for functions, variables, and types
|
|
121
|
+
3. **Architecture decisions** - Component structure, data flow, and API design
|
|
122
|
+
4. **Edge cases** - Handle boundary conditions and error states
|
|
123
|
+
5. **User experience** - Accessibility, performance, and usability considerations
|
|
124
|
+
6. **Documentation** - Add comments for complex logic, but prefer self-documenting code
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
Most formatting and common issues are automatically fixed by Biome. Run `npx ultracite@6.3.3 fix` before committing to ensure compliance.
|
|
@@ -11,7 +11,7 @@ export async function generateTitleFromUserMessage({
|
|
|
11
11
|
message: ChatMessage;
|
|
12
12
|
}) {
|
|
13
13
|
const { text: title } = await generateText({
|
|
14
|
-
model: await getLanguageModel(config.
|
|
14
|
+
model: await getLanguageModel(config.ai.workflows.title),
|
|
15
15
|
system: `\n
|
|
16
16
|
- you will generate a short title based on the first message a user begins a conversation with
|
|
17
17
|
- ensure it is not more than 40 characters long
|
|
@@ -43,17 +43,18 @@ export async function GET(
|
|
|
43
43
|
return new ChatSDKError("bad_request:api").toResponse();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const [messageWithParts, session, chat] = await Promise.all([
|
|
47
|
+
getChatMessageWithPartsById({ id: messageId }),
|
|
48
|
+
auth.api.getSession({ headers: await headers() }),
|
|
49
|
+
getChatById({ id: chatId }),
|
|
50
|
+
]);
|
|
51
|
+
|
|
48
52
|
if (!messageWithParts || messageWithParts.chatId !== chatId) {
|
|
49
53
|
return new ChatSDKError("not_found:stream").toResponse();
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
// Validate chat ownership
|
|
53
|
-
const session = await auth.api.getSession({ headers: await headers() });
|
|
54
56
|
const userId = session?.user?.id || null;
|
|
55
57
|
|
|
56
|
-
const chat = await getChatById({ id: chatId });
|
|
57
58
|
if (!chat) {
|
|
58
59
|
return new ChatSDKError("not_found:chat").toResponse();
|
|
59
60
|
}
|
|
@@ -415,7 +415,7 @@ async function createChatStream({
|
|
|
415
415
|
const responseMessages = response.messages;
|
|
416
416
|
|
|
417
417
|
// Generate and stream follow-up suggestions
|
|
418
|
-
if (config.
|
|
418
|
+
if (config.ai.tools.followupSuggestions.enabled) {
|
|
419
419
|
const followupSuggestionsResult = generateFollowupSuggestions([
|
|
420
420
|
...contextForLLM,
|
|
421
421
|
...responseMessages,
|
|
@@ -826,14 +826,19 @@ export async function POST(request: NextRequest) {
|
|
|
826
826
|
const explicitlyRequestedTools =
|
|
827
827
|
determineExplicitlyRequestedTools(selectedTool);
|
|
828
828
|
|
|
829
|
-
const contextResult = await
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
829
|
+
const [contextResult, mcpConnectors] = await Promise.all([
|
|
830
|
+
prepareRequestContext({
|
|
831
|
+
userMessage,
|
|
832
|
+
chatId,
|
|
833
|
+
isAnonymous,
|
|
834
|
+
anonymousPreviousMessages,
|
|
835
|
+
modelDefinition,
|
|
836
|
+
explicitlyRequestedTools,
|
|
837
|
+
}),
|
|
838
|
+
config.ai.tools.mcp.enabled && userId && !isAnonymous
|
|
839
|
+
? getMcpConnectorsByUserId({ userId })
|
|
840
|
+
: Promise.resolve([]),
|
|
841
|
+
]);
|
|
837
842
|
|
|
838
843
|
if (contextResult.error) {
|
|
839
844
|
return contextResult.error;
|
|
@@ -841,12 +846,6 @@ export async function POST(request: NextRequest) {
|
|
|
841
846
|
|
|
842
847
|
const { previousMessages, allowedTools } = contextResult;
|
|
843
848
|
|
|
844
|
-
// Fetch MCP connectors for authenticated users (only if MCP integration enabled)
|
|
845
|
-
const mcpConnectors: McpConnector[] =
|
|
846
|
-
config.features.mcp && userId && !isAnonymous
|
|
847
|
-
? await getMcpConnectorsByUserId({ userId })
|
|
848
|
-
: [];
|
|
849
|
-
|
|
850
849
|
// Create AbortController with timeout
|
|
851
850
|
const abortController = new AbortController();
|
|
852
851
|
const timeoutId = setTimeout(() => {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { AnonymousSessionInit } from "@/components/anonymous-session-init";
|
|
4
4
|
import { ChatIdProvider } from "@/providers/chat-id-provider";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
interface ChatProvidersProps {
|
|
7
7
|
children: React.ReactNode;
|
|
8
|
-
}
|
|
8
|
+
}
|
|
9
9
|
|
|
10
10
|
export function ChatProviders({ children }: ChatProvidersProps) {
|
|
11
11
|
return (
|
|
@@ -19,17 +19,18 @@ export default async function ChatLayout({
|
|
|
19
19
|
}: {
|
|
20
20
|
children: React.ReactNode;
|
|
21
21
|
}) {
|
|
22
|
-
const cookieStore = await
|
|
23
|
-
|
|
22
|
+
const [cookieStore, headersRes, chatModels] = await Promise.all([
|
|
23
|
+
cookies(),
|
|
24
|
+
headers(),
|
|
25
|
+
getChatModels(),
|
|
26
|
+
]);
|
|
27
|
+
const session = await auth.api.getSession({ headers: headersRes });
|
|
24
28
|
const isCollapsed = cookieStore.get("sidebar:state")?.value !== "true";
|
|
25
29
|
|
|
26
30
|
const cookieModel = cookieStore.get("chat-model")?.value;
|
|
27
31
|
const isAnonymous = !session?.user;
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
const chatModels = await getChatModels();
|
|
31
|
-
|
|
32
|
-
const default_chat_model = config.models.defaults.chat;
|
|
33
|
+
const default_chat_model = config.ai.workflows.chat;
|
|
33
34
|
// Check if the model from cookie exists in available models
|
|
34
35
|
let defaultModel: AppModelId =
|
|
35
36
|
(cookieModel as AppModelId) ?? default_chat_model;
|
|
@@ -37,9 +37,10 @@ export async function GET(request: NextRequest) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async function cleanupOrphanedAttachments() {
|
|
40
|
-
// Skip cleanup if neither
|
|
41
|
-
const
|
|
42
|
-
|
|
40
|
+
// Skip cleanup if neither image tool nor attachments is enabled
|
|
41
|
+
const imageGenerationEnabled = config.ai.tools.image.enabled;
|
|
42
|
+
const attachmentsEnabled = config.features.attachments;
|
|
43
|
+
if (!(imageGenerationEnabled || attachmentsEnabled)) {
|
|
43
44
|
return { deletedCount: 0, deletedUrls: [], skipped: true };
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -425,19 +425,19 @@
|
|
|
425
425
|
|
|
426
426
|
.lexical-content-editable {
|
|
427
427
|
position: relative;
|
|
428
|
-
|
|
429
|
-
user-select: text;
|
|
430
|
-
white-space: pre-wrap;
|
|
431
|
-
word-break: break-word;
|
|
428
|
+
overflow-y: auto;
|
|
432
429
|
font-family: inherit;
|
|
433
430
|
font-size: inherit;
|
|
434
431
|
line-height: 1.5;
|
|
435
432
|
color: inherit;
|
|
436
|
-
|
|
437
|
-
|
|
433
|
+
word-break: break-word;
|
|
434
|
+
white-space: pre-wrap;
|
|
438
435
|
resize: none;
|
|
439
|
-
|
|
436
|
+
user-select: text;
|
|
437
|
+
outline: none;
|
|
440
438
|
scrollbar-width: thin;
|
|
439
|
+
background: transparent;
|
|
440
|
+
border: none;
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
.lexical-content-editable::-webkit-scrollbar {
|
|
@@ -458,15 +458,15 @@
|
|
|
458
458
|
}
|
|
459
459
|
|
|
460
460
|
.lexical-placeholder {
|
|
461
|
-
color: var(--muted-foreground);
|
|
462
|
-
pointer-events: none;
|
|
463
|
-
user-select: none;
|
|
464
461
|
position: absolute;
|
|
465
462
|
top: 0;
|
|
466
|
-
left: 0;
|
|
467
463
|
right: 0;
|
|
468
464
|
bottom: 0;
|
|
465
|
+
left: 0;
|
|
469
466
|
z-index: 1;
|
|
467
|
+
color: var(--muted-foreground);
|
|
468
|
+
pointer-events: none;
|
|
469
|
+
user-select: none;
|
|
470
470
|
}
|
|
471
471
|
|
|
472
472
|
.lexical-root {
|
|
@@ -474,8 +474,8 @@
|
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
.lexical-root p {
|
|
477
|
-
margin: 0;
|
|
478
477
|
padding: 0;
|
|
478
|
+
margin: 0;
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
.lexical-root p:first-child {
|
|
@@ -505,25 +505,25 @@
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
.editor-container {
|
|
508
|
-
background: #fff;
|
|
509
|
-
margin: 20px auto 20px auto;
|
|
510
|
-
border-radius: 2px;
|
|
511
|
-
max-width: 600px;
|
|
512
|
-
color: #000;
|
|
513
508
|
position: relative;
|
|
514
|
-
|
|
509
|
+
max-width: 600px;
|
|
510
|
+
margin: 20px auto 20px auto;
|
|
515
511
|
font-weight: 400;
|
|
512
|
+
line-height: 20px;
|
|
513
|
+
color: #000;
|
|
516
514
|
text-align: left;
|
|
515
|
+
background: #fff;
|
|
516
|
+
border-radius: 2px;
|
|
517
517
|
}
|
|
518
518
|
|
|
519
519
|
.editor-input {
|
|
520
|
-
resize: none;
|
|
521
|
-
font-size: 15px;
|
|
522
520
|
position: relative;
|
|
523
|
-
tab-size: 1;
|
|
524
|
-
outline: 0;
|
|
525
521
|
padding: 10px 10px 5px;
|
|
522
|
+
font-size: 15px;
|
|
523
|
+
tab-size: 1;
|
|
526
524
|
caret-color: currentColor;
|
|
525
|
+
resize: none;
|
|
526
|
+
outline: 0;
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
/*
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"extends": ["ultracite/core", "ultracite/react", "ultracite/next"],
|
|
3
3
|
"files": {
|
|
4
4
|
"includes": [
|
|
5
|
-
"**/*",
|
|
6
5
|
"!.next",
|
|
7
|
-
"!.next",
|
|
8
|
-
"!.devtools",
|
|
9
6
|
"!.devtools",
|
|
7
|
+
"!node_modules",
|
|
8
|
+
"!playwright-report",
|
|
9
|
+
"!test-results",
|
|
10
10
|
"!components/ui",
|
|
11
11
|
"!components/ai-elements",
|
|
12
12
|
"!components/streamdown",
|
|
@@ -45,14 +45,7 @@ const config = {
|
|
|
45
45
|
paymentProcessors: [],
|
|
46
46
|
},
|
|
47
47
|
features: {
|
|
48
|
-
sandbox: true, // Vercel-native, no key needed
|
|
49
|
-
webSearch: true, // Requires TAVILY_API_KEY or FIRECRAWL_API_KEY
|
|
50
|
-
urlRetrieval: true, // Requires FIRECRAWL_API_KEY
|
|
51
|
-
deepResearch: true, // Requires webSearch
|
|
52
|
-
mcp: true, // Requires MCP_ENCRYPTION_KEY
|
|
53
|
-
imageGeneration: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
54
48
|
attachments: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
55
|
-
followupSuggestions: true,
|
|
56
49
|
},
|
|
57
50
|
legal: {
|
|
58
51
|
minimumAge: 13,
|
|
@@ -74,7 +67,7 @@ const config = {
|
|
|
74
67
|
github: true, // Requires AUTH_GITHUB_ID + AUTH_GITHUB_SECRET
|
|
75
68
|
vercel: true, // Requires VERCEL_APP_CLIENT_ID + VERCEL_APP_CLIENT_SECRET
|
|
76
69
|
},
|
|
77
|
-
|
|
70
|
+
ai: {
|
|
78
71
|
gateway: "vercel",
|
|
79
72
|
providerOrder: ["openai", "google", "anthropic", "xai"],
|
|
80
73
|
disabledModels: ["morph/morph-v3-large", "morph/morph-v3-fast"],
|
|
@@ -100,22 +93,56 @@ const config = {
|
|
|
100
93
|
"openai/gpt-5-nano",
|
|
101
94
|
"anthropic/claude-haiku-4.5",
|
|
102
95
|
],
|
|
103
|
-
|
|
96
|
+
workflows: {
|
|
104
97
|
chat: "openai/gpt-5-mini",
|
|
105
98
|
title: "openai/gpt-5-nano",
|
|
106
99
|
pdf: "openai/gpt-5-mini",
|
|
107
|
-
artifact: "openai/gpt-5-nano",
|
|
108
|
-
artifactSuggestion: "openai/gpt-5-mini",
|
|
109
|
-
followupSuggestions: "openai/gpt-5-nano",
|
|
110
|
-
suggestions: "openai/gpt-5-mini",
|
|
111
|
-
polishText: "openai/gpt-5-mini",
|
|
112
|
-
formatSheet: "openai/gpt-5-mini",
|
|
113
|
-
analyzeSheet: "openai/gpt-5-mini",
|
|
114
|
-
codeEdits: "openai/gpt-5-mini",
|
|
115
100
|
chatImageCompatible: "openai/gpt-4o-mini",
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
101
|
+
},
|
|
102
|
+
tools: {
|
|
103
|
+
webSearch: {
|
|
104
|
+
enabled: true, // Requires TAVILY_API_KEY or FIRECRAWL_API_KEY
|
|
105
|
+
},
|
|
106
|
+
urlRetrieval: {
|
|
107
|
+
enabled: true, // Requires FIRECRAWL_API_KEY
|
|
108
|
+
},
|
|
109
|
+
codeExecution: {
|
|
110
|
+
enabled: true, // Vercel-native, no key needed
|
|
111
|
+
},
|
|
112
|
+
mcp: {
|
|
113
|
+
enabled: true, // Requires MCP_ENCRYPTION_KEY
|
|
114
|
+
},
|
|
115
|
+
followupSuggestions: {
|
|
116
|
+
enabled: true,
|
|
117
|
+
default: "openai/gpt-5-nano",
|
|
118
|
+
},
|
|
119
|
+
text: {
|
|
120
|
+
polish: "openai/gpt-5-mini",
|
|
121
|
+
},
|
|
122
|
+
sheet: {
|
|
123
|
+
format: "openai/gpt-5-mini",
|
|
124
|
+
analyze: "openai/gpt-5-mini",
|
|
125
|
+
},
|
|
126
|
+
code: {
|
|
127
|
+
edits: "openai/gpt-5-mini",
|
|
128
|
+
},
|
|
129
|
+
image: {
|
|
130
|
+
enabled: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
131
|
+
default: "google/gemini-3-pro-image",
|
|
132
|
+
},
|
|
133
|
+
video: {
|
|
134
|
+
enabled: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
135
|
+
default: "xai/grok-imagine-video",
|
|
136
|
+
},
|
|
137
|
+
deepResearch: {
|
|
138
|
+
enabled: true, // Requires webSearch
|
|
139
|
+
defaultModel: "google/gemini-2.5-flash-lite",
|
|
140
|
+
finalReportModel: "google/gemini-3-flash",
|
|
141
|
+
allowClarification: true,
|
|
142
|
+
maxResearcherIterations: 1,
|
|
143
|
+
maxConcurrentResearchUnits: 2,
|
|
144
|
+
maxSearchQueries: 2,
|
|
145
|
+
},
|
|
119
146
|
},
|
|
120
147
|
},
|
|
121
148
|
anonymous: {
|
|
@@ -29,7 +29,6 @@ export function AnonymousSessionInit() {
|
|
|
29
29
|
const trpc = useTRPC();
|
|
30
30
|
|
|
31
31
|
useEffect(() => {
|
|
32
|
-
// Only initialize for non-authenticated users after session is loaded
|
|
33
32
|
if (isPending) {
|
|
34
33
|
return;
|
|
35
34
|
}
|
|
@@ -37,34 +36,27 @@ export function AnonymousSessionInit() {
|
|
|
37
36
|
return;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
// Get raw session data and validate/migrate
|
|
41
39
|
const existingSession = getAnonymousSession();
|
|
42
40
|
|
|
43
41
|
if (existingSession) {
|
|
44
|
-
// Validate the existing session schema
|
|
45
42
|
if (!isValidAnonymousSession(existingSession)) {
|
|
46
43
|
console.warn(
|
|
47
44
|
"Invalid session schema detected during init, clearing and creating new session"
|
|
48
45
|
);
|
|
49
46
|
clearAnonymousSession();
|
|
50
|
-
|
|
51
|
-
setAnonymousSession(newSession);
|
|
52
|
-
// Ensure UI refetches credits after creating a valid anonymous session
|
|
47
|
+
setAnonymousSession(createAnonymousSession());
|
|
53
48
|
queryClient.invalidateQueries({
|
|
54
49
|
queryKey: trpc.credits.getAvailableCredits.queryKey(),
|
|
55
50
|
});
|
|
56
51
|
return;
|
|
57
52
|
}
|
|
58
53
|
} else {
|
|
59
|
-
|
|
60
|
-
const newSession = createAnonymousSession();
|
|
61
|
-
setAnonymousSession(newSession);
|
|
62
|
-
// Ensure UI refetches credits after first-time session creation
|
|
54
|
+
setAnonymousSession(createAnonymousSession());
|
|
63
55
|
queryClient.invalidateQueries({
|
|
64
56
|
queryKey: trpc.credits.getAvailableCredits.queryKey(),
|
|
65
57
|
});
|
|
66
58
|
}
|
|
67
|
-
}, [
|
|
59
|
+
}, [isPending, queryClient, trpc, session?.user]);
|
|
68
60
|
|
|
69
|
-
return null;
|
|
61
|
+
return null;
|
|
70
62
|
}
|
|
@@ -7,16 +7,16 @@ import { Button } from "./ui/button";
|
|
|
7
7
|
import { Toggle } from "./ui/toggle";
|
|
8
8
|
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface ArtifactActionsProps {
|
|
11
11
|
artifact: UIArtifact;
|
|
12
|
-
handleVersionChange: (type: "next" | "prev" | "toggle" | "latest") => void;
|
|
13
12
|
currentVersionIndex: number;
|
|
13
|
+
handleVersionChange: (type: "next" | "prev" | "toggle" | "latest") => void;
|
|
14
14
|
isCurrentVersion: boolean;
|
|
15
|
-
|
|
15
|
+
isReadonly: boolean;
|
|
16
16
|
metadata: any;
|
|
17
|
+
mode: "edit" | "diff";
|
|
17
18
|
setMetadata: Dispatch<SetStateAction<any>>;
|
|
18
|
-
|
|
19
|
-
};
|
|
19
|
+
}
|
|
20
20
|
|
|
21
21
|
function PureArtifactActions({
|
|
22
22
|
artifact,
|
|
@@ -34,16 +34,16 @@ import { VersionFooter } from "./version-footer";
|
|
|
34
34
|
|
|
35
35
|
export const artifactDefinitions = [textArtifact, codeArtifact, sheetArtifact];
|
|
36
36
|
|
|
37
|
-
export
|
|
38
|
-
|
|
37
|
+
export interface UIArtifact {
|
|
38
|
+
content: string;
|
|
39
|
+
date?: string;
|
|
39
40
|
documentId: string;
|
|
41
|
+
isVisible: boolean;
|
|
40
42
|
kind: ArtifactKind;
|
|
41
|
-
content: string;
|
|
42
43
|
messageId: string;
|
|
43
|
-
isVisible: boolean;
|
|
44
44
|
status: "streaming" | "idle";
|
|
45
|
-
|
|
46
|
-
}
|
|
45
|
+
title: string;
|
|
46
|
+
}
|
|
47
47
|
|
|
48
48
|
function PureArtifactPanel({
|
|
49
49
|
isReadonly,
|
|
@@ -48,7 +48,7 @@ const PureAssistantMessage = ({
|
|
|
48
48
|
key={`action-${messageId}`}
|
|
49
49
|
messageId={messageId}
|
|
50
50
|
/>
|
|
51
|
-
{isReadonly || !config.
|
|
51
|
+
{isReadonly || !config.ai.tools.followupSuggestions.enabled ? null : (
|
|
52
52
|
<FollowUpSuggestionsParts messageId={messageId} />
|
|
53
53
|
)}
|
|
54
54
|
</MessageContent>
|
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
import { useSidebar } from "@/components/ui/sidebar";
|
|
11
11
|
import { cn } from "@/lib/utils";
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
interface ChatLayoutContextValue {
|
|
14
14
|
isSecondaryPanelVisible: boolean;
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
16
|
|
|
17
17
|
const ChatLayoutContext = createContext<ChatLayoutContextValue | null>(null);
|
|
18
18
|
|
|
@@ -4,15 +4,16 @@ import {
|
|
|
4
4
|
Images,
|
|
5
5
|
type LucideIcon,
|
|
6
6
|
Telescope,
|
|
7
|
+
Video,
|
|
7
8
|
} from "lucide-react";
|
|
8
9
|
import type { UiToolName } from "@/lib/ai/types";
|
|
9
10
|
import { config } from "@/lib/config";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
name: string;
|
|
12
|
+
interface ToolDefinition {
|
|
13
13
|
icon: LucideIcon;
|
|
14
|
+
name: string;
|
|
14
15
|
shortName: string;
|
|
15
|
-
}
|
|
16
|
+
}
|
|
16
17
|
|
|
17
18
|
export const toolDefinitions: Record<UiToolName, ToolDefinition> = {
|
|
18
19
|
webSearch: { name: "Web Search", icon: GlobeIcon, shortName: "Search" },
|
|
@@ -22,6 +23,7 @@ export const toolDefinitions: Record<UiToolName, ToolDefinition> = {
|
|
|
22
23
|
shortName: "Research",
|
|
23
24
|
},
|
|
24
25
|
generateImage: { name: "Create an image", icon: Images, shortName: "Image" },
|
|
26
|
+
generateVideo: { name: "Create a video", icon: Video, shortName: "Video" },
|
|
25
27
|
createTextDocument: { name: "Canvas", icon: Edit3, shortName: "Canvas" },
|
|
26
28
|
createCodeDocument: { name: "Canvas", icon: Edit3, shortName: "Canvas" },
|
|
27
29
|
createSheetDocument: { name: "Canvas", icon: Edit3, shortName: "Canvas" },
|
|
@@ -37,10 +39,11 @@ export const toolDefinitions: Record<UiToolName, ToolDefinition> = {
|
|
|
37
39
|
export const enabledTools: UiToolName[] = [
|
|
38
40
|
// Canvas tools are always available
|
|
39
41
|
"createTextDocument",
|
|
40
|
-
// Web search
|
|
41
|
-
...(config.
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
// Web search tool
|
|
43
|
+
...(config.ai.tools.webSearch.enabled ? (["webSearch"] as const) : []),
|
|
44
|
+
// Deep research tool
|
|
45
|
+
...(config.ai.tools.deepResearch.enabled ? (["deepResearch"] as const) : []),
|
|
44
46
|
// Image generation requires imageGeneration integration
|
|
45
|
-
...(config.
|
|
47
|
+
...(config.ai.tools.image.enabled ? (["generateImage"] as const) : []),
|
|
48
|
+
...(config.ai.tools.video.enabled ? (["generateVideo"] as const) : []),
|
|
46
49
|
];
|
|
@@ -4,14 +4,14 @@ import { Pencil, PinIcon, Trash2 } from "lucide-react";
|
|
|
4
4
|
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
|
5
5
|
import { ShareMenuItem } from "@/components/upgrade-cta/share-menu-item";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
interface ChatMenuItemsProps {
|
|
8
8
|
isPinned: boolean;
|
|
9
|
-
onRename: () => void;
|
|
10
|
-
onTogglePin: () => void;
|
|
11
9
|
onDelete: () => void;
|
|
10
|
+
onRename: () => void;
|
|
12
11
|
onShare?: () => void;
|
|
12
|
+
onTogglePin: () => void;
|
|
13
13
|
showShare?: boolean;
|
|
14
|
-
}
|
|
14
|
+
}
|
|
15
15
|
|
|
16
16
|
export function ChatMenuItems({
|
|
17
17
|
isPinned,
|