Skip to content

Conversation

joaquim-verges
Copy link
Member

@joaquim-verges joaquim-verges commented Aug 27, 2025


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

  • Added @thirdweb-dev/ai-sdk-provider for blockchain AI integration.
  • Updated package.json files across multiple apps to include the new provider.
  • Implemented new functionality for handling connected wallets in the thirdweb provider.
  • Created API routes for AI chat requests in Next.js.
  • Enhanced components for chat UI, including ChatContainer, Message, and Reasoning.
  • Introduced tools for signing transactions and swaps.
  • Updated TypeScript configurations for the new provider.
  • Improved error handling in wallet connection logic.
  • Added README documentation for the new SDK provider.

The following files were skipped due to too many changes: packages/ai-sdk-provider/src/provider.ts, pnpm-lock.yaml

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • AI SDK demo page with streaming chat, reasoning UI, session continuity, tool-driven on-chain signing/swap flows, reusable chat/message/prompt UI, and a new AI SDK provider package.
  • Bug Fixes

    • Improved wallet connect behavior when already connected, safer connect error handling, and expanded signing method support.
  • Documentation

    • New package README and in-app docs showcasing server/client examples and integration guidance.
  • Chores

    • Dev script and build config updates, dependency upgrades, changesets added, and tooling config adjustments.

Copy link

changeset-bot bot commented Aug 27, 2025

🦋 Changeset detected

Latest commit: 931a85e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
thirdweb Patch
@thirdweb-dev/ai-sdk-provider Minor
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch

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

Copy link

vercel bot commented Aug 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs-v2 Ready Ready Preview Comment Aug 28, 2025 10:12pm
nebula Ready Ready Preview Comment Aug 28, 2025 10:12pm
thirdweb_playground Ready Ready Preview Comment Aug 28, 2025 10:12pm
thirdweb-www Ready Ready Preview Comment Aug 28, 2025 10:12pm
wallet-ui Ready Ready Preview Comment Aug 28, 2025 10:12pm

Copy link
Contributor

