Skip to content

Conversation

@johann-taberlet
Copy link

@johann-taberlet johann-taberlet commented Dec 16, 2025

🎯 Changes

Adds a new adapter package @tanstack/ai-claude-agent-sdk that integrates the Claude Agent SDK with TanStack AI.

Features

  • Streaming chat completions via Claude Agent SDK's agentic runtime
  • Extended thinking support with configurable token budgets
  • Built-in Claude Code tools - Bash, Read, Write, Edit, Glob, Grep, etc.
  • Multimodal content - images and documents support
  • Full type safety with per-model provider options
  • Automatic authentication via Claude Max subscription or API key

⚠️ Known Limitation: Custom Tools Not Supported

Custom TanStack AI tools are currently not functional with this adapter.

This is due to an upstream issue in the Claude Agent SDK (anthropics/claude-agent-sdk-typescript#38):

  • The Claude Agent SDK requires Zod v3 as a peer dependency
  • TanStack AI supports Zod v4 (and uses it internally)
  • When converting custom tool Zod schemas to MCP format, the version mismatch causes schema generation to fail, resulting in empty parameter schemas
  • This affects all custom tools defined with toolDefinition() from TanStack AI

Workaround: Use the built-in Claude Code tools (Bash, Read, Write, Edit, Glob, Grep, etc.) which work correctly as they don't require Zod schema conversion.

Status: Waiting for the Claude Agent SDK to upgrade to Zod v4. Once #38 is resolved, custom tools should work.

Package Structure

packages/typescript/ai-claude-agent-sdk/
├── src/
│   ├── index.ts                    # Main exports
│   ├── claude-agent-sdk-adapter.ts # Core adapter implementation
│   ├── builtin-tools.ts            # Claude Code built-in tools
│   ├── model-meta.ts               # Model metadata & types
│   ├── message-types.ts            # Multimodal message types
│   └── tools/                      # Tool conversion utilities
└── tests/                          # Comprehensive test suite

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • New Features

    • Adds a Claude Agent SDK adapter with streaming chat completions, summarization, and automatic auth
    • Extended "thinking" capability with configurable token budgets
    • Tool integration: supports custom tools and a set of built-in Claude Code tools
    • Multimodal support for images and documents
    • Type-safe per-model provider options; embeddings disabled (explicit error)
  • Tests

    • Comprehensive unit tests covering streaming, multimodal handling, tools, thinking, summarization, and error paths

✏️ Tip: You can customize this high-level summary in your review settings.

johann-taberlet and others added 2 commits December 16, 2025 14:53
Add a new adapter that enables Claude Max subscribers to use their
subscription for AI development via Claude Code/Agent SDK.

Supported features:
- Basic chat with streaming
- Built-in Claude Code tools (Bash, Read, Write, Edit, Glob, Grep, etc.)
- Extended thinking with budget_tokens
- Summarization

Known limitation:
Custom MCP tools are not supported due to Zod 4 incompatibility in the
Claude Agent SDK. See: anthropics/claude-agent-sdk-typescript#38
For custom tools, use the Anthropic adapter instead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix ESLint import ordering and unnecessary type conditions
- Update test to match actual SDK behavior
- Add changeset for release

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@johann-taberlet johann-taberlet requested a review from a team December 16, 2025 14:10
@coderabbitai
Copy link

coderabbitai bot commented Dec 16, 2025

Warning

Rate limit exceeded

@johann-taberlet has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 44 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 08fc91f and 26fbee7.

📒 Files selected for processing (2)
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1 hunks)

Walkthrough

Adds a new @tanstack/ai-claude-agent-sdk package implementing a Claude Agent SDK adapter with streaming chat, configurable thinking budgets, tool integration (custom + built-in Claude Code), multimodal content handling, per-model provider options, summarization, and explicit embeddings-not-supported behavior.

Changes

Cohort / File(s) Change Summary
Package manifest & build
packages/typescript/ai-claude-agent-sdk/package.json, packages/typescript/ai-claude-agent-sdk/tsconfig.json, packages/typescript/ai-claude-agent-sdk/vite.config.ts
New package metadata, scripts, dependencies, TypeScript config, and Vite/Vitest configuration for the new adapter package.
Core adapter implementation
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
New ClaudeAgentSdk class and factories implementing streaming chat, thinking block handling, tool-call streaming, prompt construction, error mapping, summarization, and explicit embeddings-not-supported error; includes option mapping and helper utilities.
Public index & exports
packages/typescript/ai-claude-agent-sdk/src/index.ts
Re-exports adapter, factories, config types, model metadata, provider options, message metadata, tool converters, and built-in tool definitions.
Model metadata
packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
Adds model constants (haiku, sonnet, opus), CLAUDE_AGENT_SDK_MODELS array, and derived types for provider options and input modalities.
Provider options & validation
packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts
New config interfaces (ClaudeAgentSdkConfig, ThinkingOptions, ClaudeAgentSdkProviderOptions, InternalClaudeAgentSdkOptions) and validateTextProviderOptions enforcing mutual exclusions and token/constraint rules.
Message metadata / modalities
packages/typescript/ai-claude-agent-sdk/src/message-types.ts
New multimodal metadata types for text, image, document, audio, and video modalities plus cache-control and media-type constraints.
Built-in Claude Code tools
packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts
Introduces BUILTIN_TOOL_MARKER, BuiltinToolDefinition, helper, type guard, and predefined built-in tools (Read, Write, Edit, Bash, Glob, Grep, WebFetch, WebSearch, Task, TodoWrite, NotebookEdit, AskUserQuestion).
Tool conversion & custom tools
packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts, packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts, packages/typescript/ai-claude-agent-sdk/src/tools/index.ts
New CustomTool and CacheControl types, converter from TanStack AI Tool (Zod → JSON Schema), convertToolsToProviderFormat helper, and tool re-exports.
Multimodal content handling tests
packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts
Tests for converting image/document ContentParts into SDK prompt content streams, mixed-modality handling, and error emission for unsupported modalities.
Adapter behavior tests
packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts
Tests covering streaming behavior, thinking blocks, tool interactions, summarization, error handling, and embeddings-not-supported assertion.
Tool conversion tests
packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
Tests for tool conversion (Zod schemas → adapter JSON Schema), cache control preservation, enums, arrays, nested objects, and empty arrays.
Changelog
.changeset/claude-agent-sdk-adapter.md
Adds changeset describing the new adapter package and features.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant ClaudeAdapter as Adapter
participant ClaudeSDK as Claude Agent SDK
participant ToolExec as Tool / Built-in
Client->>ClaudeAdapter: chatStream(request with messages, tools, options)
ClaudeAdapter->>ClaudeSDK: buildPrompt + attach multimodal streams
ClaudeSDK-->>ClaudeAdapter: stream of deltas (content / thinking / tool_call / done / error)
alt tool_call emitted
ClaudeAdapter->>ToolExec: invoke tool or map builtin
ToolExec-->>ClaudeAdapter: tool result
ClaudeAdapter->>Client: emit tool_call chunk
ClaudeAdapter->>Client: emit tool_result chunk (embedded as assistant message)
else content/thinking deltas
ClaudeAdapter->>Client: emit content/thinking chunks (with model, id, timestamps)
end
ClaudeSDK-->>ClaudeAdapter: done (usage metadata)
ClaudeAdapter->>Client: emit done chunk (with usage/error mapping)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Files/areas to focus on:
    • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (stream state machine, abort handling, delta → chunk mapping)
    • packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (validation logic and thinking token constraints)
    • packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts and tools/* (JSON Schema conversion, built-in vs custom tool handling)
    • tests/* (mock fidelity to SDK stream shapes and edge-case coverage)

"I tunneled through code with a twitch and a grin,
Streams of small thoughts and a toolbox within,
Images and PDFs neatly tucked in a row,
Tokens for thinking, and tool calls that glow,
A rabbit’s soft cheer — new adapter will grow!"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.37% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a new Claude Agent SDK adapter for TanStack AI, which aligns with the core objective of this PR.
Description check ✅ Passed The description comprehensively covers changes, features, known limitations, package structure, and completes the required checklist sections from the template.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

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

🧹 Nitpick comments (11)
packages/typescript/ai-claude-agent-sdk/package.json (1)

56-58: Use semver range for peerDependencies in published packages.

Using workspace:* in peerDependencies may not resolve correctly when the package is published to npm. Consider using a semver range that will be substituted during publish, or ensure your publish workflow transforms this value.

   "peerDependencies": {
-    "@tanstack/ai": "workspace:*"
+    "@tanstack/ai": ">=0.0.1"
   }
packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts (1)

49-68: Consider using a type guard or stricter typing for metadata extraction.

The type assertion on line 51 casts tool.metadata broadly. While this works, a more defensive approach would improve type safety.

 export function convertCustomToolToAdapterFormat(tool: Tool): CustomTool {
-  const metadata =
-    (tool.metadata as { cacheControl?: CacheControl | null } | undefined) || {}
+  const metadata = tool.metadata as
+    | { cacheControl?: CacheControl | null }
+    | undefined
+  const cacheControl = metadata?.cacheControl ?? null

   // Convert Zod schema to JSON Schema
   const jsonSchema = convertZodToJsonSchema(tool.inputSchema)

   const inputSchema = {
     type: 'object' as const,
     properties: jsonSchema?.properties || null,
     required: jsonSchema?.required || null,
   }

   return {
     name: tool.name,
     type: 'custom',
     description: tool.description,
     input_schema: inputSchema,
-    cache_control: metadata.cacheControl || null,
+    cache_control: cacheControl,
   }
 }
packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts (1)

403-412: Consider adding explicit typing for mock stream messages.

The createMockStream helper uses any[] for messages. Adding a union type for the expected message shapes would improve maintainability and catch mock data errors early.

-function createMockStream(messages: any[]): AsyncIterable<any> {
+type MockMessage =
+  | ReturnType<typeof createSystemMessage>
+  | ReturnType<typeof createAssistantMessage>
+  | ReturnType<typeof createResultMessage>
+
+function createMockStream(messages: MockMessage[]): AsyncIterable<MockMessage> {
   return {
     async *[Symbol.asyncIterator]() {
       for (const msg of messages) {
         yield msg
       }
     },
   }
 }
packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts (2)

116-151: Test logic is convoluted and potentially misleading.

This test consumes the stream twice with separate mock setups. The first invocation (lines 126-131) is consumed without assertions, then a new mock is set up (lines 135-140) and the stream is consumed again (lines 142-147). The final assertion only verifies the second call's result.

Consider simplifying to test the default model behavior in a single stream invocation:

     it('should use default model when not specified', async () => {
       const mockStream = createMockStream([
         createSystemMessage(),
         createAssistantMessage('Response'),
         createResultMessage('success'),
       ])
       mockQuery.mockReturnValue(mockStream)

       const adapter = claudeAgentSdk()
+      const chunks: StreamChunk[] = []

       for await (const _ of adapter.chatStream({
-        model: '',
+        model: '' as any,
         messages: [{ role: 'user', content: 'Test' }],
       })) {
-        // consume
+        chunks.push(_)
       }

-      // Should default to sonnet
-      const chunks: StreamChunk[] = []
-      const mockStream2 = createMockStream([
-        createSystemMessage(),
-        createAssistantMessage('Response'),
-        createResultMessage('success'),
-      ])
-      mockQuery.mockReturnValue(mockStream2)
-
-      for await (const chunk of adapter.chatStream({
-        model: '',
-        messages: [{ role: 'user', content: 'Test' }],
-      })) {
-        chunks.push(chunk)
-      }
-
       const doneChunk = chunks.find((c) => c.type === 'done')
       expect(doneChunk?.model).toBe('sonnet')
     })

320-341: Consider strengthening the style verification assertion.

The test for bullet-points style only verifies that mockQuery was called with prompt: expect.any(String), which doesn't actually confirm the style was incorporated into the system prompt. Consider asserting on the actual prompt content.

       // Verify the query was called with system prompt mentioning bullet points
       expect(mockQuery).toHaveBeenCalledWith(
         expect.objectContaining({
-          prompt: expect.any(String),
+          prompt: expect.stringContaining('Text to summarize'),
         }),
       )
+      // The style is applied via systemPrompts in chatStream options, which affects
+      // the internal buildSummarizationPrompt - consider adding integration test
packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (1)

35-64: Consider camelCase for consistency with coding guidelines.

The properties stop_sequences and top_k use snake_case, while maxTurns uses camelCase. Per coding guidelines, camelCase is preferred. However, if this intentionally mirrors Anthropic's API naming conventions for familiarity, consider adding a comment explaining the rationale.

 export interface ClaudeAgentSdkProviderOptions {
   /**
    * Extended thinking configuration.
    */
   thinking?: ThinkingOptions

   /**
    * Custom stop sequences that will cause the model to stop generating.
+   * Note: Uses snake_case to match Anthropic API conventions.
    */
   stop_sequences?: Array<string>

   /**
    * Top-K sampling parameter.
+   * Note: Uses snake_case to match Anthropic API conventions.
    */
   top_k?: number
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (4)

347-370: Consider log level or configurable logging for error details.

The console.error call logs full error details including the stack trace, which is helpful for debugging but may be verbose in production. Consider making this configurable or using a debug flag.


420-429: Silent auto-adjustment of max_tokens may be unexpected.

When thinking.budget_tokens exceeds maxTokens, the code silently adjusts max_tokens to budget_tokens + 1. This could be surprising to users who explicitly set a lower maxTokens. Consider logging a warning or documenting this behavior.

     const maxTokens =
       thinkingBudget && thinkingBudget >= defaultMaxTokens
-        ? thinkingBudget + 1
+        ? (() => {
+            console.warn(
+              `[Claude Agent SDK Adapter] max_tokens adjusted from ${defaultMaxTokens} to ${thinkingBudget + 1} to accommodate thinking.budget_tokens`
+            )
+            return thinkingBudget + 1
+          })()
         : defaultMaxTokens

632-660: Factory functions are identical implementations.

Both createClaudeAgentSdk and claudeAgentSdk have identical implementations. If intentional (for different naming conventions), consider having one delegate to the other to reduce duplication.

 export function createClaudeAgentSdk(
   config?: ClaudeAgentSdkConfig,
 ): ClaudeAgentSdk {
   return new ClaudeAgentSdk(config)
 }

 /**
  * Create a Claude Agent SDK adapter with default configuration.
  * ...
  */
 export function claudeAgentSdk(config?: ClaudeAgentSdkConfig): ClaudeAgentSdk {
-  return new ClaudeAgentSdk(config)
+  return createClaudeAgentSdk(config)
 }

585-608: Minor inefficiency: collects all user messages but only uses the last.

The method iterates through all messages and collects user messages into an array, but ultimately only returns the last one. Consider returning early or iterating in reverse to find the last user message directly.

   private buildPromptString(messages: Array<ModelMessage>): string {
-    // Find all user messages and combine them
-    const userMessages: Array<string> = []
-
-    for (const message of messages) {
+    // Find the last user message
+    for (let i = messages.length - 1; i >= 0; i--) {
+      const message = messages[i]
       if (message.role === 'user') {
-        let content: string
         if (typeof message.content === 'string') {
-          content = message.content
-        } else if (message.content) {
-          content = message.content
+          return message.content
+        } else if (message.content && message.content.length > 0) {
+          return message.content
             .map((part) => this.convertContentPartToText(part))
             .join('\n')
-        } else {
-          content = ''
         }
-        userMessages.push(content)
       }
     }
-
-    // Return the last user message or empty string
-    const lastMessage = userMessages[userMessages.length - 1]
-    return lastMessage ?? ''
+    return ''
   }
packages/typescript/ai-claude-agent-sdk/src/message-types.ts (1)

71-85: Address Biome linter warning for empty interfaces.

The static analysis tool flags empty interfaces as equivalent to {}. Since these are documented placeholders for unsupported modalities, consider using type aliases or adding a marker property to satisfy the linter while maintaining the intent.

 /**
  * Metadata for Claude Agent SDK audio content parts.
  * Note: Audio is NOT supported by Claude - placeholder for type compatibility.
  */
-export interface ClaudeAgentSdkAudioMetadata {
-  // Audio not supported - placeholder for type compatibility
-}
+export type ClaudeAgentSdkAudioMetadata = Record<string, never>

 /**
  * Metadata for Claude Agent SDK video content parts.
  * Note: Video is NOT supported by Claude - placeholder for type compatibility.
  */
-export interface ClaudeAgentSdkVideoMetadata {
-  // Video not supported - placeholder for type compatibility
-}
+export type ClaudeAgentSdkVideoMetadata = Record<string, never>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e011bf8 and 398d408.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • .changeset/claude-agent-sdk-adapter.md (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/package.json (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/index.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/tools/index.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/tsconfig.json (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/vite.config.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from /adapters subpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with toolDefinition() and Zod schema inference
Implement isomorphic tool system using toolDefinition() with .server() and .client() implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Files:

  • packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts
  • packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts
  • packages/typescript/ai-claude-agent-sdk/vite.config.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/index.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts
  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts
  • packages/typescript/ai-claude-agent-sdk/src/index.ts
  • packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for function and variable names throughout the codebase

Files:

  • packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts
  • packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts
  • packages/typescript/ai-claude-agent-sdk/vite.config.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/index.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts
  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts
  • packages/typescript/ai-claude-agent-sdk/src/index.ts
  • packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Write unit tests using Vitest alongside source files with .test.ts naming convention

Files:

  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
  • packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts
  • packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts
packages/typescript/*/src/model-meta.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Files:

  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
packages/typescript/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export tree-shakeable adapters with clear subpath exports in package.json (e.g., @tanstack/ai/adapters, @tanstack/ai-openai/adapters) to minimize bundle size

Files:

  • packages/typescript/ai-claude-agent-sdk/src/index.ts
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
  • packages/typescript/ai-claude-agent-sdk/vite.config.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/index.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • .changeset/claude-agent-sdk-adapter.md
  • packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts
  • packages/typescript/ai-claude-agent-sdk/tsconfig.json
  • packages/typescript/ai-claude-agent-sdk/src/index.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/*/package.json : Use `workspace:*` protocol for internal package dependencies in package.json (e.g., `"tanstack/ai": "workspace:*"`)

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/model-meta.ts : Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts
  • packages/typescript/ai-claude-agent-sdk/src/index.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/index.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts
  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • .changeset/claude-agent-sdk-adapter.md
  • packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
  • packages/typescript/ai-claude-agent-sdk/src/index.ts
  • packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement isomorphic tool system using `toolDefinition()` with `.server()` and `.client()` implementations for dual-environment execution

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts
  • packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/index.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts
  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with `toolDefinition()` and Zod schema inference

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts
  • packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts
  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.test.ts : Write unit tests using Vitest alongside source files with `.test.ts` naming convention

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/vite.config.ts
  • packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts
  • packages/typescript/ai-claude-agent-sdk/tsconfig.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to .eslintrc* : Use ESLint with custom TanStack config for linting all TypeScript and JavaScript files

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/vite.config.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/tools/index.ts
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts
  • packages/typescript/ai-claude-agent-sdk/src/index.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top

Applied to files:

  • .changeset/claude-agent-sdk-adapter.md
🧬 Code graph analysis (6)
packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (2)
packages/typescript/ai-claude-agent-sdk/src/index.ts (4)
  • ClaudeAgentSdkConfig (16-16)
  • ClaudeAgentSdkModel (22-22)
  • ThinkingOptions (30-30)
  • ClaudeAgentSdkProviderOptions (29-29)
packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1)
  • ClaudeAgentSdkModel (93-93)
packages/typescript/ai-claude-agent-sdk/src/tools/index.ts (1)
packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts (1)
  • CustomTool (15-41)
packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts (3)
packages/typescript/ai-claude-agent-sdk/src/tools/index.ts (2)
  • CustomTool (14-14)
  • convertCustomToolToAdapterFormat (4-4)
packages/typescript/ai-claude-agent-sdk/src/index.ts (1)
  • CustomTool (47-47)
packages/python/tanstack-ai/src/tanstack_ai/tool_utils.py (1)
  • tool (12-65)
packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts (2)
packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts (1)
  • convertToolsToProviderFormat (31-39)
packages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.ts (1)
  • convertCustomToolToAdapterFormat (49-69)
packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts (4)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (2)
  • claudeAgentSdk (658-660)
  • ClaudeAgentSdk (85-610)
packages/typescript/ai-claude-agent-sdk/src/index.ts (3)
  • claudeAgentSdk (15-15)
  • ClaudeAgentSdk (13-13)
  • CLAUDE_AGENT_SDK_MODELS (21-21)
packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1)
  • CLAUDE_AGENT_SDK_MODELS (84-88)
packages/typescript/ai/src/types.ts (1)
  • StreamChunk (672-680)
packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts (3)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1)
  • claudeAgentSdk (658-660)
packages/typescript/ai-claude-agent-sdk/src/index.ts (1)
  • claudeAgentSdk (15-15)
packages/typescript/ai/src/types.ts (2)
  • ContentPart (159-170)
  • StreamChunk (672-680)
🪛 Biome (2.1.2)
packages/typescript/ai-claude-agent-sdk/src/message-types.ts

[error] 75-77: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


[error] 83-85: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (29)
.changeset/claude-agent-sdk-adapter.md (1)

1-14: LGTM!

The changeset is well-documented and accurately describes the new adapter package features. The minor version bump is appropriate for introducing new functionality.

packages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.ts (1)

31-39: LGTM!

The converter function is well-documented with a practical Zod example that aligns with the coding guidelines. The implementation correctly delegates to the custom tool converter, maintaining a clean separation of concerns.

packages/typescript/ai-claude-agent-sdk/src/tools/index.ts (1)

1-14: LGTM!

Clean barrel exports with good documentation. The type alias ClaudeAgentSdkTool = CustomTool provides extensibility for future tool types while maintaining a clear public API. The separate /tools subpath export aligns with the tree-shakeable architecture guidelines.

packages/typescript/ai-claude-agent-sdk/package.json (1)

49-49: Package version is valid but is one release behind current.

The @anthropic-ai/claude-agent-sdk version 0.1.69 exists on npm; the current version is 0.1.70. The caret dependency ^0.1.69 allows patch updates up to 0.1.x, so npm will install 0.1.70 or newer 0.1 versions. Consider updating the dependency to ^0.1.70 to use the latest version explicitly.

packages/typescript/ai-claude-agent-sdk/vite.config.ts (1)

1-37: LGTM!

The Vite/Vitest configuration is well-structured with appropriate test settings, coverage configuration, and dual entry points (index.ts and tools/index.ts) that support tree-shakeable exports as per coding guidelines.

packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.ts (1)

1-151: LGTM!

Comprehensive test coverage for both convertToolsToProviderFormat and convertCustomToolToAdapterFormat. Tests properly use Zod schemas for input definitions as per coding guidelines, and cover key scenarios including simple tools, multiple tools, optional parameters, nested objects, arrays, cache control, and enums.

packages/typescript/ai-claude-agent-sdk/tests/message-converter.test.ts (1)

1-450: LGTM!

Comprehensive test suite covering multimodal content handling with proper mocking of the Claude Agent SDK. Tests verify image/document conversion (both base64 and URL), mixed content ordering, and appropriate error handling for unsupported modalities (audio, video).

packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1)

1-113: Well-structured model metadata file.

The implementation follows the coding guidelines for model metadata files with proper per-model type safety through ClaudeAgentSdkChatModelProviderOptionsByName and ClaudeAgentSdkModelInputModalitiesByName type maps. The as const satisfies ModelMeta pattern ensures type safety while preserving literal types.

packages/typescript/ai-claude-agent-sdk/src/builtin-tools.ts (4)

38-46: Well-implemented type guard with proper defensive checks.

The isBuiltinTool function correctly handles null/non-object cases before checking for the symbol marker. This is a robust pattern for runtime type checking.


268-271: Type assertion on Object.keys is acceptable here.

The cast Object.keys(builtinTools) as Array<BuiltinToolName> is safe because builtinTools is defined as const and its keys are statically known. This is a common TypeScript pattern for preserving literal types from object keys.


1-271: Comprehensive built-in tools implementation with good documentation.

Well-structured module providing typed built-in Claude Code tools. The use of Zod schemas for input validation aligns with coding guidelines, and the Symbol-based marker pattern enables reliable runtime identification of built-in tools. The JSDoc example in the builtinTools export helps users understand the intended usage.


55-221: All built-in tool names (Read, Write, Edit, Bash, Glob, Grep, WebFetch, WebSearch, Task, TodoWrite, NotebookEdit, AskUserQuestion) match the Claude Agent SDK specifications exactly as defined in the tool definitions. Tool names use correct PascalCase format and are properly exported in the builtinTools object.

packages/typescript/ai-claude-agent-sdk/tests/adapter.test.ts (7)

1-23: LGTM! Well-structured test setup with proper mocking.

The test imports and mock configuration follow Vitest best practices. The mock is correctly placed before the import of the mocked module.


24-50: LGTM! Good coverage of adapter instantiation scenarios.

The tests properly verify both factory functions and validate the exposed model list (haiku, sonnet, opus).


154-221: LGTM! Thorough validation of streaming content chunks.

The tests properly verify delta streaming, content accumulation, and metadata presence (timestamp, model, id format) across all chunks.


223-297: LGTM! Comprehensive error handling test coverage.

The tests properly verify error chunk emission for SDK errors, result-level errors, and HTTP status code mapping (429 → rate_limit).


358-487: LGTM! Solid test coverage for tool calls and tool results.

The tests validate tool_call chunk emission, finish reason handling, multiple tool calls, and tool result message formatting in conversation flow.


489-672: LGTM! Excellent coverage of extended thinking functionality.

The tests comprehensively cover thinking chunk emission, ordering relative to content chunks, budget token propagation to the SDK, usage statistics, and handling of full thinking blocks in assistant messages.


675-808: LGTM! Well-designed helper functions for test isolation.

The mock stream and message creation helpers provide clean abstractions for constructing synthetic SDK responses. The crypto.randomUUID?.() fallback handles environments without native crypto support.

packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (3)

1-14: LGTM! Clean configuration interface with clear documentation.

The ClaudeAgentSdkConfig interface is minimal and well-documented, appropriately noting that no API key is required since authentication is handled by the SDK runtime.


16-29: LGTM! Well-structured thinking options type.

The discriminated union pattern with type: 'enabled' | 'disabled' is clean and the documentation clearly states the constraint that budget_tokens must be less than max_tokens.


79-105: LGTM! Comprehensive validation with clear error messages.

The validation function properly enforces mutual exclusivity of top_p/temperature, minimum token constraints, and thinking budget constraints. Error messages are clear and actionable.

packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (5)

1-106: LGTM! Well-documented adapter class with clear feature documentation.

The class documentation clearly states supported features (basic chat, built-in tools, extended thinking, summarization) and known limitations (custom MCP tools due to Zod 4 incompatibility, no embeddings). The type parameters properly leverage the generic BaseAdapter.


185-309: LGTM! Careful handling of both partial and complete messages.

The logic correctly handles the case where both streaming deltas and complete messages may be received, using accumulated content checks to avoid duplicate emissions.


312-345: LGTM! Proper handling of result messages with usage extraction.

The result handling correctly differentiates success from error subtypes and properly extracts usage statistics for the done chunk.


373-404: LGTM! Clean summarization implementation using chatStream.

The implementation properly delegates to chatStream with a generated system prompt and extracts the final content and usage statistics.


452-486: LGTM! Exhaustive content part handling with clear placeholders.

The method properly handles all content part types with an exhaustive never check, and provides clear placeholder text for multimodal content.

packages/typescript/ai-claude-agent-sdk/src/message-types.ts (2)

