-
Notifications
You must be signed in to change notification settings - Fork 585
[Package] @thirdweb-dev/ai-sdk-provider #7922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Package] @thirdweb-dev/ai-sdk-provider #7922
Conversation
🦋 Changeset detectedLatest commit: 931a85e The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a new @thirdweb-dev/ai-sdk-provider package (provider, tools, types, build configs), integrates a streaming AI chat API and client UI in the playground, introduces multiple chat UI primitives, adjusts EIP-1193 wallet connect behavior, updates WalletConnect methods, bumps shiki across packages, and adds changesets and docs. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as Playground UI (ChatContainer)
participant API as Next.js /api/chat
participant Provider as ThirdwebProvider
participant TWAPI as Thirdweb API
User->>UI: submit prompt (messages + session_id)
UI->>API: POST /api/chat (stream: true)
API->>Provider: provider.chat(...).doStream(messages, context)
Provider->>TWAPI: POST /ai/chat (stream: true)
TWAPI-->>Provider: SSE stream (init, delta, action, done)
Provider-->>API: stream parts (text / reasoning / tool-call / finish / metadata)
API-->>UI: stream UI-formatted parts (session_id on finish)
UI->>User: render text, reasoning, and tool prompts
sequenceDiagram
autonumber
actor User
participant UI as ChatContainer
participant Wallet as Wallet/Signer
participant API as /api/chat
UI-->>UI: receive tool-call (e.g., sign_transaction)
UI->>Wallet: prepareTransaction / sign/send
Wallet-->>UI: tx hash or error
UI->>API: addToolResult(tool="sign_transaction", output={transaction_hash}) + follow-up message (session_id)
API-->>UI: continued streamed response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
c237ff2
to
5ac1076
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 17
🧹 Nitpick comments (35)
package.json (1)
65-65
: Ai-SDK-Provider Dev Script Verified & Playground:Build Update
- The
packages/ai-sdk-provider/package.json
does define adev
script (tsc … --watch
), so yourturbo run dev --filter=…/packages/ai-sdk-provider
filter is effective.- Currently, the root
playground:build
script only runsIf you’d like to build the AI SDK provider alongside the app, consider updating it to:turbo run build --filter=./apps/playground-web"playground:build": "turbo run build --filter=./apps/playground-web \ + --filter=./packages/ai-sdk-provider"
apps/playground-web/src/app/ai/components/ChatPageContent.tsx (2)
794-799
: Guard presence text access to avoid undefined render.When the presence message exists but
texts
is empty (initial state), this rendersundefined
.Apply:
- {message.type === "presence" && ( + {message.type === "presence" && ( <div className="text-muted-foreground text-sm italic flex items-center gap-2"> - {message.texts[message.texts.length - 1]} + {message.texts?.length + ? message.texts[message.texts.length - 1] + : "Processing..."} </div> )}
601-613
: Prefer a stable key; avoid stringifying the whole message object.
key={
${index}-${message}}
stringifies the object to "[object Object]" and pairs with the index. Use a stable id (e.g.,request_id
) with index fallback to reduce re-mounts.Example:
- key={`${index}-${message}`} + key={`msg-${"request_id" in message && message.request_id ? message.request_id : index}`}apps/playground-web/src/components/code/code-example.tsx (1)
58-69
: Export looks good; add explicit return type and className support for overrides.Matches repo conventions for exported UI helpers and improves reuse.
Apply:
-export function TabName(props: { - name: string; - icon: React.FC<{ className: string }>; -}) { - return ( - <div className="flex items-center gap-2 border-b p-4 text-muted-foreground text-sm"> +export function TabName(props: { + name: string; + icon: React.FC<{ className: string }>; + className?: string; +}): JSX.Element { + return ( + <div className={`flex items-center gap-2 border-b p-4 text-muted-foreground text-sm ${props.className ?? ""}`}> <props.icon className="size-4" /> {props.name} </div> ); }packages/thirdweb/src/adapters/eip1193/to-eip1193.ts (1)
133-136
: Avoid console.error in library code.Prefer routing through an internal logger or removing the log to prevent noisy consumer consoles.
apps/playground-web/src/components/loader.tsx (1)
86-96
: Improve a11y for the spinner wrapper.Expose progress semantics to AT.
- <div + <div className={cn( 'inline-flex animate-spin items-center justify-center', className )} + role="status" + aria-live="polite" + aria-busy="true" {...props} >apps/playground-web/src/components/conversation.tsx (3)
13-19
: Announce new messages to screen readers.Add live-region attributes to the log container.
<StickToBottom className={cn('relative flex-1 overflow-y-auto', className)} initial="smooth" resize="smooth" role="log" + aria-live="polite" + aria-relevant="additions text" {...props} />
47-59
: Add an accessible label to the scroll button.Improve discoverability for AT users.
<Button className={cn( 'absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full', className )} onClick={handleScrollToBottom} size="icon" type="button" variant="outline" + aria-label="Scroll to bottom" {...props} > - <ArrowDownIcon className="size-4" /> + <ArrowDownIcon className="size-4" aria-hidden="true" /> </Button>
12-20
: Add explicit return types for exported components.Matches repo TS guidelines for explicit returns.
-export const Conversation = ({ className, ...props }: ConversationProps) => ( +export const Conversation = ({ className, ...props }: ConversationProps): JSX.Element => ( ... ); -export const ConversationContent = ({ className, ...props }: ConversationContentProps) => ( +export const ConversationContent = ({ className, ...props }: ConversationContentProps): JSX.Element => ( ... ); -export const ConversationScrollButton = ({ className, ...props }: ConversationScrollButtonProps) => { +export const ConversationScrollButton = ({ className, ...props }: ConversationScrollButtonProps): JSX.Element | null => { ... }Also applies to: 26-31, 35-62
apps/playground-web/src/components/message.tsx (3)
36-41
: Remove stray Tailwind class "is-user:dark".This token doesn’t map to a valid variant and has no effect.
- 'group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground', - 'is-user:dark', + 'group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground',
10-12
: Clarify allowed roles forfrom
.If only 'user' | 'assistant' are expected, narrow the type to avoid unexpected layout for other roles.
-export type MessageProps = HTMLAttributes<HTMLDivElement> & { - from: UIMessage['role']; -}; +type ChatRole = Extract<UIMessage['role'], 'user' | 'assistant'>; +export type MessageProps = HTMLAttributes<HTMLDivElement> & { from: ChatRole };
58-61
: Improve avatar a11y and fallback initials; drop redundant margins.
- Use name-based alt when available; empty alt when decorative.
- Compute initials from up to two words and uppercase.
-export const MessageAvatar = ({ - src, - name, - className, - ...props -}: MessageAvatarProps) => ( - <Avatar className={cn('size-8 ring-1 ring-border', className)} {...props}> - <AvatarImage alt="" className="mt-0 mb-0" src={src} /> - <AvatarFallback>{name?.slice(0, 2) || 'ME'}</AvatarFallback> - </Avatar> -); +export const MessageAvatar = ({ + src, + name, + className, + ...props +}: MessageAvatarProps) => { + const initials = + name + ?.trim() + .split(/\s+/) + .slice(0, 2) + .map((p) => p[0]?.toUpperCase()) + .join('') || 'ME'; + return ( + <Avatar className={cn('size-8 ring-1 ring-border', className)} {...props}> + <AvatarImage alt={name ? `${name} avatar` : ''} src={src} /> + <AvatarFallback>{initials}</AvatarFallback> + </Avatar> + ); +};apps/playground-web/src/components/reasoning.tsx (3)
79-91
: Comment says auto-open on start, but code only auto-closes. Implement auto-open or update the comment.If intended, auto-open when streaming starts.
-// Auto-open when streaming starts, auto-close when streaming ends (once only) +// Auto-open when streaming starts, auto-close when streaming ends (once only) useEffect(() => { - if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { + // auto-open on start + if (isStreaming && !isOpen) { + setIsOpen(true); + } + // auto-close once after finish + if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { // Add a small delay before closing to allow user to see the content const timer = setTimeout(() => { setIsOpen(false); - setHasAutoClosedRef(true); + setHasAutoClosedRef(true); }, AUTO_CLOSE_DELAY); return () => clearTimeout(timer); } }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosedRef]);
64-66
:hasAutoClosedRef
is state, not a ref; useuseRef
for true “once” semantics without re-renders.Less churn and clearer naming.
-const [hasAutoClosedRef, setHasAutoClosedRef] = useState(false); +const hasAutoClosedRef = useRef(false); ... - if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { + if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef.current) { const timer = setTimeout(() => { setIsOpen(false); - setHasAutoClosedRef(true); + hasAutoClosedRef.current = true; }, AUTO_CLOSE_DELAY);Also applies to: 81-86
59-63
: Controlledduration
may not update when parent controls it.When
duration
is controlled via props,setDuration
won’t change the value locally. Ensure the parent updates the prop on stream end, or guardsetDuration
behind uncontrolled mode.-} ); +} ); ... -} else if (startTime !== null) { - setDuration(Math.round((Date.now() - startTime) / MS_IN_S)); +} else if (startTime !== null) { + // only update when uncontrolled + if (durationProp === undefined) { + setDuration(Math.round((Date.now() - startTime) / MS_IN_S)); + }Also applies to: 67-77
packages/ai-sdk-provider/tsconfig.json (2)
9-9
: Remove empty "exclude" to avoid surprising defaults later.An empty array overrides tsconfig’s default excludes. Since "include" already scopes to "src" and "test", drop "exclude" or set explicit dirs if needed.
Apply:
- "exclude": [],
1-13
: Consider moving "extends" to the top for readability.Order is not semantically significant, but keeping "extends" first matches common tsconfig conventions.
Optional tidy-up:
-{ - // This configuration is used for local development and type checking. - "compilerOptions": { +{ + "extends": "./tsconfig.base.json", + // This configuration is used for local development and type checking. + "compilerOptions": { "baseUrl": ".", "paths": { "~test/*": ["./test/src/*"] } }, - "exclude": [], - // This configuration is used for local development and type checking. - "extends": "./tsconfig.base.json", "include": ["src", "test"] }apps/playground-web/package.json (1)
4-4
: Ensure heavy dependencies are lazily loaded in the client bundleWe ran the import-pattern check and confirmed that none of these heavy modules are currently loaded via dynamic import/next/dynamic. To keep the initial payload lean, please refactor all top-level static imports of the following packages to use dynamic loading:
• apps/playground-web/src/app/ai/ai-sdk/page.tsx (lines 119–121)
–import { useChat } from '@ai-sdk/react'
–import { DefaultChatTransport } from 'ai'
• apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (lines 3–5)
–import { useChat } from '@ai-sdk/react'
–import { DefaultChatTransport } from 'ai'
• apps/playground-web/src/components/ui/markdown-renderer.tsx (line 3–4)
–import ReactMarkdown from 'react-markdown'
–import remarkGfm from 'remark-gfm'
• apps/playground-web/src/components/code/code.tsx (line 1)
–import type { BundledLanguage } from 'shiki'
• apps/playground-web/src/components/code/code-example.tsx (line 3)
–import type { BundledLanguage } from 'shiki'
• apps/playground-web/src/components/code/code.client.tsx (line 2)
–import type { BundledLanguage } from 'shiki'
• apps/playground-web/src/components/code/getCodeHtml.tsx (line 3)
–import { codeToHtml } from 'shiki'
• apps/playground-web/src/components/response.tsx (line 5)
–import { Streamdown } from 'streamdown'
• apps/playground-web/src/components/conversation.tsx (line 8)
–import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom'
Example refactor using Next.js dynamic import:
import dynamic from 'next/dynamic'; const ReactMarkdown = dynamic(() => import('react-markdown'), { ssr: false });Or for named exports:
const { useChat } = dynamic( () => import('@ai-sdk/react').then(mod => ({ default: mod.useChat })), { ssr: false }, );Please update these imports to dynamic loading to reduce the client-side bundle size.
packages/ai-sdk-provider/tsconfig.build.json (1)
1-16
: Add outDir and declarations to avoid emitting into src and to ship types.Without outDir, tsc emits alongside sources. Consider generating declarations for consumers.
Proposed:
{ "compilerOptions": { "moduleResolution": "NodeNext", "rootDir": "./src", - "sourceMap": true + "sourceMap": true, + "outDir": "./dist", + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": false },Confirm this matches your build pipeline (tsup/rollup may already handle this).
packages/ai-sdk-provider/tsconfig.base.json (1)
21-24
: Consider matching target to lib (ES2022) for consistency.You use lib ES2022 but target ES2021. Aligning target can avoid downlevel quirks and matches the Error.cause comment.
- "lib": [ - "ES2022", - "DOM" - ], + "lib": ["ES2022", "DOM"], @@ - "target": "ES2021", + "target": "ES2022",Also applies to: 41-41
apps/playground-web/src/app/api/chat/route.ts (4)
1-1
: Add server-only guard at top of server route.Prevents accidental client bundling; aligns with repo convention for server code.
+import "server-only"; import { convertToModelMessages, streamText, UIMessage } from "ai";
8-11
: Avoid relying on NEXT_PUBLIC_ var for server-only baseURL; validate secrets.
- Using a public env var to drive a server baseURL is brittle; the provider has sane defaults.
- Fail fast if THIRDWEB_SECRET_KEY is missing to avoid runtime surprises.
-const thirdweb = createThirdweb({ - baseURL: `https://${process.env.NEXT_PUBLIC_API_URL}`, - secretKey: process.env.THIRDWEB_SECRET_KEY, -}); +if (!process.env.THIRDWEB_SECRET_KEY) { + throw new Error("THIRDWEB_SECRET_KEY is required for the chat API route"); +} +const thirdweb = createThirdweb({ + secretKey: process.env.THIRDWEB_SECRET_KEY, +});
13-17
: Validate request body with zod before use.Hardens the endpoint and avoids undefined access when upstream changes occur.
-export async function POST(req: Request) { - const body = await req.json(); - const { messages, sessionId }: { messages: UIMessage[]; sessionId: string } = - body; +export async function POST(req: Request) { + const Body = z.object({ + messages: z.array(z.any()), + sessionId: z.string().optional().default(""), + }); + const { messages, sessionId } = Body.parse(await req.json());
20-24
: Don’t hardcode chain/from in production-facing examples.Make these configurable via env or request to avoid confusing outputs across networks/wallets.
- context: { - session_id: sessionId, - chain_ids: [8453], - from: "0x2247d5d238d0f9d37184d8332aE0289d1aD9991b", - }, + context: { + session_id: sessionId, + // consider reading chain_ids/from from env or request body + chain_ids: [8453], + from: "0x2247d5d238d0f9d37184d8332aE0289d1aD9991b", + },packages/ai-sdk-provider/src/types.ts (1)
8-12
: Optional: extract metadata type for reuse.Improves reuse across client/server without importing UIMessage everywhere.
export type ThirdwebMessageMetadata = { session_id: string }; export type ThirdwebAiMessage = UIMessage<ThirdwebMessageMetadata, UIDataTypes, { /* … */ }>;apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
153-159
: Replace inline styles with Tailwind per guidelines.Playground uses Tailwind only; avoid inline styles.
- <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full"
132-146
: Guard invalid tx defaults to avoid runtime errors.Empty “to” with “0x” data will fail on many chains. Disable/early-return until input is provided; optionally validate address.
const transactionData: { chain_id: number; to: string; data: `0x${string}`; value: bigint; } = useMemo(() => { return { chain_id: input?.chain_id || 8453, to: input?.to || "", data: (input?.data as `0x${string}`) || "0x", value: input?.value ? BigInt(input.value) : BigInt(0), }; }, [input]); + const canSend = + (transactionData.to && transactionData.to.length > 0) || + transactionData.data !== "0x";And apply:
- <TransactionButton + <TransactionButton + disabled={!canSend}
168-176
: Return tool error on failed send (optional).Improves chat continuity when user cancels or tx fails.
onTransactionSent={(transaction) => { addToolResult({ tool: "sign_transaction", toolCallId, output: { transaction_hash: transaction.transactionHash, }, }); }} + onError={(err) => { + // You could also emit a user-facing message here. + console.error("transaction error", err); + }}apps/playground-web/src/app/ai/ai-sdk/page.tsx (1)
60-101
: Keep server sample minimal and consistent with route.Consider mirroring the actual route (no baseURL; add maxDuration; optional tools) so copy-paste works out of the box.
packages/ai-sdk-provider/package.json (1)
45-55
: Build scripts look good; minor DX improvement.Add a “prepublishOnly” to ensure clean builds on publish.
"scripts": { "dev": "tsc --project ./tsconfig.build.json --module nodenext --moduleResolution nodenext --outDir ./dist/esm --watch", "build": "pnpm clean && pnpm build:cjs && pnpm build:esm && pnpm build:types", + "prepublishOnly": "pnpm build", "build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./dist/cjs --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json", "build:esm": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json", "build:types": "tsc --project ./tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap", "clean": "rimraf dist", "fix": "biome check ./src --fix", "format": "biome format ./src --write", "lint": "biome check ./src" },
packages/ai-sdk-provider/src/provider.ts (2)
397-415
: Don’t mutate reasoning text (remove auto “. ”).Emit exactly what the server sends.
- delta: `${parsed.data}. `, // TODO (ai): this should be in the backends + delta: String(parsed.data),
513-515
: Noisy logging in production.Gate the warn behind a debug flag or env check.
- console.warn("Failed to parse SSE data:", data, e); + if (process.env.NODE_ENV !== "production") { + console.warn("Failed to parse SSE data:", data, e); + }apps/playground-web/src/components/prompt-input.tsx (3)
165-173
: A11y: icon-only submit needs an accessible label.Add
aria-label
/title
when no children are provided.<Button className={cn('gap-1.5 rounded-lg', className)} size={size} type="submit" variant={variant} + aria-label={children ? undefined : 'Send message'} + title={children ? undefined : 'Send'} {...props} >
34-37
: Remove unusedminHeight
/maxHeight
props or wire them via Tailwind tokens.They’re declared but unused; also guideline says “Tailwind only – no inline styles.”
-export type PromptInputTextareaProps = ComponentProps<typeof Textarea> & { - minHeight?: number; - maxHeight?: number; -}; +export type PromptInputTextareaProps = ComponentProps<typeof Textarea>; @@ export const PromptInputTextarea = ({ onChange, className, placeholder = 'What would you like to know?', - minHeight = 48, - maxHeight = 164, ...props }: PromptInputTextareaProps) => {Also applies to: 39-46
24-32
: Optional: forward refs for better composition.Wrap primitives with
forwardRef
to allow parent focus/measure.Also applies to: 68-85, 117-140, 146-175
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (27)
.changeset/many-pants-tease.md
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/ai/components/ChatPageContent.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/playground-web/src/components/reasoning.tsx
apps/playground-web/src/components/conversation.tsx
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/api/chat/route.ts
packages/ai-sdk-provider/src/tools.ts
apps/playground-web/src/components/code/code-example.tsx
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/navLinks.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/page.tsx
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/prompt-input.tsx
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
packages/ai-sdk-provider/src/exports/thirdweb.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/playground-web/src/components/reasoning.tsx
apps/playground-web/src/components/conversation.tsx
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/api/chat/route.ts
packages/ai-sdk-provider/src/tools.ts
apps/playground-web/src/components/code/code-example.tsx
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/navLinks.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/page.tsx
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/prompt-input.tsx
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
packages/ai-sdk-provider/src/exports/thirdweb.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/components/reasoning.tsx
apps/playground-web/src/components/conversation.tsx
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/api/chat/route.ts
apps/playground-web/src/components/code/code-example.tsx
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/navLinks.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/app/ai/ai-sdk/page.tsx
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/prompt-input.tsx
package.json
📄 CodeRabbit inference engine (CLAUDE.md)
Track bundle budgets via
package.json#size-limit
Files:
package.json
🧠 Learnings (23)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to **/*.{ts,tsx} : Re-use shared types from `@/types` or local `types.ts` barrels
Applied to files:
packages/ai-sdk-provider/tsconfig.json
packages/ai-sdk-provider/tsconfig.base.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to biome.json : Biome is the primary linter/formatter; rules are defined in `biome.json`
Applied to files:
packages/ai-sdk-provider/biome.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : EIP-1193, EIP-5792, EIP-7702 standard support in wallet modules
Applied to files:
.changeset/many-pants-tease.md
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Support for in-app wallets (social/email login)
Applied to files:
.changeset/many-pants-tease.md
packages/ai-sdk-provider/README.md
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Smart wallets with account abstraction
Applied to files:
.changeset/many-pants-tease.md
packages/ai-sdk-provider/README.md
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/components/*.client.tsx : Client components must start with `'use client';` before imports.
Applied to files:
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
PR: thirdweb-dev/js#7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Components that listen to user events, animations or live updates.
Applied to files:
apps/playground-web/src/components/response.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/components/prompt-input.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/exports/react.native.ts : React Native specific exports are in `src/exports/react.native.ts`
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/components/code/code-example.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{tsx} : Expose `className` prop on root element of components for overrides
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/components/code/code-example.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/wagmi-adapter/** : Wagmi ecosystem integration is in `packages/wagmi-adapter/`
Applied to files:
packages/ai-sdk-provider/README.md
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Unified `Wallet` and `Account` interfaces in wallet architecture
Applied to files:
packages/ai-sdk-provider/README.md
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Build all packages using `pnpm build` and specific packages with dependencies using `turbo run build --filter=./packages/*`
Applied to files:
package.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Monorepo is managed with Turborepo and pnpm workspaces
Applied to files:
package.json
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Accept a typed `props` object and export a named function (`export function MyComponent()`).
Applied to files:
apps/playground-web/src/components/code/code-example.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `NavLink` for internal navigation with automatic active states in dashboard and playground apps
Applied to files:
apps/playground-web/src/app/navLinks.ts
📚 Learning: 2025-08-07T17:24:31.965Z
Learnt from: MananTank
PR: thirdweb-dev/js#7812
File: apps/dashboard/src/app/(app)/team/~/~project/[[...paths]]/page.tsx:1-11
Timestamp: 2025-08-07T17:24:31.965Z
Learning: In Next.js App Router, page components (page.tsx files) are server components by default and do not require the "server-only" import directive. The "server-only" directive is primarily used for utility functions, API helpers, and data access modules that should never be included in the client bundle.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/playground-web/src/components/prompt-input.tsx
📚 Learning: 2025-06-03T23:44:40.243Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7268
File: packages/thirdweb/src/wallets/in-app/core/wallet/in-app-core.ts:210-216
Timestamp: 2025-06-03T23:44:40.243Z
Learning: EIP7702 wallets do not need special handling for switching chains, unlike EIP4337 wallets which require reconnection when switching chains. In the switchChain method condition, EIP7702 should be intentionally excluded from the reconnection logic.
Applied to files:
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/exports/** : Export everything via `exports/` directory, grouped by feature in the SDK public API
Applied to files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
📚 Learning: 2025-06-17T18:30:52.976Z
Learnt from: MananTank
PR: thirdweb-dev/js#7356
File: apps/nebula/src/app/not-found.tsx:1-1
Timestamp: 2025-06-17T18:30:52.976Z
Learning: In the thirdweb/js project, the React namespace is available for type annotations (like React.FC) without needing to explicitly import React. This is project-specific configuration that differs from typical TypeScript/React setups.
Applied to files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
🧬 Code graph analysis (7)
apps/playground-web/src/components/reasoning.tsx (1)
apps/playground-web/src/components/response.tsx (1)
Response
(9-20)
packages/ai-sdk-provider/src/types.ts (2)
packages/ai-sdk-provider/src/exports/thirdweb.ts (1)
ThirdwebAiMessage
(2-2)packages/ai-sdk-provider/src/tools.ts (3)
SignTransactionInput
(65-67)MonitorTransactionInput
(69-71)SignSwapInput
(68-68)
apps/playground-web/src/app/api/chat/route.ts (1)
packages/ai-sdk-provider/src/provider.ts (1)
createThirdweb
(561-563)
packages/ai-sdk-provider/src/tools.ts (1)
packages/nebula/src/client/types.gen.ts (4)
AgentActionSignTransactionData
(460-481)AgentActionSignSwapDataToken
(390-415)AgentActionSignSwapDataIntent
(352-385)AgentActionSignSwapData
(338-347)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(8-34)apps/playground-web/src/components/prompt-input.tsx (3)
PromptInput
(24-32)PromptInputTextarea
(39-85)PromptInputSubmit
(146-175)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)
packages/ai-sdk-provider/src/provider.ts (1)
packages/ai-sdk-provider/src/tools.ts (1)
thirdwebTools
(44-63)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (4)
packages/ai-sdk-provider/src/provider.ts (1)
ThirdwebProvider
(530-558)apps/playground-web/src/components/blocks/APIHeader.tsx (1)
PageLayout
(49-70)apps/playground-web/src/components/code/code-example.tsx (2)
TabName
(58-68)CodeExample
(17-56)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)
ChatContainer
(28-119)
🔇 Additional comments (15)
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts (1)
123-126
: Good: short-circuit when already connected.Returning the cached address immediately avoids unnecessary UI prompts.
.changeset/many-pants-tease.md (1)
5-5
: Changelog reads well.Clear, scoped patch note.
apps/playground-web/src/components/ui/avatar.tsx (1)
1-50
: Solid Radix wrappers with proper ref forwarding and className merge. LGTM.apps/playground-web/src/app/navLinks.ts (2)
19-26
: Sidebar label changes look good."Live Demo" label and added "AI SDK" link align with the new pages.
23-26
: Route/ai/ai-sdk
confirmed presentThe file
apps/playground-web/src/app/ai/ai-sdk/page.tsx
exists and defines the page content for the AI SDK route, ensuring the sidebar link won’t be broken.apps/playground-web/package.json (1)
1-51
: Deps set looks consistent for new AI SDK features.Workspace ranges and versions align with Next 15/React 19. No immediate issues spotted.
packages/ai-sdk-provider/src/exports/thirdweb.ts (1)
1-2
: ESM-friendly re-exports with .js extensions LGTM.Pattern matches NodeNext + verbatimModuleSyntax best practices. Ensure tsconfig.build uses NodeNext resolution (see separate comment).
packages/ai-sdk-provider/src/types.ts (1)
8-34
: Type surface looks solid and idiomatic.Good use of UIMessage generics and type-only imports; aligns with tools’ IO.
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)
149-152
: Great UX: gated signing behind connection.ConnectButton fallback is correct for guarded actions.
apps/playground-web/src/app/ai/ai-sdk/page.tsx (1)
26-41
: LGTM: Server page composition and examples are clear.Matches Playground patterns; no need for “server-only” per team learning on page files.
packages/ai-sdk-provider/package.json (1)
31-35
: Verify Zod v4 compatibility before widening peerDep
We checked npm dist-tags for Zod and saw thatlatest
is at 4.1.3 whilenext
points to a 3.25.x beta, meaning v4 is officially released but we have no evidence your CI actually tests against it. Without explicit tests, consumers installing a v4 release could encounter breaking changes.• Your peerDeps currently allow
"zod": "^3.25.75 || ^4"
• No.github/workflows/*.yml
references to a Zod version matrix were found—there’s no CI guard covering both majors.
• Please either
- Pin to
^3
until you’ve verified code against v4, or- Expand your test matrix to install & run against both Zod 3 (e.g. 3.25.75) and Zod 4 (e.g. 4.1.3) in CI
packages/ai-sdk-provider/src/provider.ts (3)
125-140
: Verify Thirdweb /ai/chat Support forrole: "tool"
I’m not certain whether Thirdweb’s
/ai/chat
endpoint accepts messages withrole: "tool"
. Please check the official Thirdweb AI Chat API documentation. Ifrole: "tool"
is unsupported, convert tool results into an assistant message instead.Locations to update:
- packages/ai-sdk-provider/src/provider.ts lines 125–140
Suggested diff:
case "tool": { - return { - role: message.role, - content: toolContent, - }; + return { + // Thirdweb may not accept `role: "tool"`. Use "assistant" for tool output. + role: "assistant", + content: toolContent, + }; }
345-376
: Confirmed: Thirdweb SSE “delta” events usev
for text chunks
According to the official thirdweb streaming API docs, eachdelta
event’sdata
payload is a JSON object containing a singlev
property with the next piece of text. No alternate field names (e.g.delta
ordata
) are used for text chunks in the/ai/chat
stream (portal.thirdweb.com)
160-165
: It looks like ripgrep didn’t pick up any definition forLanguageModelV2CallOptions
or occurrences ofmodelId
in the provider package, and we can’t confirm the correct request field purely from this code search. To be sure, please verify against the Thirdweb AI Chat API spec or SDK type definitions:
- Does the
LanguageModelV2CallOptions
type include amodelId
(ormodel_id
) field?- When performing a POST to
/ai/chat
, is the model parameter named"model"
or"model_id"
?If you have access to the API docs or to the types in the core SDK (e.g.
@thirdweb-dev/sdk
or similar package), please confirm the exact field name and update this snippet accordingly:- const body = { - messages, - stream: false, - context: this.settings.context, - }; + const body = { + model: this.modelId, // replace "model" with "model_id" if that’s the correct field + messages, + stream: false, + context: this.settings.context, + };Until we can verify the exact field name, I recommend manually checking the API spec or the originating type definitions to avoid breaking the contract.
apps/playground-web/src/components/prompt-input.tsx (1)
156-163
: Action Required: VerifyChatStatus
Values and Align Status ChecksPlease confirm that the string literals used in the
if
/else
chain match all possibleChatStatus
values exported by theai
package. Any mismatch could lead to unreachable branches or missing UI states.• File:
apps/playground-web/src/components/prompt-input.tsx
• Lines: 156–163Consider using a
switch (status)
with adefault
branch (or exhaustiveness check) to ensure you cover everyChatStatus
.
5ac1076
to
1c89599
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
♻️ Duplicate comments (12)
apps/playground-web/src/components/loader.tsx (1)
12-12
: Remove inline style; follow Tailwind-only rule.Inline styles are disallowed in apps/playground-web. Rely on inherited text color instead.
- style={{ color: "currentcolor" }} + className="text-inherit"apps/dashboard/package.json (1)
63-63
: Shiki v3 upgrade here as well — mirror the verification.Same risks as Nebula; reuse the verification steps from the nebula comment to catch API/import mismatches at build time.
apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1)
35-36
: Client component importing server module breaks RSC boundaries — move promptNebula to a client wrapper.This file is a Client Component ("use client"). Importing from ../server/chat at runtime can cause Next.js to error or bundle server code client-side. Import only types from server/types and use a browser-safe client wrapper.
Apply:
-import { type NebulaContext, promptNebula } from "../server/chat"; -import type { NebulaUserMessage } from "../server/types"; +import type { NebulaContext, NebulaUserMessage } from "../server/types"; +import { promptNebula } from "../client/chat";Add the wrapper (new file):
// apps/playground-web/src/app/ai/client/chat.ts "use client"; import type { NebulaContext, NebulaUserMessage } from "../server/types"; type StreamHandler = (ev: any) => void; export async function promptNebula(params: { abortController: AbortController; context: NebulaContext | undefined; message: NebulaUserMessage; handleStream: StreamHandler; }) { const { abortController, context, message, handleStream } = params; const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message, context }), signal: abortController.signal, }); if (!res.ok || !res.body) throw new Error(`Request failed: ${res.status}`); const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buf += decoder.decode(value, { stream: true }); let idx: number; while ((idx = buf.indexOf("\n\n")) !== -1) { const chunk = buf.slice(0, idx).trim(); buf = buf.slice(idx + 2); if (!chunk) continue; const dataLine = chunk.split("\n").find((l) => l.startsWith("data:")); if (!dataLine) continue; try { handleStream(JSON.parse(dataLine.replace(/^data:\s*/, ""))); } catch { // ignore malformed chunks } } } }packages/ai-sdk-provider/tsconfig.base.json (1)
14-15
: importHelpers requires tslib at runtimeWith importHelpers: true, emitted JS imports from "tslib". Ensure packages/ai-sdk-provider/package.json lists tslib under dependencies to avoid runtime module-not-found.
"dependencies": { + "tslib": "^2.7.0", /* existing deps */ }
packages/ai-sdk-provider/src/tools.ts (3)
5-11
: Schema key should be chainId (camelCase), not chain_idMatches generated Nebula types and prevents parsing mismatches.
const AgentActionSignTransactionData = z.object({ - chain_id: z.number(), + chainId: z.number(), function: z.string().nullable().optional(), to: z.string(), value: z.string(), data: z.string(), });
2-2
: Invalid zod import pathImport from "zod", not "zod/v4".
-import { z } from "zod/v4"; +import { z } from "zod";
13-22
: Swap intent fields should be camelCaseAlign with generated types (originChainId, destinationChainId, etc.).
const AgentActionSignSwapDataIntent = z.object({ - origin_chain_id: z.number(), - origin_token_address: z.string(), - destination_chain_id: z.number(), - destination_token_address: z.string(), + originChainId: z.number(), + originTokenAddress: z.string(), + destinationChainId: z.number(), + destinationTokenAddress: z.string(), amount: z.string(), sender: z.string(), receiver: z.string(), - max_steps: z.number(), + maxSteps: z.number(), });packages/ai-sdk-provider/src/provider.ts (4)
119-121
: Guard lastUserMessage to avoid [undefined]Prevent sending
[undefined]
when no user/tool message exists.- const messages = this.settings.context?.session_id - ? [lastUserMessage] - : allMessages; + const messages = + this.settings.context?.session_id && lastUserMessage + ? [lastUserMessage] + : allMessages;
196-200
: Advertise SSE in streaming requestAdd Accept: text/event-stream for doStream.
- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "text/event-stream" },
428-435
: Don’t stringify tool-call inputEmit the object so the AI SDK can validate against Tool.parameters.
- input: JSON.stringify(input), + input,
62-67
: Flatten system message content to stringThirdweb API likely expects a string, not an array of parts.
- case "system": - return { - role: message.role, - content: message.content, - }; + case "system": { + const content = message.content + .map((p) => (p.type === "text" ? p.text : "")) + .join(""); + return { role: "system", content }; + }apps/playground-web/src/components/response.tsx (1)
22-24
: Remove brittle custom memo comparator; use named component with explicit return type.Comparator ignores prop changes like
components
,remarkPlugins
, etc., causing stale renders. Prefer default shallow compare and add explicit return type per guidelines.-export const Response = memo( - ({ className, ...props }: ResponseProps) => ( +export const Response = memo(function ResponseComponent( + { className, ...props }: ResponseProps, +): JSX.Element { <Streamdown components={{ /* ...unchanged... */ }} remarkPlugins={[remarkGfm]} className={cn( "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className, )} {...props} /> - ), - (prevProps, nextProps) => prevProps.children === nextProps.children, -); +});Also applies to: 158-160
🧹 Nitpick comments (14)
apps/playground-web/src/components/loader.tsx (3)
1-3
: Clarify RSC boundary: make this a Client Component (likely usage) or mark server-only.This spinner is commonly used in interactive client UI. If that’s the intent, add "use client"; otherwise add
import "server-only";
. Please confirm.+'use client'; import type { HTMLAttributes } from "react"; import { cn } from "@/lib/utils";
1-1
: Add explicit return types per repo TypeScript guidelines.Keep arrows if you prefer, but annotate returns explicitly.
-import type { HTMLAttributes } from "react"; +import type { HTMLAttributes, JSX } from "react";-const LoaderIcon = ({ size = 16 }: LoaderIconProps) => ( +const LoaderIcon = ({ size = 16 }: LoaderIconProps): JSX.Element => (-export const Loader = ({ className, size = 16, ...props }: LoaderProps) => ( +export const Loader = ({ className, size = 16, ...props }: LoaderProps): JSX.Element => (Also applies to: 8-8, 86-86
17-18
: Avoid duplicate IDs from clipPath; it’s unnecessary here.The rectangular clipPath doesn’t change rendering and risks ID collisions with multiple loaders on the page. Remove it.
- <g clipPath="url(#clip0_2393_1490)"> + {/* clipPath removed */} <path d="M8 0V4" stroke="currentColor" strokeWidth="1.5" /> ... - </g> - <defs> - <clipPath id="clip0_2393_1490"> - <rect fill="white" height="16" width="16" /> - </clipPath> - </defs>Also applies to: 73-79
apps/playground-web/package.json (1)
4-4
: Track budgets for new heavy deps & lazy-load in client components.
- Add size-limit scripts and budgets to apps/playground-web/package.json (include @ai-sdk/react, ai, streamdown, shiki) per repo guidelines
- Refactor client components (e.g. chat-container.tsx, response.tsx, code-example.tsx, code.client.tsx) to lazy-load these heavy libs via dynamic imports
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (5)
189-201
: Replace inline styles with Tailwind per app guidelinesUse className="w-full" instead of style width.
- <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full"Applies also to the swap button at Lines 263-266.
107-107
: Remove stray console.logLeftover debug log in the render path.
- console.log("---sign_swap", part);
52-55
: sessionId vs session_id naming driftYou set sessionId in the request body, while server/provider types use session_id. Align keys end-to-end or translate in the API route to avoid dropped sessions.
- body: { - sessionId, - }, + body: { + session_id: sessionId, + },Ensure the API route forwards context.session_id accordingly.
Also applies to: 210-214, 284-288
173-179
: Be resilient to pending schema rename (chainId vs chain_id)Downstream tools.ts is moving to camelCase (chainId). Support both to avoid breaking the UI during transition.
- chain_id: input?.chain_id || 8453, + chain_id: (input as any)?.chain_id ?? (input as any)?.chainId ?? 8453, - to: input?.to || "", + to: (input as any)?.to || "", - data: (input?.data as `0x${string}`) || "0x", + data: ((input as any)?.data as `0x${string}`) || "0x", - value: input?.value ? BigInt(input.value) : BigInt(0), + value: + (input as any)?.value !== undefined + ? BigInt((input as any).value) + : BigInt(0),And similarly for the swap transaction: read from input.transaction.chain_id or .chainId.
Also applies to: 245-253
186-221
: Validate required fields before preparing transactionDisable the button or show an error if
to
is empty or value parsing fails to avoid runtime errors in prepareTransaction.- <TransactionButton + <TransactionButton className="w-full" - transaction={() => - prepareTransaction({ + disabled={!transactionData.to} + transaction={() => { + // basic guard + if (!transactionData.to) { + throw new Error("Missing 'to' address"); + } + return prepareTransaction({ client: THIRDWEB_CLIENT, chain: defineChain(transactionData.chain_id), to: transactionData.to, data: transactionData.data, value: transactionData.value, - }) - } + }); + }}Repeat for the swap button block.
Also applies to: 260-295
packages/ai-sdk-provider/src/types.ts (1)
31-50
: Context keys use snake_case; document mapping at the API boundarySince app code often uses camelCase (e.g., sessionId), note the expected snake_case here or add a small adapter in provider to reduce consumer errors.
packages/ai-sdk-provider/src/provider.ts (1)
129-137
: Consider addingAccept: application/json
to non-streaming and JSON parse errorsMinor hardening; optional.
- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "application/json" },And keep the SSE Accept only in doStream.
Also applies to: 193-201
apps/playground-web/src/components/response.tsx (3)
103-107
: Preserve caller-provided classes on lists and table cells.Current literals override incoming
className
. Merge withcn()
.<ol - {...cleanedProps(props)} - className="mb-4 list-outside list-decimal pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5" + {...cleanedProps(props)} + className={cn( + "mb-4 list-outside list-decimal pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5", + props.className, + )} />- <TableCell {...cleanedProps(props)} className="text-left" /> + <TableCell + {...cleanedProps(props)} + className={cn("text-left", props.className)} + />- <TableHead - {...cleanedProps(props)} - className="text-left text-foreground" - > + <TableHead + {...cleanedProps(props)} + className={cn("text-left text-foreground", props.className)} + > {c} </TableHead><ul - {...cleanedProps(props)} - className="mb-4 list-outside list-disc pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5" + {...cleanedProps(props)} + className={cn( + "mb-4 list-outside list-disc pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5", + props.className, + )} />Also applies to: 128-130, 132-139, 144-147
151-157
: Consider merging user-provided plugins/components instead of overwriting.Spreading
{...props}
last means caller can fully replacecomponents
/remarkPlugins
. If the intent is “defaults + user additions,” destructurecomponents
/remarkPlugins
from props and merge, e.g.,components={{ ...defaults, ...(userComponents ?? {}) }}
andremarkPlugins={[remarkGfm, ...(userPlugins ?? [])]}
.If you want, I can push a patch that safely merges defaults and preserves user overrides precedence.
162-162
: displayName becomes redundant with a named memoized function.If you adopt the named
memo(function ResponseComponent …)
,Response.displayName
is optional. Remove for brevity.-Response.displayName = "Response";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/ai/components/ChatPageContent.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/portal/package.json
- .changeset/many-pants-tease.md
🚧 Files skipped from review as they are similar to previous changes (16)
- packages/ai-sdk-provider/README.md
- packages/ai-sdk-provider/tsconfig.build.json
- packages/ai-sdk-provider/tsconfig.json
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/playground-web/src/app/navLinks.ts
- apps/playground-web/src/components/message.tsx
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/src/components/ui/avatar.tsx
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- packages/ai-sdk-provider/package.json
- apps/playground-web/src/components/code/code-example.tsx
- apps/playground-web/src/components/conversation.tsx
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- apps/playground-web/src/components/reasoning.tsx
- packages/ai-sdk-provider/biome.json
- apps/playground-web/src/components/prompt-input.tsx
🧰 Additional context used
📓 Path-based instructions (4)
package.json
📄 CodeRabbit inference engine (CLAUDE.md)
Track bundle budgets via
package.json#size-limit
Files:
package.json
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/playground-web/src/components/loader.tsx
packages/ai-sdk-provider/src/provider.ts
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/response.tsx
packages/ai-sdk-provider/src/tools.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/playground-web/src/components/loader.tsx
packages/ai-sdk-provider/src/provider.ts
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/response.tsx
packages/ai-sdk-provider/src/tools.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/response.tsx
🧠 Learnings (13)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Build all packages using `pnpm build` and specific packages with dependencies using `turbo run build --filter=./packages/*`
Applied to files:
package.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Monorepo is managed with Turborepo and pnpm workspaces
Applied to files:
package.json
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Never hard-code colors – always go through Tailwind variables.
Applied to files:
apps/playground-web/src/components/loader.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to **/*.{ts,tsx} : Re-use shared types from `@/types` or local `types.ts` barrels
Applied to files:
packages/ai-sdk-provider/tsconfig.base.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Server Components (Node edge): Start files with `import "server-only";`
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Client Components (browser): Begin files with `'use client';`
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/api/**/*.{ts,tsx} : Prefix files with `import "server-only";` so they never end up in the client bundle.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/components/*.client.tsx : Client components must start with `'use client';` before imports.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{ts,tsx} : Client-side only: never import `posthog-js` in server components.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-08-07T17:24:31.965Z
Learnt from: MananTank
PR: thirdweb-dev/js#7812
File: apps/dashboard/src/app/(app)/team/~/~project/[[...paths]]/page.tsx:1-11
Timestamp: 2025-08-07T17:24:31.965Z
Learning: In Next.js App Router, page components (page.tsx files) are server components by default and do not require the "server-only" import directive. The "server-only" directive is primarily used for utility functions, API helpers, and data access modules that should never be included in the client bundle.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{ts,tsx} : Export default async functions without `'use client';` – they run on the Node edge.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
PR: thirdweb-dev/js#7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Never import `posthog-js` in server components
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
🧬 Code graph analysis (5)
packages/ai-sdk-provider/src/provider.ts (2)
packages/ai-sdk-provider/src/types.ts (3)
ThirdwebConfig
(12-29)ThirdwebSettings
(31-50)DEFAULT_BASE_URL
(10-10)packages/ai-sdk-provider/src/tools.ts (1)
createTools
(51-75)
packages/ai-sdk-provider/src/types.ts (2)
packages/ai-sdk-provider/src/exports/thirdweb.ts (1)
ThirdwebAiMessage
(2-2)packages/ai-sdk-provider/src/tools.ts (5)
SignTransactionInput
(77-79)TransactionOutput
(34-36)SignSwapInput
(80-80)MonitorTransactionInput
(81-83)MonitorTransactionOutput
(47-49)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (2)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(52-71)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)
apps/playground-web/src/components/response.tsx (2)
apps/portal/src/components/code/code.client.tsx (1)
CodeClient
(27-54)apps/portal/src/components/ui/table.tsx (5)
TableContainer
(150-150)TableBody
(144-144)TableCell
(148-148)TableHead
(146-146)TableHeader
(143-143)
packages/ai-sdk-provider/src/tools.ts (2)
packages/nebula/src/client/types.gen.ts (3)
AgentActionSignTransactionData
(460-481)AgentActionSignSwapDataIntent
(352-385)AgentActionSignSwapData
(338-347)packages/ai-sdk-provider/src/types.ts (1)
ThirdwebConfig
(12-29)
🔇 Additional comments (10)
apps/playground-web/src/components/loader.tsx (1)
82-85
: Props typing looks good.Nice: type alias over interface, extends HTMLAttributes, and a focused
size?: number
.apps/nebula/package.json (1)
35-35
: Verify Shiki v3 API usage and buildDouble-check that the
codeToHtml
import ingetCodeHtml.tsx
and theBundledLanguage
type incode.client.tsx
still resolve under Shiki 3’s ESM-only bundles (entry points, wasm assets), then runpnpm install && pnpm --filter nebula-app build
to confirm no bundler/runtime errors.package.json (1)
65-65
: LGTM: include ai-sdk-provider in playground dev filter.This ensures local edits to the new package are picked up by the playground.
packages/ai-sdk-provider/tsconfig.base.json (2)
21-24
: Confirm DOM lib usage is intentionalIncluding "DOM" widens globals in a server-leaning package. If only needed for fetch typing, consider relying on undici types or a minimal fetch type to avoid accidental browser globals bleed.
25-29
: NodeNext + verbatimModuleSyntax: ensure package.json is ESMWith "module": "NodeNext" and "verbatimModuleSyntax": true, verify package.json has "type": "module" and emitted paths end with .js in TS imports (you already do). Prevent CJS/ESM interop issues.
Also applies to: 41-44
packages/ai-sdk-provider/src/types.ts (2)
10-29
: Config surface looks goodDEFAULT_BASE_URL and ThirdwebConfig fields are clear; no issues found.
52-71
: ThirdwebAiMessage tool payload types align with the UITypes match the client usage; keep them in sync with tools.ts schemas after the pending fixes.
packages/ai-sdk-provider/src/tools.ts (1)
77-84
: Exported input types must reflect the updated schemasAfter the schema fixes, these inferred types will match the UI’s expectations.
apps/playground-web/src/components/response.tsx (2)
164-170
: Helper looks good as-is.Clear prop scrubbing for
node
, typed correctly.
26-33
: Render a secure native anchor and preserve incoming classes.Use
<a>
(not Lucide’s icon), addrel="noopener noreferrer"
for target-blank links, and mergeprops.className
.- a: (props) => ( - <Link - href={props.href ?? "#"} - target="_blank" - {...cleanedProps(props)} - className="mt-4 underline decoration-muted-foreground/50 decoration-dotted underline-offset-[5px] hover:text-foreground hover:decoration-foreground hover:decoration-solid" - /> - ), + a: (props) => ( + <a + href={props.href ?? "#"} + target="_blank" + rel="noopener noreferrer" + {...cleanedProps(props)} + className={cn( + "mt-4 underline decoration-muted-foreground/50 decoration-dotted underline-offset-[5px] hover:text-foreground hover:decoration-foreground hover:decoration-solid", + props.className, + )} + /> + ),⛔ Skipped due to learnings
Learnt from: jnsdls PR: thirdweb-dev/js#7365 File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17 Timestamp: 2025-06-18T04:30:04.326Z Learning: Next.js Link component fully supports both internal and external URLs and works appropriately with all standard anchor attributes including target="_blank", rel="noopener noreferrer", etc. Using Link for external URLs is completely appropriate and recommended.
Learnt from: MananTank PR: thirdweb-dev/js#7812 File: apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/token-banner.tsx:48-60 Timestamp: 2025-08-07T20:43:21.864Z Learning: In the TokenBanner component at apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/token-banner.tsx, the Link components use target="_blank" with internal application routes (starting with "/") to open pages in new tabs within the same application. These internal links do not require rel="noopener noreferrer" security attributes, which are only needed for external URLs.
Learnt from: jnsdls PR: thirdweb-dev/js#7365 File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17 Timestamp: 2025-06-18T04:27:16.172Z Learning: Next.js Link component supports external URLs without throwing errors. When used with absolute URLs (like https://...), it behaves like a regular anchor tag without client-side routing, but does not cause runtime crashes or errors as previously believed.
1c89599
to
63d12c9
Compare
63d12c9
to
3a95998
Compare
078e60f
to
3bb8ab2
Compare
3bb8ab2
to
4c95201
Compare
4c95201
to
b2d2227
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (11)
apps/playground-web/src/components/loader.tsx (4)
16-16
: Drop redundant <title> once the SVG is aria-hidden.Prevents duplicate announcements.
- <title>Loader</title> + {/* decorative */}
17-79
: Avoid brittle clipPath ids; remove unnecessary defs.Eliminates risk of ID collisions across multiple instances.
- <g clipPath="url(#clip0_2393_1490)"> + <g> <path d="M8 0V4" stroke="currentColor" strokeWidth="1.5" /> ... </g> - <defs> - <clipPath id="clip0_2393_1490"> - <rect fill="white" height="16" width="16" /> - </clipPath> - </defs>
9-15
: Remove inline style; Tailwind-only, and hide decorative SVG from AT.Inline styles are disallowed; the SVG should be ignored by assistive tech.
<svg height={size} strokeLinejoin="round" - style={{ color: "currentcolor" }} viewBox="0 0 16 16" width={size} + aria-hidden="true" + focusable="false" >
86-96
: Expose loading state to assistive tech (status region + SR label).Announces progress and keeps SVG decorative.
export const Loader = ({ className, size = 16, ...props }: LoaderProps) => ( <div className={cn( "inline-flex animate-spin items-center justify-center", className, )} + role="status" + aria-live="polite" + aria-busy="true" {...props} > + <span className="sr-only">Loading…</span> <LoaderIcon size={size} /> </div> );apps/playground-web/src/components/prompt-input.tsx (1)
123-125
: Fix precedence so explicitsize
is honored.Current expression treats any provided
size
as truthy and forces"default"
.- const newSize = - (size ?? Children.count(props.children) > 1) ? "default" : "icon"; + const newSize = + size ?? (Children.count(props.children) > 1 ? "default" : "icon");packages/ai-sdk-provider/src/provider.ts (4)
64-72
: Flatten system message content to a string.Thirdweb API likely expects string content; arrays may leak through unchanged. Flatten like user/assistant.
- case "system": - return { - role: message.role, - content: message.content, - }; + case "system": { + const content = Array.isArray(message.content) + ? message.content + .map((p) => (p.type === "text" ? p.text : "")) + .join("") + : String(message.content ?? ""); + return { role: "system", content }; + }
200-207
: SSE requests should advertiseAccept: text/event-stream
.Some backends gate streaming by Accept. Add it for streaming calls.
- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "text/event-stream" },
358-365
: Emit session_id undermetadata
, notid
, in response-metadata.The README’s
onFinish
expectsmessage.metadata.session_id
. Fix event payload.- controller.enqueue({ - type: "response-metadata", - id: parsed.session_id || "", - }); + controller.enqueue({ + type: "response-metadata", + metadata: { session_id: parsed.session_id }, + });
433-440
: Don’t stringify tool input; pass the object.
tool-call.input
must be a JSON-serializable object. Stringifying breaks downstream validation.- input: JSON.stringify(input), + input,packages/ai-sdk-provider/src/tools.ts (2)
2-2
: Incorrect zod import path.Import from the package root;
zod/v4
will fail at runtime.-import { z } from "zod/v4"; +import { z } from "zod";
51-75
: Tool shape incompatible with AI SDK v4; useparameters
(dropoutputSchema
).
Tool
expects aparameters
Zod schema. Returning objects withinputSchema
/outputSchema
will not type-check or validate.-export function createTools(_config: ThirdwebConfig): Record<string, Tool> { - return { +export function createTools(_config: ThirdwebConfig) { + return { sign_transaction: { id: "thirdweb.sign_transaction" as const, name: "sign_transaction", description: "Sign a transaction", - inputSchema: AgentActionSignTransactionData, - outputSchema: AgentActionTransactionOutputData, - } as const, + parameters: AgentActionSignTransactionData, + }, sign_swap: { id: "thirdweb.sign_swap" as const, name: "sign_swap", description: "Sign a swap transaction", - inputSchema: AgentActionSignSwapData, - outputSchema: AgentActionTransactionOutputData, - } as const, + parameters: AgentActionSignSwapData, + }, monitor_transaction: { id: "thirdweb.monitor_transaction" as const, name: "monitor_transaction", description: "Monitor a transaction", - inputSchema: AgentActionMonitorTransactionData, - outputSchema: AgentActionMonitorTransactionOutputData, - } as const, - } as const; + parameters: AgentActionMonitorTransactionData, + }, + } as const satisfies Record<string, Tool>; }
🧹 Nitpick comments (9)
apps/playground-web/src/components/loader.tsx (1)
4-15
: Prefer one stateless function per file; move LoaderIcon or inline it.Either inline the SVG inside Loader or extract LoaderIcon to its own file to keep single-responsibility.
apps/playground-web/src/components/prompt-input.tsx (3)
24-32
: Add explicit return types to exported components (guideline).Conform to “explicit return types” for
**/*.tsx
.-export const PromptInput = ({ className, ...props }: PromptInputProps) => ( +export const PromptInput = ({ className, ...props }: PromptInputProps): JSX.Element => ( @@ -export const PromptInputTextarea = ({ +export const PromptInputTextarea = ({ onChange, - className, + onKeyDown: userOnKeyDown, + className, placeholder = "What would you like to know?", ...props -}: PromptInputTextareaProps) => { +}: PromptInputTextareaProps): JSX.Element => { @@ -export const PromptInputToolbar = ({ +export const PromptInputToolbar = ({ className, ...props -}: PromptInputToolbarProps) => ( +}: PromptInputToolbarProps): JSX.Element => ( @@ -export const PromptInputTools = ({ +export const PromptInputTools = ({ className, ...props -}: PromptInputToolsProps) => ( +}: PromptInputToolsProps): JSX.Element => ( @@ -export const PromptInputButton = ({ +export const PromptInputButton = ({ variant = "ghost", className, size, ...props -}: PromptInputButtonProps) => { +}: PromptInputButtonProps): JSX.Element => { @@ -export const PromptInputSubmit = ({ +export const PromptInputSubmit = ({ className, variant = "default", size = "icon", status, children, ...props -}: PromptInputSubmitProps) => { +}: PromptInputSubmitProps): JSX.Element => { @@ -export const PromptInputModelSelect = (props: PromptInputModelSelectProps) => ( +export const PromptInputModelSelect = (props: PromptInputModelSelectProps): JSX.Element => ( @@ -export const PromptInputModelSelectTrigger = ({ +export const PromptInputModelSelectTrigger = ({ className, ...props -}: PromptInputModelSelectTriggerProps) => ( +}: PromptInputModelSelectTriggerProps): JSX.Element => ( @@ -export const PromptInputModelSelectContent = ({ +export const PromptInputModelSelectContent = ({ className, ...props -}: PromptInputModelSelectContentProps) => ( +}: PromptInputModelSelectContentProps): JSX.Element => ( @@ -export const PromptInputModelSelectItem = ({ +export const PromptInputModelSelectItem = ({ className, ...props -}: PromptInputModelSelectItemProps) => ( +}: PromptInputModelSelectItemProps): JSX.Element => ( @@ -export const PromptInputModelSelectValue = ({ +export const PromptInputModelSelectValue = ({ className, ...props -}: PromptInputModelSelectValueProps) => ( +}: PromptInputModelSelectValueProps): JSX.Element => (Also applies to: 39-46, 89-97, 101-113, 117-140, 146-153, 179-181, 187-199, 205-210, 214-219, 225-230
115-140
: Icon-only buttons need an accessible label.When
newSize === "icon"
and children are only an icon, requirearia-label
(or render sr-only text). Consider asserting in dev builds or document it in the prop JSDoc.
1-231
: Scope/coupling: consider splitting into focused files.This file defines many primitives; per guideline “one stateless, single-responsibility function per file,” consider splitting (e.g.,
prompt-input.tsx
,prompt-input-textarea.tsx
,prompt-input-submit.tsx
,prompt-input-model-select.tsx
) with a barrel export.apps/playground-web/src/components/conversation.tsx (1)
1-63
: Consider splitting into single-responsibility modules.This file exports three components; guidelines prefer one stateless SRP per file. Suggest Conversation.tsx, ConversationContent.tsx, ConversationScrollButton.tsx with an index.ts barrel.
I can generate the file split + barrel diff if you want.
packages/ai-sdk-provider/README.md (1)
156-166
: Docs nit: add missing imports/placeholders for copy-pasteability.Readers may stumble on undefined symbols. Consider adding a short preface showing how to obtain
THIRDWEB_CLIENT
anddefineChain
(or mark them as placeholders).+// NOTE: replace with your own client and chain utilities +import { defineChain, createThirdwebClient } from "thirdweb"; +const THIRDWEB_CLIENT = createThirdwebClient({ clientId: process.env.NEXT_PUBLIC_TW_CLIENT_ID! });packages/ai-sdk-provider/src/types.ts (1)
12-29
: Prefer type aliases over interfaces (style).Guideline suggests type aliases unless nominal typing is needed.
-export interface ThirdwebConfig { +export type ThirdwebConfig = { /** * Optional: The base URL of the thirdweb API, defaults to https://api.thirdweb.com */ baseURL?: string; /** * Your project secret key for backend usage - get it from the thirdweb dashboard */ secretKey?: string; /** * Your project client ID - get it from the thirdweb dashboard */ clientId?: string; /** * Optional: The user wallet auth token (JWT) for executing transactions */ walletAuthToken?: string; -} +};packages/ai-sdk-provider/src/provider.ts (1)
529-532
: Add explicit return type to factory.Matches guideline for explicit return types.
-export function createThirdwebAI(config: ThirdwebConfig = {}) { +export function createThirdwebAI(config: ThirdwebConfig = {}): ThirdwebProvider { return new ThirdwebProvider(config); }packages/ai-sdk-provider/src/tools.ts (1)
24-28
: Confirm swap schema fields (nullable action, tokens).Upstream types often include
action: string | null
and token shapes (from
,to
). If API emits these, extend the schema; if not, ignore.-const AgentActionSignSwapData = z.object({ - action: z.string(), +const AgentActionSignSwapDataToken = z.object({ + address: z.string(), + chain_id: z.number(), + amount: z.string(), + symbol: z.string(), + decimals: z.number(), + price: z.number().nullable(), +}); + +const AgentActionSignSwapData = z.object({ + action: z.string().nullable(), intent: AgentActionSignSwapDataIntent, + from: AgentActionSignSwapDataToken.optional(), + to: AgentActionSignSwapDataToken.optional(), transaction: AgentActionSignTransactionData, });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (35)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/knip.json
(2 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)apps/portal/src/app/ai/chat/ai-sdk/page.mdx
(1 hunks)apps/portal/src/app/ai/sidebar.tsx
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)packages/thirdweb/src/wallets/wallet-connect/controller.ts
(2 hunks)packages/ui/package.json
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- .changeset/many-pants-tease.md
🚧 Files skipped from review as they are similar to previous changes (27)
- apps/portal/package.json
- packages/ai-sdk-provider/tsconfig.json
- apps/playground-web/knip.json
- packages/ui/package.json
- apps/portal/src/app/ai/sidebar.tsx
- apps/nebula/package.json
- packages/ai-sdk-provider/tsconfig.build.json
- packages/ai-sdk-provider/biome.json
- apps/playground-web/src/app/navLinks.ts
- apps/playground-web/src/components/response.tsx
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
- apps/playground-web/src/components/ui/avatar.tsx
- apps/portal/src/app/ai/chat/ai-sdk/page.mdx
- packages/thirdweb/src/wallets/wallet-connect/controller.ts
- packages/ai-sdk-provider/package.json
- packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- apps/dashboard/package.json
- package.json
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/src/components/code/code-example.tsx
- packages/ai-sdk-provider/tsconfig.base.json
- apps/playground-web/src/components/message.tsx
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- apps/playground-web/package.json
- apps/playground-web/src/components/reasoning.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/playground-web/src/components/loader.tsx
packages/ai-sdk-provider/src/tools.ts
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/components/conversation.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/components/prompt-input.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/playground-web/src/components/loader.tsx
packages/ai-sdk-provider/src/tools.ts
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/components/conversation.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/components/prompt-input.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/conversation.tsx
apps/playground-web/src/components/prompt-input.tsx
🧠 Learnings (7)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Applied to files:
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/prompt-input.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Never hard-code colors – always go through Tailwind variables.
Applied to files:
apps/playground-web/src/components/loader.tsx
📚 Learning: 2025-08-28T20:50:33.142Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx:167-181
Timestamp: 2025-08-28T20:50:33.142Z
Learning: The thirdweb-dev/ai-sdk-provider schemas use snake_case field naming convention (e.g., chain_id, transaction_hash) rather than camelCase, as defined in the zod schemas in packages/ai-sdk-provider/src/tools.ts.
Applied to files:
packages/ai-sdk-provider/src/tools.ts
packages/ai-sdk-provider/src/types.ts
packages/ai-sdk-provider/src/provider.ts
packages/ai-sdk-provider/README.md
📚 Learning: 2025-08-28T20:50:33.142Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx:167-181
Timestamp: 2025-08-28T20:50:33.142Z
Learning: The SignTransactionInput schema in thirdweb-dev/ai-sdk-provider uses snake_case field names (chain_id) rather than camelCase (chainId).
Applied to files:
packages/ai-sdk-provider/src/tools.ts
packages/ai-sdk-provider/src/provider.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/playground-web/src/components/prompt-input.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/wagmi-adapter/** : Wagmi ecosystem integration is in `packages/wagmi-adapter/`
Applied to files:
packages/ai-sdk-provider/README.md
📚 Learning: 2025-08-28T20:33:35.084Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: packages/ai-sdk-provider/README.md:92-101
Timestamp: 2025-08-28T20:33:35.084Z
Learning: The Vercel AI SDK's DefaultChatTransport uses `body` (not `data`) for the transport payload when calling sendMessage with additional data like sessionId.
Applied to files:
packages/ai-sdk-provider/README.md
🧬 Code graph analysis (3)
packages/ai-sdk-provider/src/tools.ts (2)
packages/nebula/src/client/types.gen.ts (3)
AgentActionSignTransactionData
(460-481)AgentActionSignSwapDataIntent
(352-385)AgentActionSignSwapData
(338-347)packages/ai-sdk-provider/src/types.ts (1)
ThirdwebConfig
(12-29)
packages/ai-sdk-provider/src/types.ts (1)
packages/ai-sdk-provider/src/tools.ts (5)
SignTransactionInput
(77-79)TransactionOutput
(34-36)SignSwapInput
(80-80)MonitorTransactionInput
(81-83)MonitorTransactionOutput
(47-49)
packages/ai-sdk-provider/src/provider.ts (2)
packages/ai-sdk-provider/src/types.ts (3)
ThirdwebConfig
(12-29)ThirdwebSettings
(31-50)DEFAULT_BASE_URL
(10-10)packages/ai-sdk-provider/src/tools.ts (4)
SignTransactionInput
(77-79)SignSwapInput
(80-80)MonitorTransactionInput
(81-83)createTools
(51-75)
🪛 LanguageTool
packages/ai-sdk-provider/README.md
[grammar] ~7-~7: There might be a mistake here.
Context: ...nd and can interact with blockchain data - Transaction execution: Sign and execut...
(QB_NEW_EN)
[grammar] ~8-~8: There might be a mistake here.
Context: ... transactions directly from AI responses - Token swaps: Perform token swaps throu...
(QB_NEW_EN)
[grammar] ~9-~9: There might be a mistake here.
Context: ...token swaps through AI-generated actions - Transaction monitoring: Track transact...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ...ack transaction status and confirmations - Session continuity: Maintain conversat...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...conversation context across interactions - Type safety: Full TypeScript support w...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ...peScript support with typed tool results - Streaming support: Real-time AI respon...
(QB_NEW_EN)
[grammar] ~242-~242: There might be a mistake here.
Context: ...Sign and execute blockchain transactions - sign_swap
: Perform token swaps - **`monitor_trans...
(QB_NEW_EN)
[grammar] ~243-~243: There might be a mistake here.
Context: ...s - sign_swap
: Perform token swaps - monitor_transaction
: Track transaction status and confirmat...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...//playground.thirdweb.com/ai/chat/ai-sdk](https://playground.thirdweb.com/ai/chat/ai-sdk) - Documentation: [https://portal.thirdwe...
(QB_NEW_EN)
[grammar] ~249-~249: There might be a mistake here.
Context: ...tps://portal.thirdweb.com/ai/chat/ai-sdk](https://portal.thirdweb.com/ai/chat/ai-sdk) - Vercel AI SDK: [https://ai-sdk.dev/doc...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Size
🔇 Additional comments (8)
apps/playground-web/src/components/prompt-input.tsx (1)
47-66
: Good IME composition guard and Shift+Enter handling.Enter-to-submit with
isComposing
and Shift newline behavior looks correct.apps/playground-web/src/components/conversation.tsx (2)
1-1
: LGTM: correct client component directive.The file starts with 'use client' as required for browser-only UI.
35-38
: Resolved: ConversationScrollButton is always within the StickToBottom provider.
Only usage of<ConversationScrollButton />
appears inside<Conversation>…</Conversation>
inchat-container.tsx
, which rendersStickToBottom
at its root—no further action needed.packages/ai-sdk-provider/README.md (3)
92-101
: Transport payload key is correct here (usebody
).Confirmed per Vercel AI SDK DefaultChatTransport; no change needed.
152-176
: Sample aligns with snake_case tool I/O.The example correctly uses snake_case (e.g., chain_id, transaction_hash) consistent with the provider/tool schemas.
Also applies to: 178-183
55-66
: Follow-up: provider must emit session_id as response metadata for this example to work.The
onFinish
handler readsmessage.metadata?.session_id
, but the provider currently enqueues it asid
instead ofmetadata
. Ensure provider’s streaming path emits{ type: "response-metadata", metadata: { session_id } }
.Also applies to: 83-91
packages/ai-sdk-provider/src/types.ts (2)
10-10
: Centralized base URL looks good; ensure all callers use it.Provider correctly defaults to
DEFAULT_BASE_URL
. Verify there are no hard-codedhttps://api.thirdweb.com
usages elsewhere.
52-71
: ThirdwebAiMessage shape aligns with tools and UI usage.Metadata includes
session_id
and tool namespaces match the README examples.
import { ArrowDownIcon } from "lucide-react"; | ||
import type { ComponentProps } from "react"; | ||
import { useCallback } from "react"; | ||
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom"; | ||
import { Button } from "@/components/ui/button"; | ||
import { cn } from "@/lib/utils"; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add explicit return types per guidelines.
Per repo TS guidelines, annotate component return types.
Apply this diff:
-import { useCallback } from "react";
+import { useCallback, type ReactElement } from "react";
-export const Conversation = ({ className, ...props }: ConversationProps) => (
+export const Conversation = (
+ { className, ...props }: ConversationProps,
+): ReactElement => (
<StickToBottom
className={cn("relative flex-1 overflow-y-auto", className)}
initial="smooth"
resize="smooth"
role="log"
{...props}
/>
);
-export const ConversationContent = ({
- className,
- ...props
-}: ConversationContentProps) => (
+export const ConversationContent = (
+ { className, ...props }: ConversationContentProps,
+): ReactElement => (
<StickToBottom.Content className={cn("p-4", className)} {...props} />
);
-export const ConversationScrollButton = ({
- className,
- ...props
-}: ConversationScrollButtonProps) => {
+export const ConversationScrollButton = (
+ { className, ...props }: ConversationScrollButtonProps,
+): ReactElement | null => {
Also applies to: 12-20, 26-31, 35-38
🤖 Prompt for AI Agents
In apps/playground-web/src/components/conversation.tsx around lines 3-9 (and
also apply to ranges 12-20, 26-31, 35-38), the review requests adding explicit
TypeScript return types for the exported/defined React components and any
component-like functions; update each component/function signature to annotate
the return type (e.g., React.ReactElement or JSX.Element as per repo guidelines)
and any relevant handler props typed with ComponentProps where needed so all
component declarations include an explicit return type.
import type { HTMLAttributes } from "react"; | ||
import { cn } from "@/lib/utils"; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add 'use client' directive for this browser component.
Marks this as a client component per app guidelines.
+ 'use client';
import type { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";
🤖 Prompt for AI Agents
In apps/playground-web/src/components/loader.tsx around lines 1 to 3, this
component needs the React "use client" directive to be treated as a client
component; add a literal 'use client' line as the very first line of the file
(above all imports) so the Next.js/app router recognizes it as a browser
component.
const LoaderIcon = ({ size = 16 }: LoaderIconProps) => ( | ||
<svg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add explicit return type to LoaderIcon.
Aligns with TS guideline for explicit returns.
-const LoaderIcon = ({ size = 16 }: LoaderIconProps) => (
+const LoaderIcon = ({ size = 16 }: LoaderIconProps): JSX.Element => (
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const LoaderIcon = ({ size = 16 }: LoaderIconProps) => ( | |
<svg | |
const LoaderIcon = ({ size = 16 }: LoaderIconProps): JSX.Element => ( | |
<svg |
🤖 Prompt for AI Agents
In apps/playground-web/src/components/loader.tsx around lines 8 to 9, the
LoaderIcon arrow function lacks an explicit TypeScript return type; update its
signature to include an explicit return type (e.g., : JSX.Element or :
React.FC<LoaderIconProps>) so the component's return is typed, keeping the
existing props typing and ensuring any necessary React import is present if
using React.FC.
size?: number; | ||
}; | ||
|
||
export const Loader = ({ className, size = 16, ...props }: LoaderProps) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add explicit return type to Loader.
Matches repository TypeScript conventions.
-export const Loader = ({ className, size = 16, ...props }: LoaderProps) => (
+export const Loader = ({ className, size = 16, ...props }: LoaderProps): JSX.Element => (
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const Loader = ({ className, size = 16, ...props }: LoaderProps) => ( | |
export const Loader = ({ className, size = 16, ...props }: LoaderProps): JSX.Element => ( |
🤖 Prompt for AI Agents
In apps/playground-web/src/components/loader.tsx around line 86, the Loader
component lacks an explicit return type; update the function signature to
declare its return type (e.g., add ": JSX.Element" or "React.ReactElement" after
the parameter list) to match repo TypeScript conventions, and adjust imports if
necessary.
export type PromptInputTextareaProps = ComponentProps<typeof Textarea> & { | ||
minHeight?: number; | ||
maxHeight?: number; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove unused minHeight
/maxHeight
props or implement them without inline styles.
These props are declared and defaulted but never applied. Given “Tailwind only – no inline styles,” either drop them or replace with fixed Tailwind classes.
-export type PromptInputTextareaProps = ComponentProps<typeof Textarea> & {
- minHeight?: number;
- maxHeight?: number;
-};
+export type PromptInputTextareaProps = ComponentProps<typeof Textarea>;
@@
export const PromptInputTextarea = ({
onChange,
+ onKeyDown: userOnKeyDown,
className,
placeholder = "What would you like to know?",
- minHeight = 48,
- maxHeight = 164,
...props
}: PromptInputTextareaProps) => {
@@
<Textarea
className={cn(
"w-full resize-none rounded-none border-none p-3 shadow-none outline-none ring-0",
"field-sizing-content max-h-[6lh] bg-transparent dark:bg-transparent",
"focus-visible:ring-0",
className,
)}
name="message"
- onChange={(e) => {
- onChange?.(e);
- }}
- onKeyDown={handleKeyDown}
- placeholder={placeholder}
- {...props}
+ {...props}
+ onChange={(e) => onChange?.(e)}
+ onKeyDown={(e) => {
+ userOnKeyDown?.(e);
+ handleKeyDown(e);
+ }}
+ placeholder={placeholder}
/>
If you do want adjustable heights later, consider a small Tailwind preset (e.g., size variants) rather than runtime numbers.
Also applies to: 43-45, 69-83
let Icon = <SendIcon className="size-4" />; | ||
|
||
if (status === "submitted") { | ||
Icon = <Loader2Icon className="size-4 animate-spin" />; | ||
} else if (status === "streaming") { | ||
Icon = <SquareIcon className="size-4" />; | ||
} else if (status === "error") { | ||
Icon = <XIcon className="size-4" />; | ||
} | ||
|
||
return ( | ||
<Button | ||
className={cn("gap-1.5 rounded-lg", className)} | ||
size={size} | ||
type="submit" | ||
variant={variant} | ||
{...props} | ||
> | ||
{children ?? Icon} | ||
</Button> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Submit button should not submit while streaming; disable during submit and add accessible label.
- When
status === "streaming"
, clicking should stop, not submit. - Disable during
"submitted"
to avoid duplicate posts. - Provide an
aria-label
when rendering icon-only.
let Icon = <SendIcon className="size-4" />;
if (status === "submitted") {
Icon = <Loader2Icon className="size-4 animate-spin" />;
} else if (status === "streaming") {
Icon = <SquareIcon className="size-4" />;
} else if (status === "error") {
Icon = <XIcon className="size-4" />;
}
+ const typeAttr: "button" | "submit" =
+ status === "streaming" ? "button" : "submit";
+ const isSubmitting = status === "submitted";
+ const aria = !children
+ ? status === "streaming"
+ ? "Stop"
+ : status === "submitted"
+ ? "Sending…"
+ : status === "error"
+ ? "Error"
+ : "Send"
+ : undefined;
+
return (
<Button
className={cn("gap-1.5 rounded-lg", className)}
size={size}
- type="submit"
+ type={typeAttr}
+ disabled={isSubmitting}
+ aria-label={aria}
variant={variant}
{...props}
>
{children ?? Icon}
</Button>
);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
let Icon = <SendIcon className="size-4" />; | |
if (status === "submitted") { | |
Icon = <Loader2Icon className="size-4 animate-spin" />; | |
} else if (status === "streaming") { | |
Icon = <SquareIcon className="size-4" />; | |
} else if (status === "error") { | |
Icon = <XIcon className="size-4" />; | |
} | |
return ( | |
<Button | |
className={cn("gap-1.5 rounded-lg", className)} | |
size={size} | |
type="submit" | |
variant={variant} | |
{...props} | |
> | |
{children ?? Icon} | |
</Button> | |
); | |
let Icon = <SendIcon className="size-4" />; | |
if (status === "submitted") { | |
Icon = <Loader2Icon className="size-4 animate-spin" />; | |
} else if (status === "streaming") { | |
Icon = <SquareIcon className="size-4" />; | |
} else if (status === "error") { | |
Icon = <XIcon className="size-4" />; | |
} | |
const typeAttr: "button" | "submit" = | |
status === "streaming" ? "button" : "submit"; | |
const isSubmitting = status === "submitted"; | |
const aria = !children | |
? status === "streaming" | |
? "Stop" | |
: status === "submitted" | |
? "Sending…" | |
: status === "error" | |
? "Error" | |
: "Send" | |
: undefined; | |
return ( | |
<Button | |
className={cn("gap-1.5 rounded-lg", className)} | |
size={size} | |
type={typeAttr} | |
disabled={isSubmitting} | |
aria-label={aria} | |
variant={variant} | |
{...props} | |
> | |
{children ?? Icon} | |
</Button> | |
); |
🤖 Prompt for AI Agents
In apps/playground-web/src/components/prompt-input.tsx around lines 154 to 174,
the submit button currently still acts as a form submit while status ===
"streaming" and can be double-submitted during "submitted", and there is no
accessible label when only an icon is rendered; update the Button so that when
status === "streaming" its type is changed to "button" (so clicking will not
submit) and clicking can be wired to a stop/abort handler via an onClick prop,
set the Button disabled prop when status === "submitted" to prevent duplicate
submissions, and ensure an aria-label prop is provided whenever children is null
(icon-only) describing the action (e.g., "Send message" or "Stop streaming"
depending on status).
b2d2227
to
cd44cf6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
♻️ Duplicate comments (9)
packages/ai-sdk-provider/src/provider.ts (4)
200-208
: Advertise SSE in streaming requests (Accept header)- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "text/event-stream" },
358-365
: Expose session_id via response-metadata.metadata, not id- if (parsed.session_id) { - controller.enqueue({ - type: "response-metadata", - id: parsed.session_id || "", - }); - } + if (parsed.session_id) { + controller.enqueue({ + type: "response-metadata", + metadata: { session_id: parsed.session_id }, + }); + }
433-440
: Do not JSON.stringify tool inputs; pass objectscontroller.enqueue({ type: "tool-call", toolCallId, toolName, - input: JSON.stringify(input), + input, providerExecuted: false, });
67-72
: Flatten system message content to stringthirdweb expects a string; system.content may be an array. Join text parts like user/assistant.
- case "system": - return { - role: message.role, - content: message.content, - }; + case "system": { + const content = Array.isArray(message.content) + ? message.content + .map((p) => (p?.type === "text" ? p.text : "")) + .join("") + : String(message.content ?? ""); + return { role: "system", content }; + }packages/ai-sdk-provider/README.md (1)
92-101
: Transport payload key — using body is correct with DefaultChatTransportMatches current Vercel AI SDK behavior. Example looks good.
apps/portal/src/app/ai/chat/ai-sdk/page.mdx (2)
114-116
: Add React key when mapping messages- {messages.map((message) => ( - <RenderMessage message={message} /> + {messages.map((message) => ( + <RenderMessage key={message.id} message={message} /> ))}
112-120
: Fix duplicatereturn
and trailing semicolon- return return ( + return ( @@ - );; + );packages/ai-sdk-provider/package.json (2)
16-20
: Use explicit “require” condition in exports (avoid “default”)"exports": { ".": { "types": "./dist/types/exports/thirdweb.d.ts", "import": "./dist/esm/exports/thirdweb.js", - "default": "./dist/cjs/exports/thirdweb.js" + "require": "./dist/cjs/exports/thirdweb.js" }, "./package.json": "./package.json" },
1-11
: Add runtime dep on tslib (importHelpers) to prevent runtime errors{ "author": "thirdweb eng <eng@thirdweb.com>", @@ - "devDependencies": { + "dependencies": { + "tslib": "^2.6.2" + }, + "devDependencies": {
🧹 Nitpick comments (6)
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts (1)
174-177
: Type the wallet stub to Wallet for safer checksAnnotate walletWithoutAccount as Wallet to keep structural typing explicit and catch shape drift at compile time.
- const walletWithoutAccount = { + const walletWithoutAccount: Wallet = { ...mockWallet, getAccount: () => undefined, };apps/playground-web/src/components/reasoning.tsx (3)
115-146
: Better UX/a11y in trigger: pluralize “second(s)” and announce streaming.return ( <CollapsibleTrigger - className={cn( + className={cn( "flex items-center gap-2 text-muted-foreground text-sm", className, )} + aria-busy={isStreaming} {...props} > @@ - {isStreaming || duration === 0 ? ( + {isStreaming || duration === 0 ? ( <p>Thinking...</p> ) : ( - <p>Thought for {duration} seconds</p> + <p> + Thought for {duration} {duration === 1 ? "second" : "seconds"} + </p> )}
43-44
: Type the memoized components explicitly; consider explicit return types.-export const Reasoning = memo( +export const Reasoning = memo<ReasoningProps>( @@ -export const ReasoningTrigger = memo( +export const ReasoningTrigger = memo<ReasoningTriggerProps>( @@ -export const ReasoningContent = memo( +export const ReasoningContent = memo<ReasoningContentProps>(Optionally prefer named functions with explicit return types per guidelines:
export function ReasoningComponent(props: ReasoningProps): JSX.Element { ... } export const Reasoning = memo(ReasoningComponent);Also applies to: 115-116, 154-155
113-167
: Consider splitting into focused files (Reasoning, Trigger, Content).This aligns with “one stateless, single-responsibility function per file” and improves reusability.
packages/ai-sdk-provider/src/provider.ts (1)
94-108
: Tool result serialization is overly verboseFor tool messages, you stringify the whole part. Consider emitting only the tool result payload to reduce noise.
- const toolContent = message.content - .map((part) => { - if (part.type === "tool-result") { - return JSON.stringify(part); - } - return ""; - }) - .join(""); + const toolContent = message.content + .map((p) => (p.type === "tool-result" ? JSON.stringify(p.result ?? p) : "")) + .join("");apps/portal/src/app/ai/chat/ai-sdk/page.mdx (1)
96-106
: sendMessage wrapper — keep using body; minor cleanup- const send = (message: string) => { - sendMessage( + const send = (message: string) => { + sendMessage( { text: message }, { body: { // send session id for continuity sessionId, }, }, ); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (35)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/knip.json
(2 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)apps/portal/src/app/ai/chat/ai-sdk/page.mdx
(1 hunks)apps/portal/src/app/ai/sidebar.tsx
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
(2 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)packages/thirdweb/src/wallets/wallet-connect/controller.ts
(2 hunks)packages/ui/package.json
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- packages/ui/package.json
- .changeset/many-pants-tease.md
🚧 Files skipped from review as they are similar to previous changes (26)
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/portal/package.json
- apps/playground-web/src/app/navLinks.ts
- packages/ai-sdk-provider/src/types.ts
- packages/ai-sdk-provider/tsconfig.base.json
- apps/dashboard/package.json
- apps/playground-web/src/components/code/code-example.tsx
- packages/ai-sdk-provider/tsconfig.build.json
- apps/playground-web/src/components/ui/avatar.tsx
- apps/nebula/package.json
- apps/playground-web/src/components/response.tsx
- apps/playground-web/knip.json
- packages/ai-sdk-provider/tsconfig.json
- apps/playground-web/package.json
- apps/playground-web/src/components/loader.tsx
- packages/ai-sdk-provider/src/tools.ts
- packages/thirdweb/src/wallets/wallet-connect/controller.ts
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- package.json
- packages/ai-sdk-provider/biome.json
- apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
- apps/playground-web/src/components/message.tsx
- apps/playground-web/src/components/conversation.tsx
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/src/components/prompt-input.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/portal/src/app/ai/sidebar.tsx
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
apps/playground-web/src/components/reasoning.tsx
packages/ai-sdk-provider/src/provider.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/portal/src/app/ai/sidebar.tsx
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
apps/playground-web/src/components/reasoning.tsx
packages/ai-sdk-provider/src/provider.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}
: Place tests alongside code:foo.ts
↔foo.test.ts
Use real function invocations with stub data in tests; avoid brittle mocks
Use Mock Service Worker (MSW) for fetch/HTTP call interception in tests
Keep tests deterministic and side-effect free
UseFORKED_ETHEREUM_CHAIN
for mainnet interactions andANVIL_CHAIN
for isolated tests
Files:
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/components/reasoning.tsx
🧠 Learnings (9)
📚 Learning: 2025-08-28T20:50:33.142Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx:167-181
Timestamp: 2025-08-28T20:50:33.142Z
Learning: The thirdweb-dev/ai-sdk-provider schemas use snake_case field naming convention (e.g., chain_id, transaction_hash) rather than camelCase, as defined in the zod schemas in packages/ai-sdk-provider/src/tools.ts.
Applied to files:
apps/portal/src/app/ai/chat/ai-sdk/page.mdx
packages/ai-sdk-provider/package.json
packages/ai-sdk-provider/src/provider.ts
packages/ai-sdk-provider/README.md
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to test/src/test-wallets.ts : Predefined test accounts are in `test/src/test-wallets.ts`
Applied to files:
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : EIP-1193, EIP-5792, EIP-7702 standard support in wallet modules
Applied to files:
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Unified `Wallet` and `Account` interfaces in wallet architecture
Applied to files:
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Smart wallets with account abstraction
Applied to files:
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/exports/** : Export everything via `exports/` directory, grouped by feature in the SDK public API
Applied to files:
packages/ai-sdk-provider/package.json
📚 Learning: 2025-08-28T20:50:33.142Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx:167-181
Timestamp: 2025-08-28T20:50:33.142Z
Learning: The SignTransactionInput schema in thirdweb-dev/ai-sdk-provider uses snake_case field names (chain_id) rather than camelCase (chainId).
Applied to files:
packages/ai-sdk-provider/src/provider.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/wagmi-adapter/** : Wagmi ecosystem integration is in `packages/wagmi-adapter/`
Applied to files:
packages/ai-sdk-provider/README.md
📚 Learning: 2025-08-28T20:33:35.084Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: packages/ai-sdk-provider/README.md:92-101
Timestamp: 2025-08-28T20:33:35.084Z
Learning: The Vercel AI SDK's DefaultChatTransport uses `body` (not `data`) for the transport payload when calling sendMessage with additional data like sessionId.
Applied to files:
packages/ai-sdk-provider/README.md
🧬 Code graph analysis (3)
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts (1)
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts (1)
toProvider
(58-163)
apps/playground-web/src/components/reasoning.tsx (1)
apps/playground-web/src/components/response.tsx (1)
Response
(9-20)
packages/ai-sdk-provider/src/provider.ts (2)
packages/ai-sdk-provider/src/types.ts (3)
ThirdwebConfig
(12-29)ThirdwebSettings
(31-50)DEFAULT_BASE_URL
(10-10)packages/ai-sdk-provider/src/tools.ts (4)
SignTransactionInput
(77-79)SignSwapInput
(80-80)MonitorTransactionInput
(81-83)createTools
(51-75)
🪛 LanguageTool
packages/ai-sdk-provider/README.md
[grammar] ~7-~7: There might be a mistake here.
Context: ...nd and can interact with blockchain data - Transaction execution: Sign and execut...
(QB_NEW_EN)
[grammar] ~8-~8: There might be a mistake here.
Context: ... transactions directly from AI responses - Token swaps: Perform token swaps throu...
(QB_NEW_EN)
[grammar] ~9-~9: There might be a mistake here.
Context: ...token swaps through AI-generated actions - Transaction monitoring: Track transact...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ...ack transaction status and confirmations - Session continuity: Maintain conversat...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...conversation context across interactions - Type safety: Full TypeScript support w...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ...peScript support with typed tool results - Streaming support: Real-time AI respon...
(QB_NEW_EN)
[grammar] ~242-~242: There might be a mistake here.
Context: ...Sign and execute blockchain transactions - sign_swap
: Perform token swaps - **`monitor_trans...
(QB_NEW_EN)
[grammar] ~243-~243: There might be a mistake here.
Context: ...s - sign_swap
: Perform token swaps - monitor_transaction
: Track transaction status and confirmat...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...//playground.thirdweb.com/ai/chat/ai-sdk](https://playground.thirdweb.com/ai/chat/ai-sdk) - Documentation: [https://portal.thirdwe...
(QB_NEW_EN)
[grammar] ~249-~249: There might be a mistake here.
Context: ...tps://portal.thirdweb.com/ai/chat/ai-sdk](https://portal.thirdweb.com/ai/chat/ai-sdk) - Vercel AI SDK: [https://ai-sdk.dev/doc...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Unit Tests
- GitHub Check: Size
- GitHub Check: Build Packages
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (4)
packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts (1)
173-191
: LGTM: eth_accounts returns [] when not connectedThe test correctly reflects the new behavior of returning an empty array when no account is connected.
apps/playground-web/src/components/reasoning.tsx (1)
148-152
: No change needed:Response
only accepts string children
Streamdown’schildren
prop is defined asstring
, so keepingchildren: string
inReasoningContentProps
is correct (npmjs.com).apps/portal/src/app/ai/sidebar.tsx (1)
28-31
: Add AI SDK nav entry — LGTMAccurate label, path, and placement. No issues.
packages/ai-sdk-provider/src/provider.ts (1)
398-408
: Confirm key casing matches tool schemaYou’re using snake_case (chain_id, etc.). That aligns with current zod schemas in this repo; keep as-is. If schemas change to camelCase, this block must follow.
import { useControllableState } from "@radix-ui/react-use-controllable-state"; | ||
import { BrainIcon, ChevronDownIcon } from "lucide-react"; | ||
import type { ComponentProps } from "react"; | ||
import { createContext, memo, useContext, useEffect, useState } from "react"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Import useRef
for the new refs.
-import { createContext, memo, useContext, useEffect, useState } from "react";
+import { createContext, memo, useContext, useEffect, useRef, useState } from "react";
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import { createContext, memo, useContext, useEffect, useState } from "react"; | |
import { createContext, memo, useContext, useEffect, useRef, useState } from "react"; |
🤖 Prompt for AI Agents
In apps/playground-web/src/components/reasoning.tsx around line 6, the new refs
require React's useRef but the import list only includes createContext, memo,
useContext, useEffect, useState; update the import statement to also import
useRef from "react" (i.e., add useRef to the destructured imports) so the newly
added refs can be created without errors.
export type ReasoningProps = ComponentProps<typeof Collapsible> & { | ||
isStreaming?: boolean; | ||
open?: boolean; | ||
defaultOpen?: boolean; | ||
onOpenChange?: (open: boolean) => void; | ||
duration?: number; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid prop duplication with Collapsible
; add control hooks for duration and auto-close.
Intersecting the same keys from ComponentProps<typeof Collapsible>
can cause brittle types. Also expose onDurationChange
and autoClose
.
-export type ReasoningProps = ComponentProps<typeof Collapsible> & {
+export type ReasoningProps = Omit<
+ ComponentProps<typeof Collapsible>,
+ "open" | "onOpenChange" | "defaultOpen"
+> & {
isStreaming?: boolean;
open?: boolean;
defaultOpen?: boolean;
onOpenChange?: (open: boolean) => void;
duration?: number;
+ onDurationChange?: (duration: number) => void;
+ autoClose?: boolean;
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export type ReasoningProps = ComponentProps<typeof Collapsible> & { | |
isStreaming?: boolean; | |
open?: boolean; | |
defaultOpen?: boolean; | |
onOpenChange?: (open: boolean) => void; | |
duration?: number; | |
}; | |
export type ReasoningProps = Omit< | |
ComponentProps<typeof Collapsible>, | |
"open" | "onOpenChange" | "defaultOpen" | |
> & { | |
isStreaming?: boolean; | |
open?: boolean; | |
defaultOpen?: boolean; | |
onOpenChange?: (open: boolean) => void; | |
duration?: number; | |
onDurationChange?: (duration: number) => void; | |
autoClose?: boolean; | |
}; |
🤖 Prompt for AI Agents
In apps/playground-web/src/components/reasoning.tsx around lines 32 to 38, avoid
duplicating prop keys from ComponentProps<typeof Collapsible> by Omit-ing the
open/defaultOpen/onOpenChange keys and then extend with your own controlled
props; replace the current type with something like Omit<ComponentProps<typeof
Collapsible>, 'open'|'defaultOpen'|'onOpenChange'> & { isStreaming?: boolean;
open?: boolean; defaultOpen?: boolean; onOpenChange?: (open: boolean) => void;
duration?: number; onDurationChange?: (duration: number) => void; autoClose?:
boolean; }; update any usages/consumers to pass onDurationChange and autoClose
where needed and ensure internal logic reads duration from props and calls
onDurationChange when it changes.
export function RenderMessage(props: { | ||
message: ThirdwebAiMessage; | ||
sessionId: string; | ||
addToolResult: (toolResult: ThirdwebAiMessage) => void; | ||
sendMessage: (message: string) => void; | ||
}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prop types don’t match usage
-export function RenderMessage(props: {
- message: ThirdwebAiMessage;
- sessionId: string;
- addToolResult: (toolResult: ThirdwebAiMessage) => void;
- sendMessage: (message: string) => void;
-}) {
- const { message, sessionId, addToolResult, sendMessage } = props;
+export function RenderMessage(props: {
+ message: ThirdwebAiMessage;
+ sessionId: string;
+ addToolResult: (toolResult: any) => void;
+ send: (message: string) => void;
+}) {
+ const { message, sessionId, addToolResult, send } = props;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export function RenderMessage(props: { | |
message: ThirdwebAiMessage; | |
sessionId: string; | |
addToolResult: (toolResult: ThirdwebAiMessage) => void; | |
sendMessage: (message: string) => void; | |
}) { | |
export function RenderMessage(props: { | |
message: ThirdwebAiMessage; | |
sessionId: string; | |
addToolResult: (toolResult: any) => void; | |
send: (message: string) => void; | |
}) { | |
const { message, sessionId, addToolResult, send } = props; | |
// …rest of function body | |
} |
onTransactionSent={(transaction) => { | ||
// add the tool result to the messages array | ||
addToolResult({ | ||
tool: "sign_transaction", | ||
toolCallId, | ||
output: { | ||
transaction_hash: transaction.transactionHash, | ||
chain_id: transaction.chain.id, | ||
}, | ||
}); | ||
// send the message to the model to continue the conversation | ||
sendMessage(undefined, { | ||
body: { | ||
// send session id for continuity | ||
sessionId, | ||
}, | ||
}); | ||
}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undefined variable and wrong wrapper usage in tool flow
- addToolResult({
- tool: "sign_transaction",
- toolCallId,
+ addToolResult({
+ tool: "sign_transaction",
+ toolCallId: part.toolCallId,
output: {
transaction_hash: transaction.transactionHash,
chain_id: transaction.chain.id,
},
});
- // send the message to the model to continue the conversation
- sendMessage(undefined, {
- body: {
- // send session id for continuity
- sessionId,
- },
- });
+ // continue the conversation
+ send("");
@@
- // in case of error, send the error message to the model
- sendMessage(
- { text: `Transaction failed: ${error.message}` },
- {
- body: {
- sessionId,
- },
- },
- );
+ // in case of error, continue with an error message
+ send(`Transaction failed: ${error.message}`);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
onTransactionSent={(transaction) => { | |
// add the tool result to the messages array | |
addToolResult({ | |
tool: "sign_transaction", | |
toolCallId, | |
output: { | |
transaction_hash: transaction.transactionHash, | |
chain_id: transaction.chain.id, | |
}, | |
}); | |
// send the message to the model to continue the conversation | |
sendMessage(undefined, { | |
body: { | |
// send session id for continuity | |
sessionId, | |
}, | |
}); | |
}} | |
onTransactionSent={(transaction) => { | |
// add the tool result to the messages array | |
addToolResult({ | |
tool: "sign_transaction", | |
toolCallId: part.toolCallId, | |
output: { | |
transaction_hash: transaction.transactionHash, | |
chain_id: transaction.chain.id, | |
}, | |
}); | |
// continue the conversation | |
send(""); | |
}} | |
// in case of error, continue with an error message | |
send(`Transaction failed: ${error.message}`); |
const walletWithoutAccount = { | ||
...mockWallet, | ||
getAccount: () => undefined, | ||
}; | ||
|
||
const customConnect = vi.fn().mockResolvedValue(walletWithoutAccount); | ||
|
||
const provider = toProvider({ | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
connectOverride: customConnect, | ||
wallet: mockWallet, | ||
wallet: walletWithoutAccount, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: customConnect returns a Wallet, but provider expects an Account
connectOverride must resolve to an Account (with .address). Returning walletWithoutAccount will produce [undefined] for eth_requestAccounts and hides the “Unable to connect wallet” error path. Also assert the returned accounts and that wallet.connect wasn’t called.
- const walletWithoutAccount = {
+ const walletWithoutAccount: Wallet = {
...mockWallet,
getAccount: () => undefined,
};
- const customConnect = vi.fn().mockResolvedValue(walletWithoutAccount);
+ // isolate mock call counts for this test
+ vi.clearAllMocks();
+ // connectOverride should return an Account
+ const customConnect = vi.fn().mockResolvedValue(mockAccount);
@@
const provider = toProvider({
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
connectOverride: customConnect,
wallet: walletWithoutAccount,
});
- await provider.request({
- method: "eth_requestAccounts",
- params: [],
- });
+ await expect(
+ provider.request({
+ method: "eth_requestAccounts",
+ params: [],
+ }),
+ ).resolves.toEqual([mockAccount.address]);
expect(customConnect).toHaveBeenCalledWith(walletWithoutAccount);
+ // ensure fallback wallet.connect was not used
+ expect(walletWithoutAccount.connect).not.toHaveBeenCalled();
Also applies to: 213-213
🤖 Prompt for AI Agents
In packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts around lines
194-206 (and also adjust the similar case at line 213), the test's
connectOverride mock resolves to a Wallet-like object without an account, but
the provider expects an Account (with .address). Update the mockResolvedValue to
return an Account object containing a valid address (not walletWithoutAccount),
change assertions to verify the returned accounts array contains that address,
and add an assertion that wallet.connect was not called; ensure the alternate
case at line 213 is updated the same way.
cd44cf6
to
931a85e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/thirdweb/src/wallets/wallet-connect/controller.ts (4)
108-112
: Ensure display_uri listener is always cleaned up (try/finally) to avoid leaks on failed connect.
Today the listener is removed only after a successful flow; failures before Line 189 leave it registered.Apply:
if (onDisplayUri) { provider.events.addListener("display_uri", onDisplayUri); } - // For UniversalProvider, we need to connect with namespaces - await provider.connect({ + // For UniversalProvider, we need to connect with namespaces + try { + await provider.connect({ ...(wcOptions?.pairingTopic ? { pairingTopic: wcOptions?.pairingTopic } : {}), namespaces: { [NAMESPACE]: { chains: chainsToRequest, events: ["chainChanged", "accountsChanged"], methods: [ "eth_sendTransaction", "eth_signTransaction", "eth_sign", "personal_sign", "eth_signTypedData", "eth_signTypedData_v4", "wallet_switchEthereumChain", "wallet_addEthereumChain", ], rpcMap, }, }, - }); + }); + } finally { + if (onDisplayUri) { + provider.events.removeListener("display_uri", onDisplayUri); + } + } @@ - if (onDisplayUri) { - provider.events.removeListener("display_uri", onDisplayUri); - }Also applies to: 130-152, 189-191
501-507
: Listener leak: session_delete handler is never removed on disconnect.
You add it on Line 542 but don’t remove it in disconnect().Apply:
async function disconnect() { provider.removeListener("accountsChanged", onAccountsChanged); provider.removeListener("chainChanged", onChainChanged); provider.removeListener("disconnect", onDisconnect); + provider.removeListener("session_delete", onDisconnect); await provider.disconnect(); cachedProvider = null; }
Also applies to: 539-543
399-419
: Don’t rely onthis
inside object-literal methods; use closed-overaddress
.
DestructuringsignMessage
would breakthis.address
. Use the capturedaddress
consistently.Apply:
return requestAndOpenWallet({ provider, payload: { method: "personal_sign", - params: [messageToSign, this.address], + params: [messageToSign, getAddress(address)], }, chain: `eip155:${chain.id}`, walletInfo, sessionRequestHandler, });
441-446
: Same here: removethis
usage in signTypedData.
Prevents brittle call-site dependencies.Apply:
return await requestAndOpenWallet({ provider, payload: { method: "eth_signTypedData_v4", - params: [this.address, typedData], + params: [getAddress(address), typedData], }, chain: `eip155:${chain.id}`, walletInfo, sessionRequestHandler, });
♻️ Duplicate comments (9)
packages/ai-sdk-provider/package.json (1)
15-20
: Use “require” condition instead of “default” in exports map.Replace the CJS condition key to avoid resolver/bundler ambiguity.
"exports": { ".": { "types": "./dist/types/exports/thirdweb.d.ts", "import": "./dist/esm/exports/thirdweb.js", - "default": "./dist/cjs/exports/thirdweb.js" + "require": "./dist/cjs/exports/thirdweb.js" }, "./package.json": "./package.json" },packages/ai-sdk-provider/src/provider.ts (4)
358-365
: Expose session_id via metadata, not id, in response-metadata events.Downstream consumers expect metadata for auxiliary values.
- if (parsed.session_id) { - controller.enqueue({ - type: "response-metadata", - id: parsed.session_id || "", - }); - } + if (parsed.session_id) { + controller.enqueue({ + type: "response-metadata", + metadata: { session_id: parsed.session_id }, + }); + }
200-208
: SSE requests should advertise Accept: text/event-stream.Helps intermediaries and servers route streaming responses correctly.
- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "text/event-stream" },
433-441
: Do not JSON.stringify tool-call input; emit the object.Stringifying breaks consumer typing (e.g., UI reading part.input as an object).
controller.enqueue({ type: "tool-call", toolCallId, toolName, - input: JSON.stringify(input), + input, providerExecuted: false, });
64-72
: Flatten system message content to string (align with user/assistant).Thirdweb API likely expects a string; arrays can leak through here.
- case "system": - return { - role: message.role, - content: message.content, - }; + case "system": { + const content = Array.isArray(message.content) + ? message.content.map((p) => (p.type === "text" ? p.text : "")).join("") + : String(message.content ?? ""); + return { role: "system", content }; + }apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (4)
189-194
: Use Tailwind, not inline styles (project guideline).- <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton className="w-full"
31-32
: Potential secret leak: don’t import a client that may embed secretKey in a client component.Swap to a public client (clientId-only) for the browser.
-import { THIRDWEB_CLIENT } from "../../../../lib/client"; +// Use a client-safe instance that never bundles secretKey +import { THIRDWEB_PUBLIC_CLIENT as THIRDWEB_CLIENT } from "@/lib/public-client";Outside this file, add a client-safe factory:
// apps/playground-web/src/lib/public-client.ts "use client"; import { createThirdwebClient } from "thirdweb"; export const THIRDWEB_PUBLIC_CLIENT = createThirdwebClient({ clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID as string, });
184-201
: Guard invalid transaction payloads before calling prepareTransaction.Prevents runtime errors when tool input is incomplete.
if (!account) { return <ConnectButton client={THIRDWEB_CLIENT} />; } + const isValidTx = + Boolean(transactionData.to) || + transactionData.data !== "0x" || + transactionData.value > 0n; + if (!isValidTx) { + return ( + <div className="py-4 text-sm text-muted-foreground"> + Waiting for transaction details… + </div> + ); + }
3-6
: Import DefaultChatTransport from @ai-sdk/react (current import is wrong).The symbol isn’t exported from "ai".
-import { useChat } from "@ai-sdk/react"; -import type { ThirdwebAiMessage } from "@thirdweb-dev/ai-sdk-provider"; -import { DefaultChatTransport } from "ai"; +import { useChat, DefaultChatTransport } from "@ai-sdk/react"; +import type { ThirdwebAiMessage } from "@thirdweb-dev/ai-sdk-provider";
🧹 Nitpick comments (16)
apps/playground-web/knip.json (2)
3-3
: Broadening ignore to src/components/ may hide dead code across all components**Confirm this is intentional. This suppresses unused-export/unused-file detection for the entire components tree. Prefer targeted ignores (only noisy subtrees or patterns like stories/mdx) to retain coverage on the rest of the components.
If the goal is to silence Storybook/docs noise, consider a narrower rule set, e.g.:
- "ignore": ["src/components/**", "src/app/insight/utils.ts"], + "ignore": [ + "src/components/ui/**", + "src/components/**/{*.stories.*,*.mdx}", + "src/app/insight/utils.ts" + ],Would you like me to scan the repo tree and propose an ignore list based on actual subfolders (icons, primitives, chat, etc.)?
12-13
: Ignoring @thirdweb-dev/ai-sdk-provider in ignoreDependencies can mask real unused-dep driftOnly keep this if Knip is false-positive due to dynamic import or runtime-only usage. Otherwise, let Knip track it. Alternative: help Knip discover usage by adding concrete entries for the server route(s) that import the provider.
Example adjustment:
"next": true, - "project": ["src/**"] + "project": ["src/**"], + "entry": [ + "src/app/ai/api/route.ts", + "src/app/**/route.ts" + ]If dynamic imports are the reason, I can craft an entries list for all consuming routes/components to avoid using ignoreDependencies here.
packages/ai-sdk-provider/package.json (5)
23-26
: Tighten published files; avoid shipping TS sources unless intentional.- "files": [ - "dist/*", - "src/*" - ], + "files": [ + "dist/**", + "README.md", + "LICENSE" + ],
56-58
: Remove duplicate “typings” field; “types” is sufficient."type": "module", "types": "./dist/types/exports/thirdweb.d.ts", - "typings": "./dist/types/exports/thirdweb.d.ts",
41-44
: Fix repository metadata and add “directory” for workspace path."repository": { "type": "git", - "url": "git+https://github.com/thirdweb-dev/js.git#main" + "url": "git+https://github.com/thirdweb-dev/js.git", + "directory": "packages/ai-sdk-provider" },
27-29
: Advertise tree-shakeability at the package root."license": "Apache-2.0", + "sideEffects": false, "main": "./dist/cjs/exports/thirdweb.js",
Also applies to: 56-56
59-59
: Ensure public publish for scoped package."version": "0.0.1" + , + "publishConfig": { + "access": "public" + }packages/ai-sdk-provider/README.md (1)
27-39
: Server route example: mark as server-only per project guidelines.Add the server-only directive to avoid accidental client bundling in Next.js App Router.
-// app/api/chat/route.ts +// app/api/chat/route.ts +import "server-only";packages/ai-sdk-provider/src/provider.ts (1)
224-495
: Optional: handle aborts cleanly in stream reader.Cancel the reader on abort to prevent dangling reads.
return { stream: new ReadableStream<LanguageModelV2StreamPart>({ - async start(controller) { + async start(controller) { + const onAbort = () => { + try { reader.cancel(); } catch {} + controller.error(new DOMException("Aborted", "AbortError")); + }; + abortSignal?.addEventListener?.("abort", onAbort, { once: true }); try { // ... } catch (error) { controller.error(error); } finally { reader.releaseLock(); + abortSignal?.removeEventListener?.("abort", onAbort); } }, }), };apps/playground-web/src/components/message.tsx (3)
30-36
: Remove invalid Tailwind tokenis-user:dark
.This class does nothing and clutters the bundle.
"group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground", "group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground", - "is-user:dark", className,
54-57
: Improve avatar fallback readability.Uppercase initials for consistency.
- <AvatarFallback>{name?.slice(0, 2) || "ME"}</AvatarFallback> + <AvatarFallback>{(name?.slice(0, 2) || "ME").toUpperCase()}</AvatarFallback>
6-8
: Constrainfrom
to user|assistant or handle 'system' explicitly.Prevents unintended assistant styling for system messages.
-export type MessageProps = HTMLAttributes<HTMLDivElement> & { - from: UIMessage["role"]; -}; +export type MessageProps = HTMLAttributes<HTMLDivElement> & { + from: Extract<UIMessage["role"], "user" | "assistant"> | "system"; +};apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (4)
108-108
: Remove debug logging before merge.- console.log("---sign_swap", part);
296-301
: Follow-up message payload: keep usingbody
, but consider empty string vs undefined consistently.Both work; choose one convention across file (recommend empty string to signal “continue”).
- sendMessage(undefined, { + sendMessage("", { body: { sessionId, }, });
313-314
: Title-case button label.- Sign swap + Sign Swap
1-318
: Split file into focused components.This client module defines ChatContainer, SignTransactionButton, and SignSwapButton. Prefer single-responsibility per file for maintainability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (36)
.changeset/many-pants-tease.md
(1 hunks).changeset/tricky-onions-jam.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/knip.json
(2 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)apps/portal/src/app/ai/chat/ai-sdk/page.mdx
(1 hunks)apps/portal/src/app/ai/sidebar.tsx
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
(2 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)packages/thirdweb/src/wallets/wallet-connect/controller.ts
(2 hunks)packages/ui/package.json
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- .changeset/tricky-onions-jam.md
- .changeset/many-pants-tease.md
🚧 Files skipped from review as they are similar to previous changes (26)
- packages/ai-sdk-provider/tsconfig.json
- apps/playground-web/src/components/response.tsx
- apps/playground-web/src/components/conversation.tsx
- packages/ai-sdk-provider/tsconfig.build.json
- apps/nebula/package.json
- packages/ui/package.json
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- apps/playground-web/src/components/ui/avatar.tsx
- apps/portal/src/app/ai/sidebar.tsx
- packages/ai-sdk-provider/src/types.ts
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/portal/package.json
- packages/ai-sdk-provider/biome.json
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- apps/playground-web/src/app/navLinks.ts
- packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
- apps/playground-web/src/components/reasoning.tsx
- package.json
- packages/ai-sdk-provider/src/tools.ts
- apps/playground-web/package.json
- apps/playground-web/src/components/code/code-example.tsx
- apps/portal/src/app/ai/chat/ai-sdk/page.mdx
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/src/components/loader.tsx
- packages/ai-sdk-provider/tsconfig.base.json
- apps/playground-web/src/components/prompt-input.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
packages/thirdweb/src/wallets/wallet-connect/controller.ts
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/thirdweb/src/wallets/wallet-connect/controller.ts
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/thirdweb/src/wallets/**
📄 CodeRabbit inference engine (CLAUDE.md)
packages/thirdweb/src/wallets/**
: UnifiedWallet
andAccount
interfaces in wallet architecture
Support for in-app wallets (social/email login)
Smart wallets with account abstraction
EIP-1193, EIP-5792, EIP-7702 standard support in wallet modules
Files:
packages/thirdweb/src/wallets/wallet-connect/controller.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
🧠 Learnings (9)
📓 Common learnings
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx:167-181
Timestamp: 2025-08-28T20:50:33.142Z
Learning: The thirdweb-dev/ai-sdk-provider schemas use snake_case field naming convention (e.g., chain_id, transaction_hash) rather than camelCase, as defined in the zod schemas in packages/ai-sdk-provider/src/tools.ts.
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : EIP-1193, EIP-5792, EIP-7702 standard support in wallet modules
Applied to files:
packages/thirdweb/src/wallets/wallet-connect/controller.ts
📚 Learning: 2025-08-28T20:50:33.142Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx:167-181
Timestamp: 2025-08-28T20:50:33.142Z
Learning: The thirdweb-dev/ai-sdk-provider schemas use snake_case field naming convention (e.g., chain_id, transaction_hash) rather than camelCase, as defined in the zod schemas in packages/ai-sdk-provider/src/tools.ts.
Applied to files:
packages/ai-sdk-provider/src/provider.ts
packages/ai-sdk-provider/package.json
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/README.md
📚 Learning: 2025-08-28T20:50:33.142Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx:167-181
Timestamp: 2025-08-28T20:50:33.142Z
Learning: The SignTransactionInput schema in thirdweb-dev/ai-sdk-provider uses snake_case field names (chain_id) rather than camelCase (chainId).
Applied to files:
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/exports/** : Export everything via `exports/` directory, grouped by feature in the SDK public API
Applied to files:
packages/ai-sdk-provider/package.json
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Prefer API routes or server actions to keep tokens secret; the browser only sees relative paths.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
📚 Learning: 2025-08-28T20:33:35.084Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7922
File: packages/ai-sdk-provider/README.md:92-101
Timestamp: 2025-08-28T20:33:35.084Z
Learning: The Vercel AI SDK's DefaultChatTransport uses `body` (not `data`) for the transport payload when calling sendMessage with additional data like sessionId.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/README.md
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/wagmi-adapter/** : Wagmi ecosystem integration is in `packages/wagmi-adapter/`
Applied to files:
packages/ai-sdk-provider/README.md
🧬 Code graph analysis (2)
packages/ai-sdk-provider/src/provider.ts (2)
packages/ai-sdk-provider/src/types.ts (3)
ThirdwebConfig
(12-29)ThirdwebSettings
(31-50)DEFAULT_BASE_URL
(10-10)packages/ai-sdk-provider/src/tools.ts (4)
SignTransactionInput
(77-79)SignSwapInput
(80-80)MonitorTransactionInput
(81-83)createTools
(51-75)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (6)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(52-71)apps/playground-web/src/components/conversation.tsx (3)
Conversation
(12-20)ConversationContent
(26-31)ConversationScrollButton
(35-62)apps/playground-web/src/components/message.tsx (2)
Message
(10-20)MessageContent
(24-41)apps/playground-web/src/components/prompt-input.tsx (3)
PromptInput
(24-32)PromptInputTextarea
(39-85)PromptInputSubmit
(146-175)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)packages/thirdweb/src/exports/thirdweb.ts (1)
prepareTransaction
(181-181)
🪛 LanguageTool
packages/ai-sdk-provider/README.md
[grammar] ~7-~7: There might be a mistake here.
Context: ...nd and can interact with blockchain data - Transaction execution: Sign and execut...
(QB_NEW_EN)
[grammar] ~8-~8: There might be a mistake here.
Context: ... transactions directly from AI responses - Token swaps: Perform token swaps throu...
(QB_NEW_EN)
[grammar] ~9-~9: There might be a mistake here.
Context: ...token swaps through AI-generated actions - Transaction monitoring: Track transact...
(QB_NEW_EN)
[grammar] ~10-~10: There might be a mistake here.
Context: ...ack transaction status and confirmations - Session continuity: Maintain conversat...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...conversation context across interactions - Type safety: Full TypeScript support w...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ...peScript support with typed tool results - Streaming support: Real-time AI respon...
(QB_NEW_EN)
[grammar] ~242-~242: There might be a mistake here.
Context: ...Sign and execute blockchain transactions - sign_swap
: Perform token swaps - **`monitor_trans...
(QB_NEW_EN)
[grammar] ~243-~243: There might be a mistake here.
Context: ...s - sign_swap
: Perform token swaps - monitor_transaction
: Track transaction status and confirmat...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...//playground.thirdweb.com/ai/chat/ai-sdk](https://playground.thirdweb.com/ai/chat/ai-sdk) - Documentation: [https://portal.thirdwe...
(QB_NEW_EN)
[grammar] ~249-~249: There might be a mistake here.
Context: ...tps://portal.thirdweb.com/ai/chat/ai-sdk](https://portal.thirdweb.com/ai/chat/ai-sdk) - Vercel AI SDK: [https://ai-sdk.dev/doc...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Unit Tests
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (5)
packages/thirdweb/src/wallets/wallet-connect/controller.ts (1)
139-148
: Adding eth_signTypedData_v4 to WC namespace is correct and aligns with signTypedData implementation.
This matches the runtime call in createAccount.signTypedData and prevents “method not allowed” errors on wallets requiring v4.apps/dashboard/package.json (1)
63-63
: Shiki v3 upgrade verified—no legacy APIs and all packages pinned to 3.12.0
No references to deprecated getHighlighter/fromHighlighter or Shiki sub-paths found, and every package.json uses version 3.12.0; no MDX-highlighting plugins detected.packages/ai-sdk-provider/README.md (1)
92-101
: Transport payload key is correct (body
). LGTM.apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (2)
47-60
: OK to usebody
for transport payload (per SDK behavior).
1-318
: Transport import and client key usage are correct
DefaultChatTransport
should indeed be imported from"ai"
, matching the AI SDK docs (e.g. (ai-sdk.dev, v5.ai-sdk.dev)).THIRDWEB_CLIENT
inlib/client
is instantiated with a client-side clientId (no secretKey is exposed).No changes needed.
"devDependencies": { | ||
"@ai-sdk/provider": "2.0.0", | ||
"@ai-sdk/provider-utils": "3.0.7", | ||
"@biomejs/biome": "2.0.6", | ||
"rimraf": "6.0.1" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify runtime vs dev usage for @ai-sdk/provider(+utils) and add tslib if helpers are imported.
If these modules are imported at runtime (not types-only), they must be regular dependencies or peerDependencies; keeping them in devDependencies will break consumers. Also, if tsconfig uses importHelpers, add a runtime dep on tslib.
Run to confirm:
If runtime imports exist, choose ONE path:
- Make them dependencies:
- "devDependencies": {
- "@ai-sdk/provider": "2.0.0",
- "@ai-sdk/provider-utils": "3.0.7",
+ "dependencies": {
+ "@ai-sdk/provider": "^2",
+ "@ai-sdk/provider-utils": "^3"
},
+ "devDependencies": {
"@biomejs/biome": "2.0.6",
"rimraf": "6.0.1"
},
- Or peerDependencies (if you require app-level singletons):
"peerDependencies": {
- "ai": "^5",
+ "ai": "^5",
+ "@ai-sdk/provider": "^2",
+ "@ai-sdk/provider-utils": "^3",
"typescript": ">=5.0.4",
"zod": "^3.25.75 || ^4"
},
If importHelpers is true, add tslib:
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
Also applies to: 31-35
🏁 Script executed:
#!/bin/bash
# Check for runtime imports
rg -nP -C2 --type=ts "from\s+['\"]@ai-sdk/provider(|-utils)['\"]" src || true
# Inspect tsconfig for importHelpers
fd -a 'tsconfig*.json' . | xargs -I{} sh -c 'echo "==> {}"; cat "{}" | sed -n "1,160p" | rg -n "importHelpers|module|target" -n || true'
Length of output: 2142
🏁 Script executed:
#!/bin/bash
set -e
echo "Searching for runtime imports of @ai-sdk/provider-utils in packages/ai-sdk-provider"
rg -nP "from\s+['\"]@ai-sdk/provider-utils['\"]" -n packages/ai-sdk-provider || echo "No imports found"
echo "Searching for require() usage of @ai-sdk/provider-utils"
rg -nP "require\(['\"]@ai-sdk/provider-utils['\"]\)" -n packages/ai-sdk-provider || echo "No require statements found"
echo "Searching for runtime imports of @ai-sdk/provider"
rg -nP "from\s+['\"]@ai-sdk/provider['\"]" -n packages/ai-sdk-provider || echo "No imports of provider found"
rg -nP "require\(['\"]@ai-sdk/provider['\"]\)" -n packages/ai-sdk-provider || echo "No require statements found"
echo "Checking all tsconfig*.json for importHelpers"
rg -n "\"importHelpers\"\s*:" -n .
Length of output: 1832
🏁 Script executed:
#!/bin/bash
echo "=== packages/ai-sdk-provider/src/provider.ts (lines 1-30) ==="
sed -n '1,30p' packages/ai-sdk-provider/src/provider.ts || echo "provider.ts not found"
echo ""
echo "=== packages/ai-sdk-provider/package.json (lines 1-50) ==="
sed -n '1,50p' packages/ai-sdk-provider/package.json || echo "package.json not found"
Length of output: 2836
Move type-only SDK packages into peerDependencies
and add tslib
to dependencies
.
Both @ai-sdk/provider
and @ai-sdk/provider-utils
are imported solely for types and never used at runtime—declaring them under devDependencies
means downstream consumers won’t get them when installing your package. Also, since importHelpers: true
is set in packages/ai-sdk-provider/tsconfig.base.json
, you need tslib
at runtime.
--- a/packages/ai-sdk-provider/package.json
+++ b/packages/ai-sdk-provider/package.json
@@
"peerDependencies": {
- "ai": "^5",
+ "ai": "^5",
+ "@ai-sdk/provider": "2.0.0",
+ "@ai-sdk/provider-utils": "3.0.7",
"typescript": ">=5.0.4",
"zod": "^3.25.75 || ^4"
},
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
"devDependencies": {
- "@ai-sdk/provider": "2.0.0",
- "@ai-sdk/provider-utils": "3.0.7",
"@biomejs/biome": "2.0.6",
"rimraf": "6.0.1"
},
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
"devDependencies": { | |
"@ai-sdk/provider": "2.0.0", | |
"@ai-sdk/provider-utils": "3.0.7", | |
"@biomejs/biome": "2.0.6", | |
"rimraf": "6.0.1" | |
}, | |
"peerDependencies": { | |
"ai": "^5", | |
"@ai-sdk/provider": "2.0.0", | |
"@ai-sdk/provider-utils": "3.0.7", | |
"typescript": ">=5.0.4", | |
"zod": "^3.25.75 || ^4" | |
}, | |
"dependencies": { | |
"tslib": "^2.6.2" | |
}, | |
"devDependencies": { | |
- "@ai-sdk/provider": "2.0.0", | |
- "@ai-sdk/provider-utils": "3.0.7", | |
"@biomejs/biome": "2.0.6", | |
"rimraf": "6.0.1" | |
}, |
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/package.json around lines 6 to 11, the two type-only
SDK packages are declared under devDependencies and tslib is missing from
runtime deps; move "@ai-sdk/provider" and "@ai-sdk/provider-utils" from
devDependencies into peerDependencies (so consumers receive the types) and add
"tslib" to dependencies (because tsconfig uses importHelpers: true) — update
version pins as appropriate and remove them from devDependencies.
"peerDependencies": { | ||
"ai": "^5", | ||
"typescript": ">=5.0.4", | ||
"zod": "^3.25.75 || ^4" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Validate zod peer range; “^3.25.75” looks unlikely.
- Confirm the latest stable zod v3; consider widening to “^3.23.0 || ^4” if v3.25.x doesn’t exist.
- Alternatively, pin to a known-good minimal v3.
Ask the web for latest:
🌐 Web query:
What is the latest stable version of "zod" on npm?
💡 Result:
The latest stable version on npm is zod 4.1.5 (published Aug 28, 2025). [1][2]
Sources:
[1] npm package page — zod (version 4.1.5).
[2] Release listing showing v4.1.5.
🌐 Web query:
What is the latest v3 version of "zod" on npm?
💡 Result:
The latest v3 release on npm is 3.25.51 (checked Aug 28, 2025). [1]
[1] npm package "zod" — versions list.
Replace invalid zod peer range
v3.25.75 does not exist (latest v3 is 3.25.51)
- "zod": "^3.25.75 || ^4"
+ "zod": "^3.25.0 || ^4"
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
"peerDependencies": { | |
"ai": "^5", | |
"typescript": ">=5.0.4", | |
"zod": "^3.25.75 || ^4" | |
}, | |
"peerDependencies": { | |
"ai": "^5", | |
"typescript": ">=5.0.4", | |
"zod": "^3.25.0 || ^4" | |
}, |
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/package.json around lines 31 to 35, the zod
peerDependency range references a non-existent v3.25.75; update the range to a
valid published v3 version (for example replace "^3.25.75 || ^4" with "^3.25.51
|| ^4") so the peerDependencies field only references real zod versions.
PR-Codex overview
This PR introduces the
@thirdweb-dev/ai-sdk-provider
, enhancing the integration of thirdweb's blockchain capabilities with the Vercel AI SDK. It enables AI agents to interact with smart contracts, execute transactions, and perform token swaps.Detailed summary
@thirdweb-dev/ai-sdk-provider
for blockchain AI integration.package.json
files across multiple apps to include the new provider.thirdweb
provider.ChatContainer
,Message
, andReasoning
.Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores