Skip to content

Conversation

joaquim-verges
Copy link
Member

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


PR-Codex overview

This PR focuses on enhancing the thirdweb integration with the Vercel AI SDK, improving wallet management, and adding new functionalities for AI-driven blockchain interactions.

Detailed summary

  • Added support for handling already connected wallets in the eip1193 provider.
  • Introduced @thirdweb-dev/ai-sdk-provider for seamless integration with the Vercel AI SDK.
  • Enhanced UI components in playground-web for chat and messaging features.
  • Updated shiki version across multiple packages.
  • Added new tools for transaction signing and monitoring in the AI SDK provider.
  • Improved TypeScript configurations and added type definitions for new functionalities.
  • Expanded README documentation for the ai-sdk-provider.

The following files were skipped due to too many changes: 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 persistence, server-backed chat endpoint, and integrated chat container.
    • New AI SDK provider package (public exports, tools, types) with client/server examples.
    • New chat UI components: prompt input, conversation view, scroll-to-bottom, loader, message/avatar, reasoning, response, and code examples.
    • In‑chat on‑chain actions to sign transactions and swaps with result follow-ups.
  • Bug Fixes

    • Wallet connect now short-circuits if already connected and handles connect failures more gracefully.
  • Documentation

    • README for the AI SDK provider added.
  • Chores

    • Navigation links added; dependency upgrades (shiki, AI libs); playground dev script updated; changeset and package configs added.

Copy link

changeset-bot bot commented Aug 27, 2025

🦋 Changeset detected

Latest commit: 06efc26

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

This PR includes changesets to release 3 packages
Name Type
thirdweb Patch
@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 7:15pm
nebula Ready Ready Preview Comment Aug 28, 2025 7:15pm
thirdweb_playground Error Error Aug 28, 2025 7:15pm
thirdweb-www Ready Ready Preview Comment Aug 28, 2025 7:15pm
wallet-ui Ready Ready Preview Comment Aug 28, 2025 7:15pm

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 an AI chat UI and streaming API into the playground, adds multiple UI components, updates EIP-1193 connect behavior to short-circuit when already connected, bumps shiki, and adds a changeset.

Changes

Cohort / File(s) Summary
Changeset
./.changeset/many-pants-tease.md
Adds a patch changeset noting "Handle already connected wallets in 1193 provider".
Playground deps & script
apps/playground-web/package.json, package.json
Adds AI-related deps (@ai-sdk/react, ai, streamdown, use-stick-to-bottom, Radix avatar, workspace @thirdweb-dev/ai-sdk-provider) and bumps shiki to 3.12.0; 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 page and ChatContainer component wiring useChat<ThirdwebAiMessage> to /api/chat, handling session_id, rendering parts (text, reasoning, tool calls) and reporting tool results (sign_transaction, sign_swap).
Playground API route (streaming)
apps/playground-web/src/app/api/chat/route.ts
Adds POST handler that converts UI messages, calls provider chat/tools, streams model output via streamText, enforces maxDuration = 300, and attaches session_id in finish metadata.
Playground nav
apps/playground-web/src/app/navLinks.ts
Inserts "AI SDK" link (/ai/ai-sdk) into the AI submenu.
Playground UI primitives
apps/playground-web/src/components/*
Adds multiple UI components: Conversation (with ScrollButton), Loader, Message (Content/Avatar), PromptInput suite (textarea, toolbar, tools, model select, submit), Reasoning (Trigger/Content), Response wrapper, Radix Avatar wrappers, and exports TabName.
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 createThirdweb, ThirdwebProvider, and related types (transaction/monitor/sign types, ThirdwebAiMessage, config/settings).
ai-sdk-provider implementation
packages/ai-sdk-provider/src/{provider.ts,tools.ts,types.ts}
Implements ThirdwebProvider and createThirdweb, message translation, non-streaming and SSE streaming flows (mapping SSE to structured events), headers/config handling, zod-defined tool schemas and createTools factory (sign_transaction, sign_swap, monitor_transaction), DEFAULT_BASE_URL, and exported config/settings/message types.
EIP-1193 wallet connect handling
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
eth_requestAccounts now short-circuits when already connected; uses connectOverride or wallet.connect({ client }) with a catch that logs and returns null; updates error message when connect fails.
Shiki version bumps
apps/{dashboard,nebula,portal}/package.json, packages/ui/package.json
Updates shiki from 1.27.0 to 3.12.0 across multiple packages.
Portal docs & sidebar
apps/portal/src/app/ai/chat/ai-sdk/page.mdx, apps/portal/src/app/ai/sidebar.tsx
Adds MDX docs showing server/client AI SDK examples 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 /api/chat
  participant Provider as ThirdwebProvider
  participant TWAPI as Thirdweb API

  User->>UI: Submit prompt (messages + sessionId)
  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, response-metadata)
  API-->>UI: Stream UI-formatted parts (includes 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 (sessionId)
  API-->>UI: Stream continued response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

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.

Comment on lines +351 to +363
if (currentEvent === "init") {
const parsed = JSON.parse(data);
if (parsed.session_id) {
controller.enqueue({
type: "response-metadata",
id: parsed.session_id || "",
});
}
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

Send response metadata as metadata, not id

Expose session_id via response-metadata.metadata so clients can consume it.

-                        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 },
+                          });
+                        }
📝 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
if (currentEvent === "init") {
const parsed = JSON.parse(data);
if (parsed.session_id) {
controller.enqueue({
type: "response-metadata",
id: parsed.session_id || "",
});
}
if (currentEvent === "init") {
const parsed = JSON.parse(data);
if (parsed.session_id) {
controller.enqueue({
type: "response-metadata",
metadata: { session_id: parsed.session_id },
});
}
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/src/provider.ts around lines 351 to 358, the code
currently enqueues response-metadata with session_id set as id; change the shape
so session_id is exposed under a metadata property instead. Specifically, when
parsed.session_id exists, enqueue an object like { type: "response-metadata",
metadata: { session_id: parsed.session_id } } (remove or leave id empty as
appropriate for downstream consumers) so clients can read session_id from
response-metadata.metadata.

Comment on lines 391 to 424
switch (parsed.type) {
case "sign_transaction":
toolName = "sign_transaction";
input = {
chain_id: parsed.data.chain_id,
to: parsed.data.to,
value: parsed.data.value,
data: parsed.data.data,
function: parsed.data.function,
};
break;

case "sign_swap":
toolName = "sign_swap";
input = {
action: parsed.data.action,
from: parsed.data.from,
to: parsed.data.to,
intent: parsed.data.intent,
transaction: parsed.data.transaction,
};
break;

case "monitor_transaction":
toolName = "monitor_transaction";
input = {
transactionId: parsed.data.transaction_id,
};
break;
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

Align tool input shapes and keys (camelCase + monitor_transaction)

  • sign_transaction: use chainId (camelCase) to match tools schema.
  • monitor_transaction: key is transaction_id per schema.
-                            case "sign_transaction":
+                            case "sign_transaction":
                               toolName = "sign_transaction";
                               input = {
-                                chain_id: parsed.data.chain_id,
+                                chainId: parsed.data.chainId ?? parsed.data.chain_id,
                                 to: parsed.data.to,
                                 value: parsed.data.value,
                                 data: parsed.data.data,
                                 function: parsed.data.function,
                               };
                               break;
...
-                            case "monitor_transaction":
+                            case "monitor_transaction":
                               toolName = "monitor_transaction";
                               input = {
-                                transactionId: parsed.data.transaction_id,
+                                transaction_id: parsed.data.transaction_id,
                               };
                               break;
📝 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
switch (parsed.type) {
case "sign_transaction":
toolName = "sign_transaction";
input = {
chain_id: parsed.data.chain_id,
to: parsed.data.to,
value: parsed.data.value,
data: parsed.data.data,
function: parsed.data.function,
};
break;
case "sign_swap":
toolName = "sign_swap";
input = {
action: parsed.data.action,
from: parsed.data.from,
to: parsed.data.to,
intent: parsed.data.intent,
transaction: parsed.data.transaction,
};
break;
case "monitor_transaction":
toolName = "monitor_transaction";
input = {
transactionId: parsed.data.transaction_id,
};
break;
switch (parsed.type) {
case "sign_transaction":
toolName = "sign_transaction";
input = {
chainId: parsed.data.chainId ?? parsed.data.chain_id,
to: parsed.data.to,
value: parsed.data.value,
data: parsed.data.data,
function: parsed.data.function,
};
break;
case "sign_swap":
toolName = "sign_swap";
input = {
action: parsed.data.action,
from: parsed.data.from,
to: parsed.data.to,
intent: parsed.data.intent,
transaction: parsed.data.transaction,
};
break;
case "monitor_transaction":
toolName = "monitor_transaction";
input = {
transaction_id: parsed.data.transaction_id,
};
break;
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/src/provider.ts around lines 391 to 419, the tool
input shapes don't match the expected schema: for the "sign_transaction" case
change chain_id -> chainId (use camelCase) and ensure the other keys follow the
tool schema naming, and for the "monitor_transaction" case change transactionId
-> transaction_id (use the schema's snake_case key); update the input object
keys accordingly so they exactly match the tools' expected field names.

@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: 1

♻️ Duplicate comments (14)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)

184-186: Guard against invalid tx payload to avoid runtime errors.

Don’t call prepareTransaction with empty to/data/value.

   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>
+    );
+  }
+
   return (
     <div className="py-4">
       <TransactionButton
-        style={{
-          width: "100%",
-        }}
+        className="w-full"
         transaction={() =>
           prepareTransaction({
             client: THIRDWEB_CLIENT,
             chain: defineChain(transactionData.chain_id),
             to: transactionData.to,
             data: transactionData.data,
             value: transactionData.value,
           })
         }
         onTransactionSent={(transaction) => {
           addToolResult({
             tool: "sign_transaction",
             toolCallId,
             output: {
               transaction_hash: transaction.transactionHash,
               chain_id: transaction.chain.id,
             },
           });
           sendMessage(undefined, {
             body: {
               sessionId,
             },
           });
         }}
+        onError={(err) => {
+          console.error("Transaction failed:", err);
+          sendMessage({ text: "Transaction signing failed." }, { body: { sessionId } });
+        }}
       >
         Sign Transaction
       </TransactionButton>
     </div>
   );

Also applies to: 188-223


258-260: Mirror tx validation and add error handling for swap-signing path.

Keep both signing paths consistent and resilient.

   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 swap transaction details…
+      </div>
+    );
+  }
+
   return (
     <div className="py-4">
-      <TransactionButton
-        style={{
-          width: "100%",
-        }}
+      <TransactionButton
+        className="w-full"
         transaction={() =>
           prepareTransaction({
             client: THIRDWEB_CLIENT,
             chain: defineChain(transactionData.chain_id),
             to: transactionData.to,
             data: transactionData.data,
             value: transactionData.value,
           })
         }
         onTransactionSent={(transaction) => {
           addToolResult({
             tool: "sign_swap",
             toolCallId,
             output: {
               transaction_hash: transaction.transactionHash,
               chain_id: transaction.chain.id,
             },
           });
           sendMessage(undefined, {
             body: {
               sessionId,
             },
           });
         }}
+        onError={(err) => {
+          console.error("Swap transaction failed:", err);
+          sendMessage({ text: "Swap signing failed." }, { body: { sessionId } });
+        }}
       >
         Sign swap
       </TransactionButton>
     </div>
   );

Also applies to: 262-295


31-32: Potential secret leakage: don’t import a client that may embed secretKey in the browser.

Move secrets to server-only modules; use a client-safe public client on the web.

-import { THIRDWEB_CLIENT } from "../../../../lib/client";
+// Use a client-safe client that never embeds secretKey
+import { THIRDWEB_PUBLIC_CLIENT as THIRDWEB_CLIENT } from "@/lib/public-client";

Create 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,
});
packages/ai-sdk-provider/src/provider.ts (6)

193-201: Advertise SSE support via Accept: text/event-stream.

Some backends rely on this header for streaming.

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

62-67: Flatten system message content to a string.

thirdweb API likely expects a string, not parts.

-        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 };
+        }

115-121: Guard lastUserMessage to avoid [undefined].

Only prefer the last user message when it exists.

-    const messages = this.settings.context?.session_id
-      ? [lastUserMessage]
-      : allMessages;
+    const messages =
+      this.settings.context?.session_id && lastUserMessage
+        ? [lastUserMessage]
+        : allMessages;

179-185: Apply the same lastUserMessage guard in streaming path.

Keep behavior consistent across non-stream and stream.

-    const messages = this.settings.context?.session_id
-      ? [lastUserMessage]
-      : allMessages;
+    const messages =
+      this.settings.context?.session_id && lastUserMessage
+        ? [lastUserMessage]
+        : allMessages;

351-358: Send response metadata in the metadata field, not as id.

This matches the AI SDK stream contract and lets clients read session_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 },
+                          });
+                        }

391-419: Tool-call mapping: fix input shapes and avoid stringifying input.

  • Use camelCase chainId (fallback to snake case if present).
  • Use transaction_id for monitor.
  • Pass input as an object (not JSON string).
                           switch (parsed.type) {
                             case "sign_transaction":
                               toolName = "sign_transaction";
                               input = {
-                                chain_id: parsed.data.chain_id,
+                                chainId: parsed.data.chainId ?? parsed.data.chain_id,
                                 to: parsed.data.to,
                                 value: parsed.data.value,
                                 data: parsed.data.data,
                                 function: parsed.data.function,
                               };
                               break;
 
                             case "sign_swap":
                               toolName = "sign_swap";
                               input = {
                                 action: parsed.data.action,
                                 from: parsed.data.from,
                                 to: parsed.data.to,
                                 intent: parsed.data.intent,
                                 transaction: parsed.data.transaction,
                               };
                               break;
 
                             case "monitor_transaction":
                               toolName = "monitor_transaction";
                               input = {
-                                transactionId: parsed.data.transaction_id,
+                                transaction_id: parsed.data.transaction_id,
                               };
                               break;
 ...
                           // Emit as tool call
                           controller.enqueue({
                             type: "tool-call",
                             toolCallId,
                             toolName,
-                            input: JSON.stringify(input),
+                            input,
                             providerExecuted: false,
                           });

Also applies to: 428-435

packages/ai-sdk-provider/src/tools.ts (5)

2-2: Fix zod import path.

zod/v4 will fail; import from zod.

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

5-11: Align tx schema with generated types: use chainId (camelCase).

Matches packages/nebula/*/types.gen.ts.

 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(),
 });

13-22: Swap intent fields should be camelCase; max_stepsmaxSteps.

Mirror Nebula types.

 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(),
 });

24-28: Include token shapes and allow nullable action in swap schema.

Nebula includes from/to token objects; action can be null.

+const AgentActionSignSwapDataToken = z.object({
+  address: z.string(),
+  chain_id: z.number(), // token keeps snake_case per generated types
+  amount: z.string(),
+  symbol: z.string(),
+  decimals: z.number(),
+  price: z.number().nullable(),
+});
+
 const AgentActionSignSwapData = z.object({
-  action: z.string(),
+  action: z.string().nullable(),
   intent: AgentActionSignSwapDataIntent,
+  from: AgentActionSignSwapDataToken,
+  to: AgentActionSignSwapDataToken,
   transaction: AgentActionSignTransactionData,
 });

51-75: Use AI SDK v4 Tool shape: parameters (drop outputSchema).

This will type-check and validate inputs at runtime.

-export function createTools(_config: ThirdwebConfig): Record<string, Tool> {
-  return {
-    sign_transaction: {
+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,
-    sign_swap: {
+      parameters: AgentActionSignTransactionData,
+    },
+    sign_swap: {
       id: "thirdweb.sign_swap" as const,
       name: "sign_swap",
       description: "Sign a swap transaction",
-      inputSchema: AgentActionSignSwapData,
-      outputSchema: AgentActionTransactionOutputData,
-    } as const,
-    monitor_transaction: {
+      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 (5)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (2)

5-6: Prefer path aliases over deep relatives for consistency.

Use @/ paths to match repo conventions.

-import { PageLayout } from "../../../components/blocks/APIHeader";
-import { createMetadata } from "../../../lib/metadata";
+import { PageLayout } from "@/components/blocks/APIHeader";
+import { createMetadata } from "@/lib/metadata";

25-40: Add explicit return types for components.

Keeps with TS style guide and improves DX.

-export default function Page() {
+export default function Page(): React.ReactElement {
   return (
     ...
   );
 }

Apply similarly to ServerCodeExample and ChatExample.

-function ServerCodeExample() {
+function ServerCodeExample(): React.ReactElement {
-function ChatExample() {
+function ChatExample(): React.ReactElement {
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)

30-30: Use path alias for local imports.

Minor consistency fix.

-import { Loader } from "../../../../components/loader";
+import { Loader } from "@/components/loader";

107-111: Remove leftover debug log.

Avoid shipping console noise.

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

191-201: Use Tailwind instead of inline styles.

Project guideline: Tailwind only.

-      <TransactionButton
-        style={{
-          width: "100%",
-        }}
+      <TransactionButton
+        className="w-full"
📜 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 3a95998 and bd4643d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (29)
  • .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/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 (22)
  • apps/nebula/package.json
  • apps/playground-web/src/components/conversation.tsx
  • apps/playground-web/src/components/ui/avatar.tsx
  • packages/ai-sdk-provider/README.md
  • packages/ai-sdk-provider/src/types.ts
  • apps/playground-web/src/app/navLinks.ts
  • packages/ai-sdk-provider/tsconfig.build.json
  • apps/playground-web/src/components/loader.tsx
  • packages/ai-sdk-provider/tsconfig.json
  • packages/ai-sdk-provider/tsconfig.base.json
  • apps/playground-web/src/app/api/chat/route.ts
  • apps/playground-web/src/components/response.tsx
  • apps/playground-web/package.json
  • packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
  • apps/dashboard/package.json
  • apps/playground-web/src/components/reasoning.tsx
  • apps/playground-web/src/components/code/code-example.tsx
  • packages/ai-sdk-provider/package.json
  • apps/playground-web/src/components/message.tsx
  • packages/ai-sdk-provider/biome.json
  • package.json
  • apps/playground-web/src/components/prompt-input.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:

  • packages/ai-sdk-provider/src/exports/thirdweb.ts
  • apps/playground-web/src/app/ai/ai-sdk/page.tsx
  • packages/ai-sdk-provider/src/provider.ts
  • apps/playground-web/src/app/ai/ai-sdk/components/chat-container.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:

  • packages/ai-sdk-provider/src/exports/thirdweb.ts
  • apps/playground-web/src/app/ai/ai-sdk/page.tsx
  • packages/ai-sdk-provider/src/provider.ts
  • apps/playground-web/src/app/ai/ai-sdk/components/chat-container.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/app/ai/ai-sdk/page.tsx
  • apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
🧠 Learnings (5)
📚 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
📚 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/**/*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
🧬 Code graph analysis (4)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (4)
packages/ai-sdk-provider/src/provider.ts (1)
  • ThirdwebProvider (494-522)
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 (33-150)
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)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (9)
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/response.tsx (1)
  • Response (9-20)
apps/playground-web/src/components/reasoning.tsx (3)
  • Reasoning (43-111)
  • ReasoningTrigger (115-146)
  • ReasoningContent (154-167)
apps/playground-web/src/components/loader.tsx (1)
  • Loader (86-96)
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)
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 (5)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (1)

25-39: Solid page structure and demos.

Layout, metadata, and live preview wiring look good and align with Playground patterns.

Also applies to: 42-107, 109-159

apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)

33-60: Nice chat wiring and session propagation.

The useChat setup, session propagation via onFinish, and form handling look clean.

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

494-527: Provider surface looks good.

ThirdwebProvider, languageModel, and factory createThirdweb are clean and align with ProviderV2.

packages/ai-sdk-provider/src/tools.ts (2)

30-49: Output types and enums look fine.

Transaction/monitor outputs align with expected shapes.


77-83: Confirm downstream usage after schema renames.

If you adopt camelCase (chainId, intent fields), update any consumers (e.g., ChatContainer) to read input.chainId and input.transaction.chainId.

Would you like me to sweep the repo and raise a follow-up PR to align these call sites?

Copy link

socket-security bot commented Aug 28, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​ai-sdk/​provider@​2.0.01001007194100
Updatedshiki@​1.27.0 ⏵ 3.12.0100 +110075 +199 +2100
Added@​ai-sdk/​provider-utils@​3.0.71001007697100
Addedai@​5.0.25100100100100100

View full report

Copy link
Contributor

github-actions bot commented Aug 28, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 64.06 KB (0%) 1.3 s (0%) 232 ms (+180.5% 🔺) 1.6 s
thirdweb (cjs) 357.05 KB (0%) 7.2 s (0%) 702 ms (+16.04% 🔺) 7.9 s
thirdweb (minimal + tree-shaking) 5.73 KB (0%) 115 ms (0%) 90 ms (+1188.72% 🔺) 204 ms
thirdweb/chains (tree-shaking) 526 B (0%) 11 ms (0%) 41 ms (+2001.39% 🔺) 51 ms
thirdweb/react (minimal + tree-shaking) 19.15 KB (0%) 383 ms (0%) 59 ms (+337.38% 🔺) 442 ms

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: 3

♻️ Duplicate comments (9)
packages/ai-sdk-provider/tsconfig.build.json (1)

3-6: Align moduleResolution with NodeNext to avoid ESM/CJS resolution issues.

Base config uses NodeNext; overriding to "node" can break .js extension and package.json#exports resolution.

   "compilerOptions": {
-    "moduleResolution": "node",
+    "moduleResolution": "NodeNext",
     "rootDir": "./src",
     "sourceMap": true
   },
packages/ai-sdk-provider/src/tools.ts (5)

2-2: Fix zod import path

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

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

24-28: Add token shapes and make action nullable for Swap schema

Include from/to token data and make action nullable. Keep token.chain_id as snake_case per generated types.

+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(),
+  action: z.string().nullable(),
   intent: AgentActionSignSwapDataIntent,
+  from: AgentActionSignSwapDataToken,
+  to: AgentActionSignSwapDataToken,
   transaction: AgentActionSignTransactionData,
 });

51-75: Use AI SDK v4 Tool shape (parameters), drop outputSchema, and tighten typing

Replace inputSchema with parameters; remove outputSchema. Return an object that satisfies Record<string, Tool>.

-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>;
 }

5-11: Align SignTransaction schema with generated types (chainId)

Use camelCase to match Nebula client types.

 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(),
 });

13-22: Align Swap Intent schema with generated types (camelCase + maxSteps)

Rename fields and maxSteps to camelCase.

 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(),
 });
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)

30-31: Avoid exposing secretKey in client bundle

Import a client-safe instance (clientId only). Move secretKey usage to a server-only module.

-import { Loader } from "../../../../components/loader";
-import { THIRDWEB_CLIENT } from "../../../../lib/client";
+import { Loader } from "@/components/loader";
+// Client-safe thirdweb client (no secretKey)
+import { THIRDWEB_PUBLIC_CLIENT as THIRDWEB_CLIENT } from "@/lib/public-client";

Create 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 against invalid tx payload before rendering TransactionButton (sign_transaction)

Avoid calling prepareTransaction with empty to/data/value.

   const account = useActiveAccount();

   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>
+    );
+  }

268-286: Mirror tx validation for swap path

Same guard for sign_swap.

   const account = useActiveAccount();

   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 swap transaction details…
+      </div>
+    );
+  }
🧹 Nitpick comments (9)
packages/ai-sdk-provider/tsconfig.build.json (2)

4-5: Ensure declaration outputs for consumers.

If not already set in tsconfig.base.json, emit types in builds.

   "compilerOptions": {
     "rootDir": "./src",
-    "sourceMap": true
+    "sourceMap": true,
+    "declaration": true,
+    "declarationMap": true
   },

7-13: Broaden test excludes to cover common patterns.

Consider excluding .spec.* alongside .test.* to match both conventions.

   "exclude": [
     "src/**/*.test.ts",
     "src/**/*.test.tsx",
+    "src/**/*.spec.ts",
+    "src/**/*.spec.tsx",
     "src/**/*.test-d.ts",
     "src/**/*.bench.ts",
     "src/**/*.macro.ts"
   ],
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)

191-193: Use Tailwind instead of inline styles

Replace style prop with className per guidelines.

-      <TransactionButton
-        style={{
-          width: "100%",
-        }}
+      <TransactionButton
+        className="w-full"

Also applies to: 275-277


108-108: Remove debug log

Drop console.log in production UI.

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

33-35: Add explicit return types for exported components

Align with TS guidelines for explicit return types.

-export function ChatContainer() {
+export function ChatContainer(): JSX.Element {
@@
-const SignTransactionButton = (props: SignTransactionButtonProps) => {
+const SignTransactionButton = (props: SignTransactionButtonProps): JSX.Element => {
@@
-const SignSwapButton = (props: SignSwapButtonProps) => {
+const SignSwapButton = (props: SignSwapButtonProps): JSX.Element => {

Also applies to: 167-168, 249-250

apps/playground-web/src/components/reasoning.tsx (4)

5-6: Import ReactElement for explicit component return types

Prepare for explicit returns below.

-import type { ComponentProps } from "react";
+import type { ComponentProps, ReactElement } from "react";

43-44: Add explicit return types for exported components

Make return types explicit.

-export const Reasoning = memo(
+export const Reasoning = memo(
   ({
@@
-  }: ReasoningProps) => {
+  }: ReasoningProps): ReactElement => {
@@
-export const ReasoningTrigger = memo(
+export const ReasoningTrigger = memo(
   ({ className, children, ...props }: ReasoningTriggerProps) => {
@@
-export const ReasoningContent = memo(
-  ({ className, children, ...props }: ReasoningContentProps) => (
+export const ReasoningContent = memo(
+  ({ className, children, ...props }: ReasoningContentProps): ReactElement => (

Also applies to: 115-116, 154-155


130-134: Pluralize “second(s)”

Tiny UX polish.

-              <p>Thought for {duration} seconds</p>
+              <p>
+                Thought for {duration} {duration === 1 ? "second" : "seconds"}
+              </p>

64-86: Rename state: not a ref

Variable ends with “Ref” but uses useState. Rename for clarity.

-    const [hasAutoClosedRef, setHasAutoClosedRef] = useState(false);
+    const [hasAutoClosed, setHasAutoClosed] = useState(false);
@@
-      if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) {
+      if (defaultOpen && !isStreaming && isOpen && !hasAutoClosed) {
@@
-          setHasAutoClosedRef(true);
+          setHasAutoClosed(true);
@@
-    }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosedRef]);
+    }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosed]);
📜 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 94b7076 and 37cda56.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (29)
  • .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/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
  • apps/playground-web/src/components/conversation.tsx
🚧 Files skipped from review as they are similar to previous changes (23)
  • apps/dashboard/package.json
  • apps/nebula/package.json
  • packages/ai-sdk-provider/package.json
  • apps/playground-web/src/components/code/code-example.tsx
  • apps/playground-web/src/components/loader.tsx
  • apps/playground-web/src/app/api/chat/route.ts
  • package.json
  • packages/ai-sdk-provider/tsconfig.base.json
  • apps/playground-web/src/components/response.tsx
  • packages/ai-sdk-provider/tsconfig.json
  • packages/ai-sdk-provider/README.md
  • .changeset/many-pants-tease.md
  • packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
  • packages/ai-sdk-provider/src/exports/thirdweb.ts
  • apps/playground-web/src/components/message.tsx
  • packages/ai-sdk-provider/src/types.ts
  • apps/playground-web/src/app/ai/ai-sdk/page.tsx
  • packages/ai-sdk-provider/biome.json
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/package.json
  • packages/ai-sdk-provider/src/provider.ts
  • apps/playground-web/src/components/ui/avatar.tsx
  • apps/playground-web/src/components/prompt-input.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/app/ai/ai-sdk/components/chat-container.tsx
  • apps/playground-web/src/components/reasoning.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/app/ai/ai-sdk/components/chat-container.tsx
  • apps/playground-web/src/components/reasoning.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/app/ai/ai-sdk/components/chat-container.tsx
  • apps/playground-web/src/components/reasoning.tsx
🧠 Learnings (2)
📚 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
🧬 Code graph analysis (3)
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/reasoning.tsx (1)
apps/playground-web/src/components/response.tsx (1)
  • Response (9-20)
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)
⏰ 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). (3)
  • GitHub Check: Size
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/ai-sdk-provider/tsconfig.build.json (1)

14-15: Build scripts correctly target tsconfig.build.json; ensure tsconfig.base.json’s compilerOptions include your intended module, outDir, declaration, and declarationMap settings.

Comment on lines +167 to +181
const SignTransactionButton = (props: SignTransactionButtonProps) => {
const { input, addToolResult, toolCallId, sendMessage, sessionId } = props;
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]);
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

Update to camelCase transaction fields (chainId) for sign_transaction

Reflect schema changes; pass correct chain id into defineChain.

-  const transactionData: {
-    chain_id: number;
+  const transactionData: {
+    chainId: number;
     to: string;
     data: `0x${string}`;
     value: bigint;
   } = useMemo(() => {
     return {
-      chain_id: input?.chain_id || 8453,
+      chainId: input?.chainId || 8453,
       to: input?.to || "",
       data: (input?.data as `0x${string}`) || "0x",
       value: input?.value ? BigInt(input.value) : BigInt(0),
     };
   }, [input]);
...
-            chain: defineChain(transactionData.chain_id),
+            chain: defineChain(transactionData.chainId),

Also applies to: 195-201

🤖 Prompt for AI Agents
In apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx around
lines 167-181 (and also update lines 195-201), the transaction object uses
snake_case fields (chain_id) but the schema and downstream defineChain expect
camelCase (chainId). Change the transaction object keys to camelCase (chainId)
and ensure any consumers/defineChain calls receive transaction.chainId instead
of chain_id; keep the same default value (8453) and preserve types/values (e.g.,
value as BigInt) when renaming the field.

Comment on lines +251 to +265
const transactionData: {
chain_id: number;
to: string;
data: `0x${string}`;
value: bigint;
} = useMemo(() => {
return {
chain_id: input?.transaction?.chain_id || 8453,
to: input?.transaction?.to || "",
data: (input?.transaction?.data as `0x${string}`) || "0x",
value: input?.transaction?.value
? BigInt(input.transaction.value)
: BigInt(0),
};
}, [input]);
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

Update to camelCase transaction fields (chainId) for sign_swap

Reflect schema changes inside swap payload.

-  const transactionData: {
-    chain_id: number;
+  const transactionData: {
+    chainId: number;
     to: string;
     data: `0x${string}`;
     value: bigint;
   } = useMemo(() => {
     return {
-      chain_id: input?.transaction?.chain_id || 8453,
+      chainId: input?.transaction?.chainId || 8453,
       to: input?.transaction?.to || "",
       data: (input?.transaction?.data as `0x${string}`) || "0x",
       value: input?.transaction?.value
         ? BigInt(input.transaction.value)
         : BigInt(0),
     };
   }, [input]);
...
-            chain: defineChain(transactionData.chain_id),
+            chain: defineChain(transactionData.chainId),

Also applies to: 281-285

🤖 Prompt for AI Agents
In apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx around
lines 251 to 265 (and similarly update lines 281 to 285), the transaction object
uses snake_case keys (chain_id) but the schema expects camelCase (chainId);
update the transactionData type and returned object to use camelCase keys
(chainId) and ensure the data and value types remain the same (data as
`0x${string}`, value as bigint), and then update any downstream usages of
transaction.chain_id to transaction.chainId in the nearby code blocks (including
lines 281-285) so the sign_swap payload matches the new schema.

Comment on lines +5 to +28
const AgentActionSignTransactionData = z.object({
chain_id: z.number(),
function: z.string().nullable().optional(),
to: z.string(),
value: z.string(),
data: z.string(),
});

const AgentActionSignSwapDataIntent = z.object({
origin_chain_id: z.number(),
origin_token_address: z.string(),
destination_chain_id: z.number(),
destination_token_address: z.string(),
amount: z.string(),
sender: z.string(),
receiver: z.string(),
max_steps: z.number(),
});

const AgentActionSignSwapData = z.object({
action: z.string(),
intent: AgentActionSignSwapDataIntent,
transaction: AgentActionSignTransactionData,
});
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 downstream usage after snake_case → camelCase migration

Search for remaining chain_id, origin_chain_id, etc., and update call sites (e.g., chat container) to use camelCase.

Also applies to: 51-75


🏁 Script executed:

#!/bin/bash
# Find snake_case usages that must be migrated to camelCase
rg -nP --glob '!**/node_modules/**' --glob '!**/dist/**' \
  '\b(chain_id|origin_chain_id|destination_chain_id|origin_token_address|destination_token_address|max_steps)\b'

echo -e "\nCheck ai-sdk-provider exports used by playground:"
rg -nP --glob '!**/node_modules/**' 'tool-sign_(transaction|swap)|Sign(Transaction|Swap)Input|TransactionOutput|MonitorTransactionInput' apps/playground-web

Length of output: 47868


Migrate snake_case to camelCase in ai-sdk-provider and update downstream call sites

  • In packages/ai-sdk-provider/src/tools.ts, rename:
    • chain_id → chainId
    • origin_chain_id → originChainId
    • origin_token_address → originTokenAddress
    • destination_chain_id → destinationChainId
    • destination_token_address → destinationTokenAddress
    • max_steps → maxSteps
  • In packages/ai-sdk-provider/src/provider.ts (around line 400), replace parsed.data.chain_id with parsed.data.chainId
  • In apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (lines 96, 107, 158, 241), update “tool-sign_transaction”/“tool-sign_swap” handlers and all transactionData.chain_id accesses to chainId
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/src/tools.ts around lines 5 to 28, the Zod schemas
use snake_case keys; rename them to camelCase (chain_id→chainId,
origin_chain_id→originChainId, origin_token_address→originTokenAddress,
destination_chain_id→destinationChainId,
destination_token_address→destinationTokenAddress, max_steps→maxSteps) and
update the AgentActionSignSwapDataIntent and AgentActionSignTransactionData
schema property names accordingly; then update downstream call sites: in
packages/ai-sdk-provider/src/provider.ts (around line 400) replace
parsed.data.chain_id with parsed.data.chainId, and in
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (lines ~96,
107, 158, 241) update the “tool-sign_transaction”/“tool-sign_swap” handlers and
all transactionData.chain_id accesses to use chainId and similarly rename swap
intent properties to camelCase; ensure any type imports, destructuring, and
tests are updated to match the new property names and run type checks.

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

♻️ Duplicate comments (1)
packages/ai-sdk-provider/biome.json (1)

3-3: Fix invalid Biome "extends" path

Biome can’t resolve "//". Point to the repo root config.

-  "extends": "//",
+  "extends": ["../../biome.json"],
🧹 Nitpick comments (3)
packages/ai-sdk-provider/biome.json (1)

6-12: Double-check Assist action path and intent

If the goal is to keep package.json keys unsorted for this package, the setting looks right, but validate the path assist.actions.source.useSortedKeys against your Biome version. Also consider whether this should apply to all package.json files in the monorepo vs only this one.

Optional broadened scope:

-      "include": ["package.json"]
+      "include": ["**/package.json"]
apps/portal/src/app/ai/sidebar.tsx (1)

28-31: Unify naming with Playground nav

Playground uses “AI SDK”; consider aligning this label to avoid cross-app inconsistency.

-          name: "Vercel AI SDK",
+          name: "AI SDK",
apps/portal/src/app/ai/chat/ai-sdk/page.mdx (1)

48-49: Minor wording fix in comment

[Suggest] “to handle” reads better than “to use handle.”

-    tools: thirdweb.tools(), // optional, to use handle transactions and swaps
+    tools: thirdweb.tools(), // optional, to handle transactions and swaps
📜 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 37cda56 and b9b4ce7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • .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/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.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • .changeset/many-pants-tease.md
  • apps/portal/package.json
🚧 Files skipped from review as they are similar to previous changes (26)
  • packages/ai-sdk-provider/package.json
  • packages/ai-sdk-provider/src/exports/thirdweb.ts
  • apps/playground-web/src/components/loader.tsx
  • apps/dashboard/package.json
  • packages/ai-sdk-provider/README.md
  • apps/playground-web/src/components/response.tsx
  • packages/ai-sdk-provider/tsconfig.json
  • apps/playground-web/src/components/code/code-example.tsx
  • apps/playground-web/package.json
  • apps/playground-web/src/components/conversation.tsx
  • package.json
  • packages/ai-sdk-provider/src/provider.ts
  • packages/ai-sdk-provider/src/types.ts
  • apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
  • apps/playground-web/src/app/api/chat/route.ts
  • apps/playground-web/src/components/ui/avatar.tsx
  • apps/playground-web/src/components/message.tsx
  • packages/ai-sdk-provider/src/tools.ts
  • apps/nebula/package.json
  • packages/ai-sdk-provider/tsconfig.base.json
  • apps/playground-web/src/components/prompt-input.tsx
  • packages/ai-sdk-provider/tsconfig.build.json
  • apps/playground-web/src/app/navLinks.ts
  • packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
  • apps/playground-web/src/app/ai/ai-sdk/page.tsx
  • apps/playground-web/src/components/reasoning.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{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
**/*.{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
🧠 Learnings (2)
📚 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: Run `pnpm biome check --apply` before committing (pre-commit hook)

Applied to files:

  • packages/ai-sdk-provider/biome.json
🪛 LanguageTool
apps/portal/src/app/ai/chat/ai-sdk/page.mdx

[grammar] ~3-~3: There might be a mistake here.
Context: ...t plugs thirdweb into the Vercel AI SDK. It standardizes message parts, exposes w...

(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). (6)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
packages/ai-sdk-provider/biome.json (2)

2-2: Schema version matches root (2.0.6) and loads with Biome CLI v2.1.2; no action required.


13-13: Override selector key should be "include", not "includes"

Using the wrong key likely makes this override a no-op.

-      "includes": ["package.json"]
+      "include": ["package.json"]

Likely an incorrect or invalid review comment.

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

28-31: LGTM: correct route and placement

Link path matches the new MDX page and the section ordering is consistent.

apps/portal/src/app/ai/chat/ai-sdk/page.mdx (3)

53-61: Verify event hook used for attaching metadata

Confirm that messageMetadata({ part }) with part.type === "finish-step" is supported by your stream helper and AI SDK version. If not, switch to the supported finish callback or emit a custom event to carry session_id.


97-99: Confirm message shape used in the comment

Docs mention message.parts[...]; AI SDK messages typically expose content parts. Keep as-is if ThirdwebAiMessage intentionally uses parts, otherwise update the comment for consistency.


25-27: Ignore outdated v4 helper suggestion
This page targets AI SDK v5—keep using convertToModelMessages and UIMessage (not convertToCoreMessages) and continue calling result.toUIMessageStreamResponse() (the toDataStreamResponse helper is from v4). (v5.ai-sdk.dev, ai-sdk.dev)

Likely an incorrect or invalid review comment.

Comment on lines +3 to +4
The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK.
It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
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

Fix grammar in opening sentence

Missing verb and stray double space.

-The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK.  
-It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
+`@thirdweb-dev/ai-sdk-provider` is a thin provider that plugs thirdweb into the Vercel AI SDK.
+It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
📝 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
The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK.
It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
`@thirdweb-dev/ai-sdk-provider` is a thin provider that plugs thirdweb into the Vercel AI SDK.
It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
🧰 Tools
🪛 LanguageTool

[grammar] ~3-~3: There might be a mistake here.
Context: ...t plugs thirdweb into the Vercel AI SDK. It standardizes message parts, exposes w...

(QB_NEW_EN)

🤖 Prompt for AI Agents
In apps/portal/src/app/ai/chat/ai-sdk/page.mdx around lines 3 to 4, the opening
sentence is missing a verb and contains a stray double space; update it to be a
complete sentence (for example, "The `@thirdweb-dev/ai-sdk-provider` is a thin
provider that plugs thirdweb into the Vercel AI SDK.") and remove the extra
space between sentences.

Comment on lines +85 to +95
const send = (message: string) => {
sendMessage(
{ text: message },
{
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

Fix client example: sendMessage signature, duplicate return, and React key

  • sendMessage should pass text and use data (not body) for extra payload.
  • Remove return return.
  • Add key to mapped elements to avoid React warnings.
-    const send = (message: string) => {
-    sendMessage(
-      { text: message },
-      {
-        body: {
-          // send session id for continuity
-          sessionId,
-        },
-      },
-    );
-  };
+  const send = (text: string) => {
+    sendMessage(text, {
+      data: {
+        // send session id for continuity
+        sessionId,
+      },
+    });
+  };
@@
-  return return (
+  return (
     <>
-      {messages.map((message) => (
-        <RenderMessage message={message} />
+      {messages.map((message) => (
+        <RenderMessage key={message.id} message={message} />
       ))}
       <ChatInputBox send={send} />
     </>
-  );;
+  );

Also applies to: 99-107

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: 1

♻️ Duplicate comments (15)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (6)

302-311: Mirror error handling for swap to keep agent state consistent.

 onError={(error) => {
-  sendMessage(
-    { text: `Transaction failed: ${error.message}` },
-    {
-      body: {
-        sessionId,
-      },
-    },
-  );
+  addToolResult({
+    tool: "sign_swap",
+    toolCallId,
+    output: { error: String(error?.message || error) } as any,
+  });
+  sendMessage({ text: "Swap signing failed." }, { body: { sessionId } });
 }}

30-33: Do not import server client with secretKey in a client component; use a public client.

Importing from lib/client can leak THIRDW EB_SECRET_KEY to the browser. Swap to a client-safe export and fix the Loader import path.

-import { Loader } from "../../../../components/loader";
-import { THIRDWEB_CLIENT } from "../../../../lib/client";
+import { Loader } from "@/components/loader";
+// never ship secretKey to the browser
+import { THIRDWEB_PUBLIC_CLIENT as THIRDWEB_CLIENT } from "@/lib/public-client";

Create 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,
});

Want me to add this file and update other client imports using lib/client?


167-181: Adopt camelCase chainId and pass the correct value to defineChain.

Schema and SDKs use chainId (camelCase). Also coerce either shape on input.

-  const transactionData: {
-    chain_id: number;
+  const transactionData: {
+    chainId: number;
     to: string;
     data: `0x${string}`;
     value: bigint;
   } = useMemo(() => {
     return {
-      chain_id: input?.chain_id || 8453,
+      chainId: input?.chainId ?? input?.chain_id ?? 8453,
       to: input?.to || "",
       data: (input?.data as `0x${string}`) || "0x",
       value: input?.value ? BigInt(input.value) : BigInt(0),
     };
   }, [input]);
@@
-          prepareTransaction({
+          prepareTransaction({
             client: THIRDWEB_CLIENT,
-            chain: defineChain(transactionData.chain_id),
+            chain: defineChain(transactionData.chainId),
             to: transactionData.to,
             data: transactionData.data,
             value: transactionData.value,
           })
         }
@@
           output: {
             transaction_hash: transaction.transactionHash,
-            chain_id: transaction.chain.id,
+            chain_id: transaction.chain.id, // keep output shape if tools expect snake_case
           },

Also applies to: 195-201, 208-211


184-201: Guard against invalid tx payload before preparing the transaction.

Prevents calling prepareTransaction with empty to/data/value. Also replace inline style with Tailwind per guidelines.

   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>
+    );
+  }
+
   return (
     <div className="py-4">
-      <TransactionButton
-        style={{
-          width: "100%",
-        }}
+      <TransactionButton
+        className="w-full"
         transaction={() =>

251-265: Mirror chainId rename for swap flow and pass correct chain to defineChain.

-  const transactionData: {
-    chain_id: number;
+  const transactionData: {
+    chainId: number;
     to: string;
     data: `0x${string}`;
     value: bigint;
   } = useMemo(() => {
     return {
-      chain_id: input?.transaction?.chain_id || 8453,
+      chainId: input?.transaction?.chainId ?? input?.transaction?.chain_id ?? 8453,
       to: input?.transaction?.to || "",
       data: (input?.transaction?.data as `0x${string}`) || "0x",
       value: input?.transaction?.value
         ? BigInt(input.transaction.value)
         : BigInt(0),
     };
   }, [input]);
@@
-            chain: defineChain(transactionData.chain_id),
+            chain: defineChain(transactionData.chainId),
             to: transactionData.to,
             data: transactionData.data,
             value: transactionData.value,

Also applies to: 281-285


268-286: Add swap payload guard and Tailwind width.

   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 swap transaction details…
+      </div>
+    );
+  }
+
   return (
     <div className="py-4">
-      <TransactionButton
-        style={{
-          width: "100%",
-        }}
+      <TransactionButton
+        className="w-full"
         transaction={() =>
packages/ai-sdk-provider/package.json (1)

15-21: Use “require” condition in exports instead of “default”.

Prevents resolver confusion for CJS.

   "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 (6)

120-127: Guard lastUserMessage; avoid [undefined] in request body.

Prevents malformed payloads when no prior user/tool message exists.

-    const messages = this.settings.context?.session_id
-      ? [lastUserMessage]
-      : allMessages;
+    const messages =
+      this.settings.context?.session_id && lastUserMessage
+        ? [lastUserMessage]
+        : allMessages;

Also applies to: 184-191


198-206: Advertise SSE via Accept header for streaming requests.

Some backends gate behavior on Accept: text/event-stream.

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

356-364: Emit session_id as response metadata, not as id.

Conform to AI SDK stream contract.

-                        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 },
+                          });
+                        }

396-406: Normalize tool input shapes (chainId).

Accept both chainId and chain_id, emit camelCase for the tool.

-                            case "sign_transaction":
+                            case "sign_transaction":
                               toolName = "sign_transaction";
                               input = {
-                                chain_id: parsed.data.chain_id,
+                                chainId:
+                                  parsed.data.chainId ?? parsed.data.chain_id,
                                 to: parsed.data.to,
                                 value: parsed.data.value,
                                 data: parsed.data.data,
                                 function: parsed.data.function,
                               } as SignTransactionInput;
                               break;

433-441: Do not stringify tool-call input; pass the object.

Stringifying breaks downstream type safety.

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

64-71: Flatten system message content to string.

thirdweb API likely expects content: string, not arrays.

-        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/portal/src/app/ai/chat/ai-sdk/page.mdx (2)

3-4: Grammar: fix opening sentence.

-The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK.  
-It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
+`@thirdweb-dev/ai-sdk-provider` is a thin provider that plugs thirdweb into the Vercel AI SDK.
+It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.

85-107: Fix client example: sendMessage signature, duplicate return, and missing React keys.

Aligns with current useChat API and avoids React warnings.

-  const send = (message: string) => {
-    sendMessage(
-      { text: message },
-      {
-        body: {
-          // send session id for continuity
-          sessionId,
-        },
-      },
-    );
-  };
+  const send = (text: string) => {
+    sendMessage(text, {
+      data: {
+        // send session id for continuity
+        sessionId,
+      },
+    });
+  };
@@
-  return return (
+  return (
     <>
-      {messages.map((message) => (
-        <RenderMessage message={message} />
+      {messages.map((message) => (
+        <RenderMessage key={message.id} message={message} />
       ))}
       <ChatInputBox send={send} />
     </>
-  );;
+  );
🧹 Nitpick comments (4)
packages/ui/package.json (2)

30-30: Move Prettier to devDependencies.

Prettier is a build-time tool; keeping it in dependencies bloats consumers. Suggest relocating to devDependencies.

   "dependencies": {
-    "prettier": "3.6.2",
   },
   "devDependencies": {
+    "prettier": "3.6.2",
   }

28-28: Consider making Next a peerDependency (and possibly devDependency).

If this UI package is consumed by Next apps, declaring next as a peer avoids duplicate Next versions in the graph and reduces install size. Keep as a direct dep only if this package runs Next code at runtime.

Also applies to: 43-46

apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (2)

218-227: Also surface tool failure to the LLM with addToolResult (optional).

Today only a chat message is sent; the tool call may remain unresolved.

 onError={(error) => {
-  sendMessage(
-    { text: `Transaction failed: ${error.message}` },
-    { body: { sessionId } },
-  );
+  // mark the tool call as failed for the agent
+  addToolResult({
+    tool: "sign_transaction",
+    toolCallId,
+    output: {
+      error: String(error?.message || error),
+    } as any, // if schema doesn't support error, skip this block
+  });
+  sendMessage({ text: `Transaction failed: ${error.message}` }, { body: { sessionId } });
 }}

107-111: Remove console.log in production UI.

Avoid noisy logs in client bundles.

-                      case "tool-sign_swap":
-                        console.log("---sign_swap", part);
+                      case "tool-sign_swap":
📜 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 b9b4ce7 and 06efc26.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • .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/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.ts (1 hunks)
  • packages/ui/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (27)
  • .changeset/many-pants-tease.md
  • packages/ai-sdk-provider/tsconfig.json
  • apps/nebula/package.json
  • apps/playground-web/src/app/api/chat/route.ts
  • apps/playground-web/package.json
  • apps/dashboard/package.json
  • package.json
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/components/code/code-example.tsx
  • apps/playground-web/src/components/ui/avatar.tsx
  • apps/portal/src/app/ai/sidebar.tsx
  • packages/ai-sdk-provider/biome.json
  • packages/ai-sdk-provider/src/tools.ts
  • apps/playground-web/src/components/conversation.tsx
  • packages/ai-sdk-provider/src/exports/thirdweb.ts
  • apps/playground-web/src/components/loader.tsx
  • packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
  • packages/ai-sdk-provider/README.md
  • apps/portal/package.json
  • apps/playground-web/src/components/message.tsx
  • apps/playground-web/src/app/ai/ai-sdk/page.tsx
  • apps/playground-web/src/components/response.tsx
  • packages/ai-sdk-provider/tsconfig.build.json
  • packages/ai-sdk-provider/tsconfig.base.json
  • apps/playground-web/src/components/prompt-input.tsx
  • apps/playground-web/src/components/reasoning.tsx
  • packages/ai-sdk-provider/src/types.ts
🧰 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:

  • packages/ai-sdk-provider/src/provider.ts
  • 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/ai-sdk-provider/src/provider.ts
  • apps/playground-web/src/app/ai/ai-sdk/components/chat-container.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/app/ai/ai-sdk/components/chat-container.tsx
🧠 Learnings (3)
📚 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-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
🧬 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 (8)
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/response.tsx (1)
  • Response (9-20)
apps/playground-web/src/components/reasoning.tsx (3)
  • Reasoning (43-111)
  • ReasoningTrigger (115-146)
  • ReasoningContent (154-167)
apps/playground-web/src/components/loader.tsx (1)
  • Loader (86-96)
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)
🪛 LanguageTool
apps/portal/src/app/ai/chat/ai-sdk/page.mdx

[grammar] ~3-~3: There might be a mistake here.
Context: ...t plugs thirdweb into the Vercel AI SDK. It standardizes message parts, exposes w...

(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: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Size
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)

47-60: Ensure consistent sendMessage payload key (body vs data). This component sends the sessionId under body, but the official examples use data. Confirm which property your installed @ai-sdk/react/ai version expects and align all calls across the codebase.

@@ -31,7 +31,7 @@
"react-children-utilities": "^2.10.0",
"react-markdown": "10.1.0",
"remark-gfm": "4.0.1",
"shiki": "1.27.0",
"shiki": "3.12.0",
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

Major bump to Shiki v3 — verify API/usage across the repo.

Shiki 3 has breaking changes (e.g., getHighlighter options, BUNDLED_LANGUAGES/BUNDLED_THEMES moved, dropped setCDN/loadLanguage/loadTheme, theme handling via themes, etc.). Please scan usages and migrate or add @shikijs/compat if needed. (shiki.style)

Run this to find likely-breaking patterns:


🏁 Script executed:

#!/bin/bash
# Shiki v3 migration check
rg -nP -C2 --type=ts --type=tsx '\b(getHighlighter|codeToThemedTokens|BUNDLED_LANGUAGES|BUNDLED_THEMES|setCDN|load(Language|Theme)|setWasm|ansiToHtml)\b|from\s+[\'"]shiki[\'"]'

Length of output: 116


🏁 Script executed:

#!/bin/bash
# Shiki v3 migration check: find direct imports and API usage patterns
rg -nP -C2 --type ts --type tsx \
  -e "\\b(getHighlighter|codeToThemedTokens|BUNDLED_LANGUAGES|BUNDLED_THEMES|setCDN|load(?:Language|Theme)|setWasm|ansiToHtml)\\b" \
  -e "from\\s+['\\\"]shiki['\\\"]"

Length of output: 222


🏁 Script executed:

#!/bin/bash
# Shiki v3 migration check: search .ts and .tsx files for Shiki v2 APIs and imports
rg -nP -C2 -g '*.ts' -g '*.tsx' \
  -e '\\b(getHighlighter|codeToThemedTokens|BUNDLED_LANGUAGES|BUNDLED_THEMES|setCDN|load(?:Language|Theme)|setWasm|ansiToHtml)\\b' \
  -e "from\\s+['\\\"]shiki['\\\"]"

Length of output: 4194


Migrate all Shiki v2 API usages to v3 or include @shikijs/compat

The following files import Shiki v2 APIs that will break under v3:

  • packages/ui/src/components/code/getCodeHtml.tsx: codeToHtml, BundledLanguage
  • apps/portal/src/app/changelog/utils/transform.tsx: BundledLanguage, SpecialLanguage
  • apps/nebula/src/@/components/ui/code/getCodeHtml.tsx: codeToHtml, BundledLanguage
  • packages/ui/src/components/code/code.client.tsx & code.server.tsx: BundledLanguage
  • apps/playground-web/src/components/code/code-example.tsx: BundledLanguage

Refer to the Shiki v3 migration guide to replace codeToHtml/getHighlighter, update types, and handle themes: https://shiki.style/guide/migrate?utm_source=openai

🤖 Prompt for AI Agents
In packages/ui/package.json around line 34, the project pins "shiki": "3.12.0"
but multiple files still use Shiki v2 APIs; update all listed files to use Shiki
v3 APIs (or add the compatibility package) by either installing and importing
@shikijs/compat or migrating to getHighlighter/getHighlighter options: replace
codeToHtml usages with the v3 highlighter API (create a highlighter via
getHighlighter({ theme, langs }) and call highlighter.codeToHtml or
highlighter.codeToHtml with appropriate theme options), replace
BundledLanguage/SpecialLanguage types/usages with v3 equivalents or import types
from @shikijs/compat, and ensure language bundles and themes are loaded via the
v3 loader pattern; apply these changes in the specific files mentioned in the
review (packages/ui/src/components/code/getCodeHtml.tsx,
apps/portal/src/app/changelog/utils/transform.tsx,
apps/nebula/src/@/components/ui/code/getCodeHtml.tsx,
packages/ui/src/components/code/code.client.tsx & code.server.tsx,
apps/playground-web/src/components/code/code-example.tsx), updating imports and
types accordingly so the code compiles against Shiki v3 or use @shikijs/compat
as a temporary shim.

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