1-50: LGTM! Well-documented metadata types with clear media type constraints.

The types properly define cache control semantics and media type constraints for Claude Agent SDK content parts.


87-97: LGTM! Clean modality-to-metadata type mapping.

The ClaudeAgentSdkMessageMetadataByModality interface provides a proper type map for multimodal message construction.

Comment on lines +1 to +69
/**
* @tanstack/ai-claude-agent-sdk
*
* Claude Agent SDK adapter for TanStack AI.
* Enables Claude Max subscribers to use their subscription for AI development
* via Claude Code/Agent SDK instead of requiring separate API keys.
*
* @packageDocumentation
*/

// Main adapter exports
export {
ClaudeAgentSdk,
createClaudeAgentSdk,
claudeAgentSdk,
type ClaudeAgentSdkConfig,
} from './claude-agent-sdk-adapter'

// Model metadata exports
export {
CLAUDE_AGENT_SDK_MODELS,
type ClaudeAgentSdkModel,
type ClaudeAgentSdkChatModelProviderOptionsByName,
type ClaudeAgentSdkModelInputModalitiesByName,
} from './model-meta'

// Provider options exports
export type {
ClaudeAgentSdkProviderOptions,
ThinkingOptions,
} from './text/text-provider-options'

// Message metadata exports
export type {
ClaudeAgentSdkTextMetadata,
ClaudeAgentSdkImageMetadata,
ClaudeAgentSdkDocumentMetadata,
ClaudeAgentSdkAudioMetadata,
ClaudeAgentSdkVideoMetadata,
ClaudeAgentSdkImageMediaType,
ClaudeAgentSdkDocumentMediaType,
ClaudeAgentSdkMessageMetadataByModality,
} from './message-types'

// Tool exports
export { convertToolsToProviderFormat } from './tools/tool-converter'
export type { ClaudeAgentSdkTool, CustomTool } from './tools'

// Built-in Claude Code tools
export {
builtinTools,
isBuiltinTool,
BUILTIN_TOOL_NAMES,
type BuiltinToolDefinition,
type BuiltinToolName,
// Individual tool exports for convenience
Read,
Write,
Edit,
Bash,
Glob,
Grep,
WebFetch,
WebSearch,
Task,
TodoWrite,
NotebookEdit,
AskUserQuestion,
} from './builtin-tools'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "package.json" packages/typescript/ai-claude-agent-sdk --exec cat {}

Repository: TanStack/ai

Length of output: 1597


Add /adapters subpath export to package.json.

The package.json currently exports ./tools but is missing the ./adapters subpath for tree-shakeable adapter imports. Per the coding guidelines, add an adapters export:

"exports": {
  ".": { ... },
  "./adapters": {
    "types": "./dist/esm/adapters/index.d.ts",
    "import": "./dist/esm/packages/typescript/ai-claude-agent-sdk/src/adapters/index.js"
  },
  "./tools": { ... }
}

This enables consumers to import only the adapter functionality they need without loading unrelated code.

Comment on lines +6 to +7
"include": ["vite.config.ts", "./src"],
"exclude": ["node_modules", "dist", "**/*.config.ts"]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Conflicting include/exclude patterns for config files.