coderabbitai bot commented Aug 27, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Changesets
.changeset/many-pants-tease.md, .changeset/tricky-onions-jam.md
Adds patch and minor changeset files; one notes "Handle already connected wallets in 1193 provider", the other marks initial release for @thirdweb-dev/ai-sdk-provider.
Playground package & script
apps/playground-web/package.json, package.json
Adds AI-related dependencies (@ai-sdk/react, ai, streamdown, use-stick-to-bottom, @radix-ui/*) and workspace @thirdweb-dev/ai-sdk-provider; bumps shiki; updates playground dev script to include --filter=./packages/ai-sdk-provider.
Playground AI page & chat UI
apps/playground-web/src/app/ai/ai-sdk/page.tsx, apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
Adds AI SDK Integration Next.js page and ChatContainer component wired to useChat<ThirdwebAiMessage>/DefaultChatTransport, manages session_id, renders message parts (text, reasoning, tool calls) and handles tools (sign_transaction, sign_swap) with on-chain signing flows and addToolResult callbacks.
Playground API route (streaming)
apps/playground-web/src/app/api/chat/route.ts
Adds POST handler that converts UI messages to model messages, invokes provider chat model with tools, streams via streamText, enforces maxDuration = 300, and attaches session_id metadata on finish.
Playground nav & tooling
apps/playground-web/src/app/navLinks.ts, apps/playground-web/knip.json
Inserts "AI SDK" nav link; broadens knip ignore to src/components/** and adds @thirdweb-dev/ai-sdk-provider to ignored dependencies.
Playground UI primitives
apps/playground-web/src/components/*
Adds multiple UI components: Conversation (+ ScrollButton), Loader, Message (Content/Avatar), PromptInput suite (textarea, toolbar, tools, model select, submit), Reasoning (Trigger/Content), Response wrapper, Radix Avatar wrappers; also exports TabName from code-example.
Playground tests / preview
apps/playground-web/...
Adds client/server code examples and server code preview components used on the AI SDK page.
ai-sdk-provider package files
packages/ai-sdk-provider/{package.json,README.md,biome.json,tsconfig.base.json,tsconfig.build.json,tsconfig.json}
Adds new package manifest, README, Biome config, and TypeScript configs for @thirdweb-dev/ai-sdk-provider.
ai-sdk-provider public exports
packages/ai-sdk-provider/src/exports/thirdweb.ts
Re-exports createThirdwebAI, ThirdwebProvider, and related types for the package public API.
ai-sdk-provider implementation
packages/ai-sdk-provider/src/{provider.ts,tools.ts,types.ts}
Implements ThirdwebProvider and createThirdwebAI with non-stream and SSE streaming flows mapping events to language-model stream parts; defines zod tool schemas and createTools for sign_transaction, sign_swap, monitor_transaction; introduces DEFAULT_BASE_URL, ThirdwebConfig, ThirdwebSettings, and ThirdwebAiMessage.
EIP-1193 adapter & tests
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts, packages/thirdweb/src/adapters/eip1193/to-eip1193.test.ts
eth_accounts returns [] when no account; eth_requestAccounts short-circuits when already connected, uses connectOverride or wallet.connect({ client }) with safer error handling; tests updated to reflect wallet-without-account scenarios.
WalletConnect controller
packages/thirdweb/src/wallets/wallet-connect/controller.ts
Adds eth_signTypedData_v4 to allowed methods and adjusts display_uri listener removal to use local handler reference.
Shiki bumps
apps/{dashboard,nebula,portal}/package.json, packages/ui/package.json
Bumps shiki from 1.27.0 to 3.12.0 across several apps/packages.
Portal docs & sidebar
apps/portal/src/app/ai/chat/ai-sdk/page.mdx, apps/portal/src/app/ai/sidebar.tsx
Adds MDX documentation demonstrating server/client AI SDK usage and inserts a "Vercel AI SDK" sidebar link under Blockchain LLM.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Handle_already_connected_wallets_in_1193_provider

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added Playground Changes involving the Playground codebase. packages SDK Involves changes to the thirdweb SDK labels Aug 27, 2025
Copy link
Member Author


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

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.

@joaquim-verges joaquim-verges changed the title Handle already connected wallets in 1193 provider [Package] @thirdweb-dev/ai-sdk-provider Aug 27, 2025
@joaquim-verges joaquim-verges marked this pull request as ready for review August 27, 2025 11:06
@joaquim-verges joaquim-verges requested review from a team as code owners August 27, 2025 11:06
@joaquim-verges joaquim-verges force-pushed the Handle_already_connected_wallets_in_1193_provider branch from c237ff2 to 5ac1076 Compare August 27, 2025 11:09
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 a dev script (tsc … --watch), so your turbo run dev --filter=…/packages/ai-sdk-provider filter is effective.
  • Currently, the root playground:build script only runs
    turbo run build --filter=./apps/playground-web
    If you’d like to build the AI SDK provider alongside the app, consider updating it to:
    "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 renders undefined.

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 for from.

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; use useRef 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: Controlled duration 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 guard setDuration 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 bundle

We 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 unused minHeight/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.

📥 Commits

Reviewing files that changed from the base of the PR and between e651d0a and 5ac1076.

⛔ 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 local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown 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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() 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 with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-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 present

The 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 that latest is at 4.1.3 while next 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 for role: "tool"

I’m not certain whether Thirdweb’s /ai/chat endpoint accepts messages with role: "tool". Please check the official Thirdweb AI Chat API documentation. If role: "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 use v for text chunks
According to the official thirdweb streaming API docs, each delta event’s data payload is a JSON object containing a single v property with the next piece of text. No alternate field names (e.g. delta or data) 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 for LanguageModelV2CallOptions or occurrences of modelId 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 a modelId (or model_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: Verify ChatStatus Values and Align Status Checks

Please confirm that the string literals used in the if/else chain match all possible ChatStatus values exported by the ai package. Any mismatch could lead to unreachable branches or missing UI states.

• File: apps/playground-web/src/components/prompt-input.tsx
• Lines: 156–163

Consider using a switch (status) with a default branch (or exhaustiveness check) to ensure you cover every ChatStatus.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 runtime

With 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_id

Matches 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 path

Import from "zod", not "zod/v4".

-import { z } from "zod/v4";
+import { z } from "zod";

13-22: Swap intent fields should be camelCase

Align 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 request

Add Accept: text/event-stream for doStream.

-        headers: this.getHeaders(),
+        headers: { ...this.getHeaders(), Accept: "text/event-stream" },

428-435: Don’t stringify tool-call input

Emit the object so the AI SDK can validate against Tool.parameters.

-                            input: JSON.stringify(input),
+                            input,

62-67: Flatten system message content to string

Thirdweb 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 guidelines

Use 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.log

Leftover debug log in the render path.

-                        console.log("---sign_swap", part);

52-55: sessionId vs session_id naming drift

You 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 transaction

Disable 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 boundary

Since 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 adding Accept: application/json to non-streaming and JSON parse errors

Minor 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 with cn().

          <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 replace components/remarkPlugins. If the intent is “defaults + user additions,” destructure components/remarkPlugins from props and merge, e.g., components={{ ...defaults, ...(userComponents ?? {}) }} and remarkPlugins={[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.

📥 Commits

Reviewing files that changed from the base of the PR and between 5ac1076 and 1c89599.

⛔ 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 local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown 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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() 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 with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-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 build

Double-check that the codeToHtml import in getCodeHtml.tsx and the BundledLanguage type in code.client.tsx still resolve under Shiki 3’s ESM-only bundles (entry points, wasm assets), then run pnpm 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 intentional

Including "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 ESM

With "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 good

DEFAULT_BASE_URL and ThirdwebConfig fields are clear; no issues found.


52-71: ThirdwebAiMessage tool payload types align with the UI

Types 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 schemas

After 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), add rel="noopener noreferrer" for target-blank links, and merge props.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.

@joaquim-verges joaquim-verges force-pushed the Handle_already_connected_wallets_in_1193_provider branch from 1c89599 to 63d12c9 Compare August 28, 2025 03:02
@joaquim-verges joaquim-verges force-pushed the Handle_already_connected_wallets_in_1193_provider branch from 63d12c9 to 3a95998 Compare August 28, 2025 03:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 explicit size 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 advertise Accept: 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 under metadata, not id, in response-metadata.

The README’s onFinish expects message.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; use parameters (drop outputSchema).

Tool expects a parameters Zod schema. Returning objects with inputSchema/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, require aria-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 and defineChain (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.

📥 Commits

Reviewing files that changed from the base of the PR and between 078e60f and b2d2227.

⛔ 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 local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown 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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() 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 with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-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> in chat-container.tsx, which renders StickToBottom at its root—no further action needed.

packages/ai-sdk-provider/README.md (3)

92-101: Transport payload key is correct here (use body).

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 reads message.metadata?.session_id, but the provider currently enqueues it as id instead of metadata. 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-coded https://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.

Comment on lines +3 to +9
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";

Copy link
Contributor

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.

Comment on lines +1 to +3
import type { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";

Copy link
Contributor

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.

Comment on lines +8 to +9
const LoaderIcon = ({ size = 16 }: LoaderIconProps) => (
<svg
Copy link
Contributor

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.

Suggested change
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) => (
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +34 to +37
export type PromptInputTextareaProps = ComponentProps<typeof Textarea> & {
minHeight?: number;
maxHeight?: number;
};
Copy link
Contributor

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

Comment on lines +154 to +174
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>
);
Copy link
Contributor

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.

Suggested change
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).

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 objects

                           controller.enqueue({
                             type: "tool-call",
                             toolCallId,
                             toolName,
-                            input: JSON.stringify(input),
+                            input,
                             providerExecuted: false,
                           });

67-72: Flatten system message content to string

thirdweb 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 DefaultChatTransport

Matches 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 duplicate return 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 checks

Annotate 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 verbose

For 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.

📥 Commits

Reviewing files that changed from the base of the PR and between b2d2227 and cd44cf6.

⛔ 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 local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown 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.tsfoo.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
Use FORKED_ETHEREUM_CHAIN for mainnet interactions and ANVIL_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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() 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 with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-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 connected

The 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’s children prop is defined as string, so keeping children: string in ReasoningContentProps is correct (npmjs.com).

apps/portal/src/app/ai/sidebar.tsx (1)

28-31: Add AI SDK nav entry — LGTM

Accurate label, path, and placement. No issues.

packages/ai-sdk-provider/src/provider.ts (1)

398-408: Confirm key casing matches tool schema

You’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";
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +32 to +38
export type ReasoningProps = ComponentProps<typeof Collapsible> & {
isStreaming?: boolean;
open?: boolean;
defaultOpen?: boolean;
onOpenChange?: (open: boolean) => void;
duration?: number;
};
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +136 to +141
export function RenderMessage(props: {
message: ThirdwebAiMessage;
sessionId: string;
addToolResult: (toolResult: ThirdwebAiMessage) => void;
sendMessage: (message: string) => void;
}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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
}

Comment on lines +166 to +183
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,
},
});
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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}`);

Comment on lines +194 to 206
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,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 on this inside object-literal methods; use closed-over address.
Destructuring signMessage would break this.address. Use the captured address 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: remove this 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 drift

Only 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 token is-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: Constrain from 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 using body, 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.

📥 Commits

Reviewing files that changed from the base of the PR and between cd44cf6 and 931a85e.

⛔ 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 local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown 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/**: Unified Wallet and Account 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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() 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 with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-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 use body 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 in lib/client is instantiated with a client-side clientId (no secretKey is exposed).

No changes needed.

Comment on lines +6 to +11
"devDependencies": {
"@ai-sdk/provider": "2.0.0",
"@ai-sdk/provider-utils": "3.0.7",
"@biomejs/biome": "2.0.6",
"rimraf": "6.0.1"
},
Copy link
Contributor

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.

Suggested change
"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.

Comment on lines +31 to +35
"peerDependencies": {
"ai": "^5",
"typescript": ">=5.0.4",
"zod": "^3.25.75 || ^4"
},
Copy link
Contributor

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.

Suggested change
"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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dashboard Involves changes to the Dashboard. packages Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase. SDK Involves changes to the thirdweb SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant