-
-
Notifications
You must be signed in to change notification settings - Fork 82
feat: add Claude Agent SDK adapter for TanStack AI #156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add Claude Agent SDK adapter for TanStack AI #156
Conversation
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>
|
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 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. 📒 Files selected for processing (2)
WalkthroughAdds a new Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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:*inpeerDependenciesmay 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.metadatabroadly. 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
createMockStreamhelper usesany[]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
mockQuerywas called withprompt: 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 testpackages/typescript/ai-claude-agent-sdk/src/text/text-provider-options.ts (1)
35-64: Consider camelCase for consistency with coding guidelines.The properties
stop_sequencesandtop_kuse snake_case, whilemaxTurnsuses 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?: numberpackages/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.errorcall 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 ofmax_tokensmay be unexpected.When
thinking.budget_tokensexceedsmaxTokens, the code silently adjustsmax_tokenstobudget_tokens + 1. This could be surprising to users who explicitly set a lowermaxTokens. 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
createClaudeAgentSdkandclaudeAgentSdkhave 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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/adapterssubpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions withtoolDefinition()and Zod schema inference
Implement isomorphic tool system usingtoolDefinition()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.tspackages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.tspackages/typescript/ai-claude-agent-sdk/src/builtin-tools.tspackages/typescript/ai-claude-agent-sdk/vite.config.tspackages/typescript/ai-claude-agent-sdk/src/tools/index.tspackages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.tspackages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.tspackages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.tspackages/typescript/ai-claude-agent-sdk/tests/adapter.test.tspackages/typescript/ai-claude-agent-sdk/src/model-meta.tspackages/typescript/ai-claude-agent-sdk/src/message-types.tspackages/typescript/ai-claude-agent-sdk/src/index.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.tspackages/typescript/ai-claude-agent-sdk/src/builtin-tools.tspackages/typescript/ai-claude-agent-sdk/vite.config.tspackages/typescript/ai-claude-agent-sdk/src/tools/index.tspackages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.tspackages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.tspackages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.tspackages/typescript/ai-claude-agent-sdk/tests/adapter.test.tspackages/typescript/ai-claude-agent-sdk/src/model-meta.tspackages/typescript/ai-claude-agent-sdk/src/message-types.tspackages/typescript/ai-claude-agent-sdk/src/index.tspackages/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.tsnaming convention
Files:
packages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.tspackages/typescript/ai-claude-agent-sdk/tests/adapter.test.tspackages/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.jsonpackages/typescript/ai-claude-agent-sdk/vite.config.tspackages/typescript/ai-claude-agent-sdk/src/tools/index.tspackages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.tspackages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts.changeset/claude-agent-sdk-adapter.mdpackages/typescript/ai-claude-agent-sdk/tests/adapter.test.tspackages/typescript/ai-claude-agent-sdk/tsconfig.jsonpackages/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.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/src/model-meta.tspackages/typescript/ai-claude-agent-sdk/src/message-types.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/src/tools/tool-converter.tspackages/typescript/ai-claude-agent-sdk/src/tools/index.tspackages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.tspackages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.tspackages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.ts.changeset/claude-agent-sdk-adapter.mdpackages/typescript/ai-claude-agent-sdk/tests/adapter.test.tspackages/typescript/ai-claude-agent-sdk/src/model-meta.tspackages/typescript/ai-claude-agent-sdk/src/index.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/src/builtin-tools.tspackages/typescript/ai-claude-agent-sdk/src/tools/index.tspackages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/src/tools/custom-tool.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/tests/tool-converter.test.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.tspackages/typescript/ai-claude-agent-sdk/tests/adapter.test.tspackages/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 = CustomToolprovides extensibility for future tool types while maintaining a clear public API. The separate/toolssubpath 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.69allows patch updates up to0.1.x, so npm will install 0.1.70 or newer 0.1 versions. Consider updating the dependency to^0.1.70to 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.tsandtools/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
convertToolsToProviderFormatandconvertCustomToolToAdapterFormat. 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
ClaudeAgentSdkChatModelProviderOptionsByNameandClaudeAgentSdkModelInputModalitiesByNametype maps. Theas const satisfies ModelMetapattern 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
isBuiltinToolfunction 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 becausebuiltinToolsis defined asconstand 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
builtinToolsexport 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
ClaudeAgentSdkConfiginterface 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 thatbudget_tokensmust be less thanmax_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
chatStreamwith 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
nevercheck, 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
ClaudeAgentSdkMessageMetadataByModalityinterface provides a proper type map for multimodal message construction.
| /** | ||
| * @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' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
| "include": ["vite.config.ts", "./src"], | ||
| "exclude": ["node_modules", "dist", "**/*.config.ts"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
- 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>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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
maxTokenstothinkingBudget + 1when the thinking budget exceeds the default. This silently overrides the user'smaxTokenssetting, 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.maxLengthdirectly tomaxTokens. While this works as a rough approximation,maxLengthmight 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
createClaudeAgentSdkandclaudeAgentSdkhave identical implementations. While offering both naming patterns can be convenient for users, this creates maintenance overhead.Consider either:
- Having one call the other to reduce duplication
- Removing one if there's no semantic difference
- 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
📒 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/adapterssubpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions withtoolDefinition()and Zod schema inference
Implement isomorphic tool system usingtoolDefinition()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
convertContentPartToTexthelper 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: Accumulateinput_json_deltaevents 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_deltaevents (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_deltaevents into the tool input and yield partialtool_callchunks as data arrives, then finalize oncontent_block_stop. This provides real-time visibility into tool invocations and aligns with the StreamProcessor pattern for chunked response handling.
| 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.', | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 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 withchatStreamsrc/adapters/summarize.ts- Export summarize adapter withsummarizemethod
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.
9acbc99 to
08fc91f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 anameproperty, 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
mediaTypeis "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:
- Making it a required field and handling the requirement at the validation layer
- Updating the comment to reflect that it's optional but recommended
- 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
mediaTypeis "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
📒 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/adapterssubpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions withtoolDefinition()and Zod schema inference
Implement isomorphic tool system usingtoolDefinition()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.tspackages/typescript/ai-claude-agent-sdk/src/message-types.tspackages/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.tspackages/typescript/ai-claude-agent-sdk/src/message-types.tspackages/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.tspackages/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.tspackages/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: TheconvertContentPartToTextmethod 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', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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
/adapterssubpath 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, andcreateEmbeddingstogether. Consider splitting into:
src/adapters/text.tsforchatStreamsrc/adapters/summarize.tsforsummarizeBased on learnings from coding guidelines.
131-133: Add validation for empty prompt before calling SDK.If
messagescontains no user messages,buildPromptStringreturns an empty string (see line 603). Passing an empty prompt to the SDK'squeryfunction 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 duringcontent_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.5Verify against official Claude model specifications and update accordingly.
Claude 4.5 Haiku Sonnet Opus knowledge cutoff datespackages/typescript/ai-claude-agent-sdk/package.json (1)
22-32: Fix export paths to align with monorepo conventions.The
typesandimportfields are misaligned. All other adapter packages in the monorepo use flat paths for both (e.g.,./dist/esm/index.d.tsand./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).nameassumes the non-builtin tool conforms toToolinterface. 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
📒 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/adapterssubpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions withtoolDefinition()and Zod schema inference
Implement isomorphic tool system usingtoolDefinition()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.tspackages/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.tspackages/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.jsonpackages/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.jsonpackages/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.jsonpackages/typescript/ai-claude-agent-sdk/src/claude-agent-sdk-adapter.tspackages/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
ClaudeAgentSdkChatModelProviderOptionsByNameandClaudeAgentSdkModelInputModalitiesByNamemappings. The use ofas const satisfies ModelMetaensures compile-time safety while preserving literal types.
| "dependencies": { | ||
| "@anthropic-ai/claude-agent-sdk": "^0.1.69", | ||
| "@tanstack/ai": "workspace:*" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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>
🎯 Changes
Adds a new adapter package
@tanstack/ai-claude-agent-sdkthat integrates the Claude Agent SDK with TanStack AI.Features
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):
toolDefinition()from TanStack AIWorkaround: 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
✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.