vite.config.ts is explicitly included on line 6, but **/*.config.ts is excluded on line 7. The exclude pattern takes precedence, so vite.config.ts will be excluded despite being listed in include. If you intend to include vite.config.ts for type-checking, remove it from the exclude glob or make the exclude pattern more specific.

-  "exclude": ["node_modules", "dist", "**/*.config.ts"]
+  "exclude": ["node_modules", "dist"]
🤖 Prompt for AI Agents
packages/typescript/ai-claude-agent-sdk/tsconfig.json lines 6-7: the tsconfig
currently includes "vite.config.ts" but also excludes "**/*.config.ts", which
causes the exclude to override the include; to fix, update the exclude pattern
to not blanket-exclude vite config (e.g., remove "**/*.config.ts" or narrow it
to exclude only specific config files you don't want), or remove
"vite.config.ts" from exclude so the explicit include takes effect; ensure the
final change leaves vite.config.ts reachable by the compiler for type-checking.

johann-taberlet and others added 2 commits December 16, 2025 15:30
- Fix package.json export paths to use flat dist structure
- Fix memory leak by adding { once: true } to abort event listener

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update Opus max_output_tokens to 64K (Opus 4.5 spec)
- Replace empty interfaces with Record<string, never> type aliases
- Optimize buildPromptString to iterate in reverse for efficiency
- Make claudeAgentSdk delegate to createClaudeAgentSdk to reduce duplication

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@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

🧹 Nitpick comments (3)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (3)

424-433: Consider more explicit validation for thinking budget vs. max tokens.

The current logic automatically adjusts maxTokens to thinkingBudget + 1 when the thinking budget exceeds the default. This silently overrides the user's maxTokens setting, which could lead to unexpectedly large responses or higher costs.

Consider validating this constraint explicitly and either throwing a clear error or documenting this behavior prominently.

Example validation:

const thinkingBudget = providerOptions?.thinking?.type === 'enabled'
  ? providerOptions.thinking.budget_tokens
  : undefined
const maxTokens = options.options?.maxTokens || 1024

if (thinkingBudget !== undefined && thinkingBudget >= maxTokens) {
  throw new Error(
    `thinking.budget_tokens (${thinkingBudget}) must be less than maxTokens (${maxTokens}). ` +
    `Increase maxTokens to accommodate the thinking budget.`
  )
}

const requestParams: InternalClaudeAgentSdkOptions = {
  model: options.model,
  max_tokens: maxTokens,
  // ...

377-408: Consider clarifying maxLength vs. maxTokens mapping.

Line 385 maps options.maxLength directly to maxTokens. While this works as a rough approximation, maxLength might be interpreted as words or characters by users, not token count. Consider documenting this mapping or adding a more explicit conversion.

Add a comment or adjust the prompt to clarify the token-based constraint:

   async summarize(options: SummarizationOptions): Promise<SummarizationResult> {
     const systemPrompt = this.buildSummarizationPrompt(options)

     const chunks: Array<StreamChunk> = []
     for await (const chunk of this.chatStream({
       model: options.model,
       messages: [{ role: 'user', content: options.text }],
       systemPrompts: [systemPrompt],
+      // maxLength is treated as approximate token count
       options: { maxTokens: options.maxLength || 500 },
     })) {
       chunks.push(chunk)
     }

636-664: Consider consolidating duplicate factory functions.

Both createClaudeAgentSdk and claudeAgentSdk have identical implementations. While offering both naming patterns can be convenient for users, this creates maintenance overhead.

Consider either:

  1. Having one call the other to reduce duplication
  2. Removing one if there's no semantic difference
  3. Keeping both but documenting the preferred approach

Example consolidation:

export function createClaudeAgentSdk(
  config?: ClaudeAgentSdkConfig,
): ClaudeAgentSdk {
  return new ClaudeAgentSdk(config)
}

/**
 * Alias for createClaudeAgentSdk with default configuration.
 * ... (rest of docs)
 */
export function claudeAgentSdk(config?: ClaudeAgentSdkConfig): ClaudeAgentSdk {
  return createClaudeAgentSdk(config)
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 398d408 and ba501a5.

📒 Files selected for processing (2)
  • packages/typescript/ai-claude-agent-sdk/package.json (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/typescript/ai-claude-agent-sdk/package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from /adapters subpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with toolDefinition() and Zod schema inference
Implement isomorphic tool system using toolDefinition() with .server() and .client() implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for function and variable names throughout the codebase

Files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
🔇 Additional comments (4)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (4)

136-145: LGTM! Event listener cleanup addressed.

The abort event listener now uses { once: true } option, which properly addresses the memory leak concern from the previous review.


410-414: LGTM! Clear error for unsupported feature.

Explicitly throwing an error for embeddings with a helpful message directing users to alternative providers is the correct approach for an unsupported feature.


456-490: LGTM! Well-structured content part conversion.

The convertContentPartToText helper properly handles different content types with appropriate metadata extraction, clear error messages for unsupported types (audio/video), and an exhaustive check for type safety.


218-220: Accumulate input_json_delta events and stream partial tool call arguments.

TanStack AI supports streaming tool arguments where the model is streaming in an "input-streaming" state. Currently, input_json_delta events (line 218) are received but ignored, and tool calls are only yielded when complete assistant messages arrive (lines 269–296).

To enable incremental tool argument streaming, accumulate input_json_delta events into the tool input and yield partial tool_call chunks as data arrives, then finalize on content_block_stop. This provides real-time visibility into tool invocations and aligns with the StreamProcessor pattern for chunked response handling.

Comment on lines 85 to 414
export class ClaudeAgentSdk extends BaseAdapter<
typeof CLAUDE_AGENT_SDK_MODELS,
[],
ClaudeAgentSdkProviderOptions,
Record<string, never>,
ClaudeAgentSdkChatModelProviderOptionsByName,
ClaudeAgentSdkModelInputModalitiesByName,
ClaudeAgentSdkMessageMetadataByModality
> {
name = 'claude-agent-sdk' as const
models = CLAUDE_AGENT_SDK_MODELS

declare _modelProviderOptionsByName: ClaudeAgentSdkChatModelProviderOptionsByName
declare _modelInputModalitiesByName: ClaudeAgentSdkModelInputModalitiesByName
declare _messageMetadataByModality: ClaudeAgentSdkMessageMetadataByModality

private defaultModel?: string

constructor(config?: ClaudeAgentSdkConfig) {
super({})
this.defaultModel = config?.model
}

async *chatStream(
options: ChatOptions<string, ClaudeAgentSdkProviderOptions>,
): AsyncIterable<StreamChunk> {
const timestamp = Date.now()
const model = options.model || this.defaultModel || 'sonnet'

try {
// Build the request parameters
const requestParams = this.mapOptionsToSdk(options)

// Extract built-in tools and warn about custom tools
const { builtinToolNames, customToolNames } = this.extractBuiltinTools(options.tools)

// Warn about custom tools - not supported due to SDK Zod 4 incompatibility
// See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/38
if (customToolNames.length > 0) {
console.warn(
'[Claude Agent SDK Adapter] Custom tools are not supported due to Zod 4 incompatibility in the SDK. ' +
'See: https://github.com/anthropics/claude-agent-sdk-typescript/issues/38 ' +
'Use the Anthropic adapter (@tanstack/ai-anthropic) for custom tools. ' +
`Ignored tools: ${customToolNames.join(', ')}`,
)
}

// Convert messages to a prompt string for the SDK
const prompt = this.buildPromptString(options.messages)

// Create abort controller if signal provided
const abortController = new AbortController()
if (options.request?.signal) {
options.request.signal.addEventListener(
'abort',
() => {
abortController.abort()
},
{ once: true },
)
}

// Build SDK query options
const sdkOptions: SdkOptions = {
model,
maxTurns: requestParams.maxTurns ?? 3,
// Enable built-in tools by name
tools: builtinToolNames.length > 0 ? builtinToolNames : [],
// Explicitly allow built-in tools
...(builtinToolNames.length > 0 && { allowedTools: builtinToolNames }),
abortController,
// Include partial messages for streaming
includePartialMessages: true,
// Extended thinking support
...(requestParams.thinking?.type === 'enabled' &&
requestParams.thinking.budget_tokens && {
maxThinkingTokens: requestParams.thinking.budget_tokens,
}),
}

// Track accumulated content
let accumulatedContent = ''
let accumulatedThinking = ''
const toolCallsMap = new Map<string, { id: string; name: string; input: string }>()

// Stream responses from the SDK
const stream = query({ prompt, options: sdkOptions })

for await (const message of stream) {
// Handle abort signal
if (options.request?.signal?.aborted) {
yield {
type: 'error',
id: this.generateId(),
model,
timestamp,
error: {
message: 'Request was aborted',
code: 'aborted',
},
}
return
}

// Handle partial streaming messages
if (message.type === 'stream_event') {
const partialMessage = message as unknown as SDKPartialAssistantMessage
const event = partialMessage.event

if (event.type === 'content_block_delta') {
if (event.delta.type === 'text_delta') {
const delta = event.delta.text
accumulatedContent += delta
yield {
type: 'content',
id: this.generateId(),
model,
timestamp,
delta,
content: accumulatedContent,
role: 'assistant',
}
} else if (event.delta.type === 'thinking_delta') {
const delta = event.delta.thinking
accumulatedThinking += delta
yield {
type: 'thinking',
id: this.generateId(),
model,
timestamp,
delta,
content: accumulatedThinking,
}
} else if (event.delta.type === 'input_json_delta') {
// Tool input streaming - handled when content block stops
}
} else if (event.type === 'content_block_start') {
if (event.content_block.type === 'tool_use') {
const toolId = event.content_block.id
toolCallsMap.set(toolId, {
id: toolId,
name: event.content_block.name,
input: '',
})
}
}
}

// Handle complete assistant messages
if (message.type === 'assistant') {
const assistantMessage = message as unknown as SDKAssistantMessage
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- SDK types may be looser than what TypeScript infers
const messageContent = assistantMessage.message?.content

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime guard for SDK response
if (messageContent) {
for (const block of messageContent) {
if (block.type === 'text') {
// Full text content
if (!accumulatedContent) {
accumulatedContent = block.text
yield {
type: 'content',
id: this.generateId(),
model,
timestamp,
delta: block.text,
content: accumulatedContent,
role: 'assistant',
}
}
} else if (block.type === 'thinking') {
// Thinking content
if (!accumulatedThinking) {
accumulatedThinking = block.thinking
yield {
type: 'thinking',
id: this.generateId(),
model,
timestamp,
delta: block.thinking,
content: accumulatedThinking,
}
}
} else if (block.type === 'tool_use') {
const toolId = block.id
const inputStr = JSON.stringify(block.input || {})

if (!toolCallsMap.has(toolId)) {
toolCallsMap.set(toolId, {
id: toolId,
name: block.name,
input: inputStr,
})
}

yield {
type: 'tool_call',
id: this.generateId(),
model,
timestamp,
toolCall: {
id: toolId,
type: 'function',
function: {
name: block.name,
arguments: inputStr,
},
},
index: toolCallsMap.size - 1,
}
}
}
}

// Handle assistant message errors
if (assistantMessage.error) {
yield {
type: 'error',
id: this.generateId(),
model,
timestamp,
error: {
message: `API error: ${assistantMessage.error}`,
code: assistantMessage.error,
},
}
}
}

// Handle result messages
if (message.type === 'result') {
const resultMessage = message as unknown as SDKResultMessage

if (resultMessage.subtype !== 'success') {
yield {
type: 'error',
id: this.generateId(),
model,
timestamp,
error: {
message: 'errors' in resultMessage
? (resultMessage.errors as Array<string> | undefined)?.join(', ') || 'Unknown error occurred'
: 'Unknown error occurred',
code: resultMessage.subtype,
},
}
}

const usage = resultMessage.usage as { input_tokens?: number; output_tokens?: number } | undefined
yield {
type: 'done',
id: this.generateId(),
model,
timestamp,
finishReason: toolCallsMap.size > 0 ? 'tool_calls' : 'stop',
usage: {
promptTokens: usage?.input_tokens || 0,
completionTokens: usage?.output_tokens || 0,
totalTokens:
(usage?.input_tokens || 0) +
(usage?.output_tokens || 0),
},
}
}
}
} catch (error: unknown) {
const err = error as Error & { status?: number; code?: string }
console.error('[Claude Agent SDK Adapter] Error in chatStream:', {
message: err.message,
status: err.status,
code: err.code,
error: err,
stack: err.stack,
})

// Map error to appropriate code
const errorCode = this.mapErrorToCode(err)

yield {
type: 'error',
id: this.generateId(),
model,
timestamp,
error: {
message: err.message || 'Unknown error occurred',
code: errorCode,
},
}
}
}

async summarize(options: SummarizationOptions): Promise<SummarizationResult> {
const systemPrompt = this.buildSummarizationPrompt(options)

const chunks: Array<StreamChunk> = []
for await (const chunk of this.chatStream({
model: options.model,
messages: [{ role: 'user', content: options.text }],
systemPrompts: [systemPrompt],
options: { maxTokens: options.maxLength || 500 },
})) {
chunks.push(chunk)
}

// Extract content from chunks
let content = ''
let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 }

for (const chunk of chunks) {
if (chunk.type === 'content') {
content = chunk.content
} else if (chunk.type === 'done' && chunk.usage) {
usage = chunk.usage
}
}

return {
id: this.generateId(),
model: options.model,
summary: content,
usage,
}
}

createEmbeddings(_options: EmbeddingOptions): Promise<EmbeddingResult> {
throw new Error(
'Embeddings are not supported by Claude Agent SDK. Consider using OpenAI or another provider for embeddings.',
)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Refactor to tree-shakeable adapter architecture per coding guidelines.

This monolithic adapter violates the tree-shakeable architecture pattern specified in the coding guidelines. The class bundles chatStream, summarize, and createEmbeddings into a single export, preventing users from importing only the capabilities they need.

Based on learnings, split this into separate adapter files:

  • src/adapters/text.ts - Export text adapter with chatStream
  • src/adapters/summarize.ts - Export summarize adapter with summarize method

Update package.json to expose subpath exports like @tanstack/ai-claude-agent-sdk/adapters for tree-shakeable imports.

Based on learnings, this pattern is critical for minimizing bundle size across all TanStack AI provider packages.

@johann-taberlet johann-taberlet force-pushed the 001-claude-agent-sdk-adapter branch from 9acbc99 to 08fc91f Compare December 16, 2025 14:38
Copy link

@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

🧹 Nitpick comments (3)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1)

578-578: Type assertion may be fragile for custom tools.

Line 578 uses a type assertion (tool as Tool) when extracting the name from custom tools. This assumes the tool object has a name property, but there's no runtime validation.

Consider adding a type guard or runtime check:

       } else {
-        customToolNames.push((tool as Tool).name)
+        if ('name' in tool && typeof tool.name === 'string') {
+          customToolNames.push(tool.name)
+        }
       }
packages/typescript/ai-claude-agent-sdk/src/message-types.ts (2)

35-45: Clarify mediaType requirement for base64 image sources.

The comment at lines 37-39 states that mediaType is "Required when using base64 source type," but the field is defined as optional (mediaType?: ...). This creates ambiguity.

If mediaType is truly required for base64 sources, consider either:

  1. Making it a required field and handling the requirement at the validation layer
  2. Updating the comment to reflect that it's optional but recommended
  3. Using discriminated unions to enforce the requirement at the type level

Based on learnings, type-safe per-model configuration should ensure compile-time safety.


55-69: Clarify mediaType requirement for document content.

Similar to the image metadata, the comment at lines 57-59 states that mediaType is "Required for document content," but it's defined as optional. This creates the same ambiguity as with images.

Consider aligning the type definition with the documentation, or vice versa, to ensure clarity for developers using this API.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba501a5 and 9acbc99.

📒 Files selected for processing (3)
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from /adapters subpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with toolDefinition() and Zod schema inference
Implement isomorphic tool system using toolDefinition() with .server() and .client() implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for function and variable names throughout the codebase

Files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
packages/typescript/*/src/model-meta.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Files:

  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/model-meta.ts : Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
🧬 Code graph analysis (2)
packages/typescript/ai-claude-agent-sdk/src/message-types.ts (1)
packages/typescript/ai-claude-agent-sdk/src/index.ts (7)
  • ClaudeAgentSdkImageMediaType (40-40)
  • ClaudeAgentSdkTextMetadata (35-35)
  • ClaudeAgentSdkImageMetadata (36-36)
  • ClaudeAgentSdkDocumentMetadata (37-37)
  • ClaudeAgentSdkAudioMetadata (38-38)
  • ClaudeAgentSdkVideoMetadata (39-39)
  • ClaudeAgentSdkMessageMetadataByModality (42-42)
packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1)
packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (1)
  • ClaudeAgentSdkProviderOptions (35-64)
🔇 Additional comments (1)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1)

456-490: The convertContentPartToText method correctly converts multimodal content to text placeholders. The Claude Agent SDK's query() function accepts only string prompts or AsyncIterable dictionaries, and does not support passing multimodal content directly. This conversion approach is necessary for SDK compatibility, not a limitation of the adapter.

id: 'haiku',
context_window: 200_000,
max_output_tokens: 64_000,
knowledge_cutoff: '2025-01',
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Update knowledge_cutoff values to match official Claude 4.5 model specifications.

Sonnet 4.5 has a January 2025 reliable knowledge cutoff, Haiku 4.5 is February, and Opus 4.5 is March. The current code sets all three models to '2025-01', which is incorrect for Haiku and Opus. Update:

  • Line 53 (Haiku): change to '2025-02'
  • Line 69 (Opus): change to '2025-03'

Line 37 (Sonnet) is correct.

🤖 Prompt for AI Agents
In packages/typescript/ai-claude-agent-sdk/src/model-meta.ts around lines 37–69,
the knowledge_cutoff values for Haiku 4.5 and Opus 4.5 are incorrect (both set
to '2025-01'); update the Haiku entry at line 53 to '2025-02' and the Opus entry
at line 69 to '2025-03' while leaving Sonnet at line 37 unchanged so each model
matches the official Claude 4.5 cutoff dates.

- Haiku: knowledge_cutoff Feb 2025
- Sonnet: knowledge_cutoff Jan 2025 (was correct)
- Opus: pricing $5/$25 (not $15/$75), knowledge_cutoff May 2025

Based on official Anthropic documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@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 (5)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (3)

85-93: Consider tree-shakeable adapter architecture.

Per coding guidelines, provider implementations should export specialized adapters (text, embedding, summarize) as separate imports from an /adapters subpath rather than bundling all capabilities in a monolithic adapter. This enables tree-shaking for consumers who only need specific capabilities.

Current structure bundles chatStream, summarize, and createEmbeddings together. Consider splitting into:

  • src/adapters/text.ts for chatStream
  • src/adapters/summarize.ts for summarize

Based on learnings from coding guidelines.


131-133: Add validation for empty prompt before calling SDK.

If messages contains no user messages, buildPromptString returns an empty string (see line 603). Passing an empty prompt to the SDK's query function may cause unexpected behavior or API errors.

       // Convert messages to a prompt string for the SDK
       const prompt = this.buildPromptString(options.messages)
+
+      if (!prompt) {
+        throw new Error('No user message found. At least one user message is required.')
+      }

       // Create abort controller if signal provided

269-296: Fix tool call index calculation.

The index at line 294 uses toolCallsMap.size - 1, which is incorrect when the tool was already added during content_block_start (lines 222-229). Since the tool won't be re-added due to the check at line 273, the map size won't change, causing the index to reflect total tools rather than this specific tool's position.

Track indices when inserting:

-  const toolCallsMap = new Map<string, { id: string; name: string; input: string }>()
+  const toolCallsMap = new Map<string, { id: string; name: string; input: string; index: number }>()
       } else if (event.type === 'content_block_start') {
         if (event.content_block.type === 'tool_use') {
           const toolId = event.content_block.id
           toolCallsMap.set(toolId, {
             id: toolId,
             name: event.content_block.name,
             input: '',
+            index: toolCallsMap.size,
           })
         }
       }
         } else if (block.type === 'tool_use') {
           const toolId = block.id
           const inputStr = JSON.stringify(block.input || {})

           if (!toolCallsMap.has(toolId)) {
             toolCallsMap.set(toolId, {
               id: toolId,
               name: block.name,
               input: inputStr,
+              index: toolCallsMap.size,
             })
           }

+          const toolCall = toolCallsMap.get(toolId)!
           yield {
             type: 'tool_call',
             ...
-            index: toolCallsMap.size - 1,
+            index: toolCall.index,
           }
packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1)

32-78: Verify knowledge_cutoff values for Haiku and Opus models.

Per past review, knowledge_cutoff dates may be incorrect:

  • Line 37 (Haiku): Currently '2025-01', expected '2025-02' for Haiku 4.5
  • Line 69 (Opus): Currently '2025-01', expected '2025-03' for Opus 4.5
  • Line 53 (Sonnet): '2025-01' appears correct for Sonnet 4.5

Verify against official Claude model specifications and update accordingly.

Claude 4.5 Haiku Sonnet Opus knowledge cutoff dates
packages/typescript/ai-claude-agent-sdk/package.json (1)

22-32: Fix export paths to align with monorepo conventions.

The types and import fields are misaligned. All other adapter packages in the monorepo use flat paths for both (e.g., ./dist/esm/index.d.ts and ./dist/esm/index.js), but this package uses flat types with nested imports pointing to source files. Update both "." and "./tools" export conditions to use consistent flat paths matching the build output structure:

"exports": {
  ".": {
    "types": "./dist/esm/index.d.ts",
    "import": "./dist/esm/index.js"
  },
  "./tools": {
    "types": "./dist/esm/tools/index.d.ts",
    "import": "./dist/esm/tools/index.js"
  }
}
🧹 Nitpick comments (1)
packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1)

574-580: Verify type safety of Tool cast.

At line 578, (tool as Tool).name assumes the non-builtin tool conforms to Tool interface. If the tools array receives an unexpected type, this could cause runtime issues.

Consider adding a type guard or null check:

       } else {
-        customToolNames.push((tool as Tool).name)
+        const customTool = tool as Tool
+        if (customTool.name) {
+          customToolNames.push(customTool.name)
+        }
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9acbc99 and 08fc91f.

📒 Files selected for processing (4)
  • packages/typescript/ai-claude-agent-sdk/package.json (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts (1 hunks)
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/typescript/ai-claude-agent-sdk/src/message-types.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from /adapters subpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with toolDefinition() and Zod schema inference
Implement isomorphic tool system using toolDefinition() with .server() and .client() implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for function and variable names throughout the codebase

Files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
packages/typescript/*/src/model-meta.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Files:

  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/*/package.json : Use `workspace:*` protocol for internal package dependencies in package.json (e.g., `"tanstack/ai": "workspace:*"`)

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement isomorphic tool system using `toolDefinition()` with `.server()` and `.client()` implementations for dual-environment execution

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with `toolDefinition()` and Zod schema inference

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/package.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/model-meta.ts : Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety

Applied to files:

  • packages/typescript/ai-claude-agent-sdk/src/model-meta.ts
🧬 Code graph analysis (1)
packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1)
packages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (1)
  • ClaudeAgentSdkProviderOptions (35-64)
🔇 Additional comments (1)
packages/typescript/ai-claude-agent-sdk/src/model-meta.ts (1)

84-113: LGTM on type exports.

The model metadata structure correctly implements per-model type safety with ClaudeAgentSdkChatModelProviderOptionsByName and ClaudeAgentSdkModelInputModalitiesByName mappings. The use of as const satisfies ModelMeta ensures compile-time safety while preserving literal types.

Comment on lines +48 to +50
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.1.69",
"@tanstack/ai": "workspace:*"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

The @anthropic-ai/claude-agent-sdk dependency can use a newer patch version.

The latest version is 0.1.70 (published 13 hours ago), while the package.json specifies ^0.1.69. The caret constraint already permits 0.1.70, so ensure your lockfile is updated to pull this version during the next npm install if you want the latest patch.

🤖 Prompt for AI Agents
In packages/typescript/ai-claude-agent-sdk/package.json around lines 48 to 50,
the dependency entry for @anthropic-ai/claude-agent-sdk is ^0.1.69 which already
permits 0.1.70 but the lockfile likely pins the older patch; run a fresh install
(npm install or yarn install depending on repo) to update the lockfile so the
newer 0.1.70 patch is resolved, commit the updated package-lock.json or
yarn.lock, and ensure CI installs from the updated lockfile.

- Add validation for empty prompt (requires at least one user message)
- Fix tool call index bug: store index when tool is first added to map
  instead of using map.size - 1 which gives wrong index for multiple tools

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant