Skip to content

feat: Provider tools on /tools subpath with per-model type gating#466

Open
AlemTuzlak wants to merge 44 commits intomainfrom
worktree-cheeky-mapping-harp
Open

feat: Provider tools on /tools subpath with per-model type gating#466
AlemTuzlak wants to merge 44 commits intomainfrom
worktree-cheeky-mapping-harp

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented Apr 17, 2026

Summary

  • Every adapter package (ai-anthropic, ai-openai, ai-gemini, ai-openrouter, ai-grok, ai-groq) now exposes provider-specific tool factories (web search, code execution, computer use, etc.) on a dedicated tree-shakeable /tools subpath.
  • New ProviderTool<TProvider, TKind> phantom-branded type in @tanstack/ai gates tool usage at the type level — passing a provider tool to a model that doesn't support it produces a compile-time error on the tools: [...] array. User-defined toolDefinition() tools stay unbranded and always assignable.
  • One breaking export change in @tanstack/ai-openrouter: createWebSearchTool removed from package root, renamed to webSearchTool, moved to /tools subpath. Documented in Migration Guide §6.

What's new

Core (@tanstack/ai, minor):

  • ProviderTool<TProvider, TKind> phantom-branded subtype of Tool
  • toolCapabilities channel on TextAdapter['~types']
  • TextActivityOptions['tools'] gated with a discriminated union (Tool & { '~toolKind'?: never }) | ProviderTool<string, TAdapter['~types']['toolCapabilities'][number]>

Adapters (minor for openai/anthropic/gemini/openrouter, patch for grok/groq):

  • New /tools subpath on every adapter package.json
  • Branded factory return types (AnthropicWebSearchTool, OpenAICodeInterpreterTool, GeminiGoogleSearchTool, etc.)
  • <Provider>ChatModelToolCapabilitiesByName type maps exported from each adapter root
  • Gemini: supports.capabilities split into capabilities + new tools field
  • OpenAI: supports.tools union expanded with web_search_preview, local_shell, shell, apply_patch
  • Anthropic: new supports.tools array added per model (haiku-3 web_search only, haiku-3-5 web tools only, all others full superset)
  • OpenRouter: mapped-type capabilities ({ [K in OpenRouterChatModel]: readonly ['web_search'] }) — no per-model population needed
  • customTool factories in anthropic/openai return plain Tool (universal, not branded — every model accepts them)

Sync script (scripts/sync-provider-models.ts):

  • referenceSupportsBody templates updated for anthropic/openai/gemini/grok so newly-synced models land with correct tools fields out of the box

Tests:

  • Core type tests (5) + per-adapter type tests (3-5 each) using expectTypeOf + @ts-expect-error pattern
  • Runtime smoke tests for anthropic factory shapes + converter
  • Narrow tsconfig include for new test files to avoid surfacing unrelated pre-existing errors

Docs:

  • New concept page docs/tools/provider-tools.md with provider × tools matrix
  • Provider Tools H2 section on every adapter page (docs/adapters/*.md)
  • Migration Guide §6 for OpenRouter rename
  • Callout from docs/tools/tools.md → provider-tools page
  • Nav entry in docs/config.json

Test plan

  • pnpm test:types passes across all 30 projects
  • pnpm build produces dist/esm/tools/index.{js,d.ts} for every adapter with a /tools subpath
  • pnpm test:lib passes (full test suite)
  • pnpm test:eslint passes (0 errors)
  • pnpm test:build (publint) passes
  • pnpm test:sherif passes (workspace consistency)
  • pnpm test:knip passes (no unused deps introduced)
  • Verify @ts-expect-error directives actually fire: remove one per adapter, confirm TypeScript reports a real TS2322 on the line
  • Before next scheduled sync job runs, confirm the updated referenceSupportsBody templates produce correctly-shaped new model entries

Known follow-ups (out of scope)

  • Capability map drift: hand-maintained <Provider>ChatModelToolCapabilitiesByName maps in anthropic/openai/gemini/grok/groq need manual updates when the sync script adds new models. Migrating them to mapped types (like OpenRouter already does) would eliminate the drift surface. Small follow-up PR.
  • Anthropic textEditorTool name hardcode: name: 'str_replace_editor' is the 2025-01-24 variant; newer SDK variants (20250429/20250728) use 'str_replace_based_edit_tool'. Tool-result dispatcher matches by name — users on newer variants silently lose tool results. One-line fix (name: config.name) deferred to a focused PR.
  • OpenRouter wire format check: factory emits { type: 'web_search', web_search: {...} } (legacy plugin shape) but the SDK types ChatRequest.tools as a flat ChatWebSearchShorthand. Shape predates this PR; worth a one-shot gateway test to confirm acceptance.
  • Full Playwright E2E: a proper testing/e2e/ feature scenario for anthropic webSearchTool is deferred. Runtime smoke tests in ai-anthropic/tests/provider-tools-smoke.test.ts cover the critical factory → converter → SDK shape path.

Summary by CodeRabbit

Release Notes

  • New Features

    • Provider-native tools now accessible via /tools subpath (web search, code execution, computer use, etc.) across Anthropic, Gemini, OpenAI, and OpenRouter adapters.
    • TypeScript compile-time validation ensures provider tools only work with compatible models.
  • Breaking Changes

    • OpenRouter: createWebSearchTool removed from package root; import webSearchTool from @tanstack/ai-openrouter/tools.
  • Documentation

    • Added comprehensive provider tools documentation with examples and model compatibility matrices across adapters.

Separates tool-type entries (code_execution, file_search, search_grounding,
grounding_with_gmaps, url_context, image_generation) from general capability
flags in ModelMeta.supports. Adds a new tools field to the supports shape,
renames grounding_with_gmaps → google_maps and search_grounding → google_search,
drops image_generation (no tool factory yet), and introduces
GeminiChatModelToolCapabilitiesByName. Threads a fifth TToolCapabilities
generic through GeminiTextAdapter and exports the new type map from the root.
…hTool on /tools subpath

Renames createWebSearchTool → webSearchTool, brands its return type as
OpenRouterWebSearchTool (ProviderTool<'openrouter', 'web_search'>), moves
web_search exports to the new ./tools subpath, adds
OpenRouterChatModelToolCapabilitiesByName mapped type (all chat models
support web_search via the gateway), threads TToolCapabilities through the
text adapter, and adds per-model type tests.
…d toolCapabilities

- Add tools?: ReadonlyArray<never> to ModelMeta.supports interface in both packages
- Add tools: [] as const to every chat model constant
- Export GrokChatModelToolCapabilitiesByName / GroqChatModelToolCapabilitiesByName type maps
- Add 5th TToolCapabilities generic to GrokTextAdapter / GroqTextAdapter via ResolveToolCapabilities
- Add ./tools subpath to package.json exports and vite.config.ts entry for both packages
- Re-export new ToolCapabilitiesByName types from root index.ts in both packages
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

This PR introduces a provider-native tool system across TanStack AI adapters. It adds /tools subpaths to adapter packages, introduces branded ProviderTool<TProvider, TKind> types for compile-time tool-capability gating, restructures tool configuration types with backward-compatible deprecated aliases, updates model metadata with supports.tools arrays, and provides comprehensive documentation and migration guidance.

Changes

Cohort / File(s) Summary
Changesets
.changeset/provider-tools-*
Added changeset entries for all adapter packages (ai-anthropic, ai-core, ai-gemini, ai-grok, ai-groq, ai-openai, ai-openrouter) documenting new /tools subpaths, branded tool factory return types, tool-capability gating, and (for OpenRouter) a breaking change removing createWebSearchTool from package root.
Core Type System
packages/typescript/ai/src/tools/provider-tool.ts, packages/typescript/ai/src/activities/chat/adapter.ts, packages/typescript/ai/src/activities/chat/index.ts, packages/typescript/ai/src/index.ts
Introduced ProviderTool<TProvider, TKind> interface with phantom-branded ~provider and ~toolKind fields; extended TextAdapter and BaseTextAdapter with TToolCapabilities generic parameter; updated TextActivityOptions['tools'] to accept user tools and provider-specific tools gated by model capabilities.
Anthropic Adapter - Tools
packages/typescript/ai-anthropic/src/tools/*.ts, packages/typescript/ai-anthropic/src/tools/index.ts
Restructured all tool config types (web-search, web-fetch, code-execution, computer-use, bash, text-editor, memory, custom) from *Tool to *ToolConfig, marked old names as deprecated aliases, updated factory functions to return branded AnthropicXxxTool types; added barrel exports in tools/index.ts.
Anthropic Adapter - Integration
packages/typescript/ai-anthropic/src/adapters/text.ts, packages/typescript/ai-anthropic/src/index.ts, packages/typescript/ai-anthropic/src/model-meta.ts, packages/typescript/ai-anthropic/package.json, packages/typescript/ai-anthropic/vite.config.ts
Extended adapter with tool capability type resolution; added supports.tools metadata to all Claude models; exported AnthropicChatModelToolCapabilitiesByName type; added /tools export in package.json; added tools entry point to vite.config.
Gemini Adapter - Tools
packages/typescript/ai-gemini/src/tools/*.ts, packages/typescript/ai-gemini/src/tools/index.ts
Renamed tool config types across all tools (code-execution, computer-use, file-search, google-maps, google-search, google-search-retrieval, url-context) to *ToolConfig with deprecated aliases; updated factories to return branded GeminiXxxTool types; added barrel exports.
Gemini Adapter - Integration
packages/typescript/ai-gemini/src/adapters/text.ts, packages/typescript/ai-gemini/src/index.ts, packages/typescript/ai-gemini/src/model-meta.ts, packages/typescript/ai-gemini/package.json, packages/typescript/ai-gemini/vite.config.ts
Extended adapter with tool capability type resolution; restructured model metadata moving tool-related capabilities from supports.capabilities to new supports.tools arrays; exported GeminiChatModelToolCapabilitiesByName; added /tools export and entry point.
Grok & Groq Adapters - Integration
packages/typescript/ai-grok/..., packages/typescript/ai-groq/... (adapters, index, model-meta, package.json, vite.config)
Added tool capability type resolution to both adapters; set empty supports.tools: [] for all models (no provider-specific tools); exported *ChatModelToolCapabilitiesByName types; added /tools exports and entry points.
OpenAI Adapter - Tools
packages/typescript/ai-openai/src/tools/*.ts, packages/typescript/ai-openai/src/tools/index.ts
Restructured all tool types (web-search, web-search-preview, file-search, image-generation, code-interpreter, mcp, computer-use, local-shell, shell, apply-patch, custom) to use *ToolConfig with deprecated aliases; updated factories to return branded OpenAIXxxTool types; consolidated exports with explicit named re-exports.
OpenAI Adapter - Integration
packages/typescript/ai-openai/src/adapters/text.ts, packages/typescript/ai-openai/src/index.ts, packages/typescript/ai-openai/src/model-meta.ts, packages/typescript/ai-openai/package.json, packages/typescript/ai-openai/vite.config.ts
Extended adapter with tool capability resolution; expanded model metadata supports.tools to include web_search_preview, local_shell, shell, apply_patch; exported OpenAIChatModelToolCapabilitiesByName; added /tools export and entry point.
OpenRouter Adapter - Tools
packages/typescript/ai-openrouter/src/tools/*.ts, packages/typescript/ai-openrouter/src/tools/index.ts
Renamed WebSearchTool to WebSearchToolConfig; added deprecated alias; created OpenRouterWebSearchTool branded type; introduced convertWebSearchToolToAdapterFormat; removed createWebSearchTool export from root (breaking change); updated tool converter to handle web-search tools.
OpenRouter Adapter - Integration
packages/typescript/ai-openrouter/src/adapters/text.ts, packages/typescript/ai-openrouter/src/index.ts, packages/typescript/ai-openrouter/src/model-meta.ts, packages/typescript/ai-openrouter/package.json, packages/typescript/ai-openrouter/vite.config.ts
Added tool capability type resolution; exported OpenRouterChatModelToolCapabilitiesByName; removed root exports of createWebSearchTool and WebSearchTool (breaking); added /tools export and entry point.
Documentation
docs/adapters/*.md, docs/tools/provider-tools.md, docs/tools/tools.md, docs/migration/migration.md, docs/config.json
Added "Provider Tools" sections to adapter docs with tool usage examples and model support details; created new provider-tools guide; added migration section documenting OpenRouter breaking change and new type-level tool gating; updated nav config.
Test Files
packages/typescript/ai*/tests/provider-tools-smoke.test.ts, packages/typescript/ai*/tests/tools-per-model-type-safety.test.ts, packages/typescript/ai/tests/tools-per-model-type-safety.test.ts, packages/typescript/ai/tests/test-utils.ts
Added smoke tests verifying tool factory runtime behavior; added compile-time type-safety tests for per-model tool compatibility; updated mock adapter to include toolCapabilities metadata.
Build & Tooling
packages/typescript/ai*/tsconfig.json, packages/typescript/ai*/vite.config.ts, scripts/sync-provider-models.ts
Updated tsconfig includes to add test files; extended vite entry points to include ./src/tools/index.ts; updated model metadata generation templates to include supports.tools.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Adapter as TextAdapter<br/>(with TToolCapabilities)
    participant ProviderTool as Provider Tool Factory<br/>(e.g., webSearchTool)
    participant TypeChecker as TypeScript Compiler
    
    User->>ProviderTool: Call webSearchTool(config)
    ProviderTool->>ProviderTool: Return AnthropicWebSearchTool<br/>(ProviderTool<'anthropic','web_search'>)
    User->>Adapter: adapter.chat({tools:[webSearchTool(...)]})
    Adapter->>TypeChecker: Check tool compatibility against<br/>TAdapter['~types']['toolCapabilities']
    alt Tool kind in supportedCapabilities
        TypeChecker->>Adapter: ✓ Accept tool
        Adapter->>User: Execute chat with tool
    else Tool kind not supported
        TypeChecker->>User: ✗ Compile error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 Hops excitedly with carrot in paw

New tools emerge from each provider's vault,
Branded types guide where each tool can dance,
Models whisper what they support with style,
TypeScript guards each choice at compile time—
A rabbit's toolkit grows more precise! 🥕

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-cheeky-mapping-harp

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 17, 2026

🚀 Changeset Version Preview

8 package(s) bumped directly, 25 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-anthropic 0.7.3 → 1.0.0 Changeset
@tanstack/ai-gemini 0.8.7 → 1.0.0 Changeset
@tanstack/ai-grok 0.6.5 → 1.0.0 Changeset
@tanstack/ai-groq 0.1.4 → 1.0.0 Changeset
@tanstack/ai-ollama 0.6.5 → 1.0.0 Changeset
@tanstack/ai-openai 0.7.4 → 1.0.0 Changeset
@tanstack/ai-openrouter 0.7.4 → 1.0.0 Changeset
@tanstack/ai-code-mode 0.1.2 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.1.2 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.1.4 → 1.0.0 Dependent
@tanstack/ai-event-client 0.2.2 → 1.0.0 Dependent
@tanstack/ai-fal 0.6.12 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.2 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.2 → 1.0.0 Dependent
@tanstack/ai-preact 0.6.14 → 1.0.0 Dependent
@tanstack/ai-react 0.7.10 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.6.1 → 1.0.0 Dependent
@tanstack/ai-solid 0.6.14 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.6.1 → 1.0.0 Dependent
@tanstack/ai-svelte 0.6.14 → 1.0.0 Dependent
@tanstack/ai-vue 0.6.14 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.10.2 → 0.11.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-client 0.7.9 → 0.7.10 Dependent
@tanstack/ai-code-mode-models-eval 0.0.5 → 0.0.6 Dependent
@tanstack/ai-devtools-core 0.3.19 → 0.3.20 Dependent
@tanstack/ai-isolate-cloudflare 0.1.2 → 0.1.3 Dependent
@tanstack/ai-vue-ui 0.1.25 → 0.1.26 Dependent
@tanstack/preact-ai-devtools 0.1.23 → 0.1.24 Dependent
@tanstack/react-ai-devtools 0.2.23 → 0.2.24 Dependent
@tanstack/solid-ai-devtools 0.2.23 → 0.2.24 Dependent
ts-svelte-chat 0.1.31 → 0.1.32 Dependent
ts-vue-chat 0.1.31 → 0.1.32 Dependent
vanilla-chat 0.0.29 → 0.0.30 Dependent

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Apr 17, 2026

View your CI Pipeline Execution ↗ for commit 0c1bd22

Command Status Duration Result
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 1m 21s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-17 17:37:31 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 17, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@466

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@466

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@466

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@466

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@466

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@466

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@466

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@466

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@466

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@466

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@466

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@466

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@466

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@466

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@466

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@466

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@466

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@466

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@466

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@466

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@466

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@466

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@466

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@466

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@466

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@466

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@466

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@466

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@466

commit: 0c1bd22

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/typescript/ai/tests/type-check.test.ts (1)

11-22: ⚠️ Potential issue | 🟡 Minor

Pass explicit toolCapabilities type parameter to MockAdapter.

The TextAdapter interface has 5 type parameters, but the MockAdapter instantiation (lines 11–22) provides only 4 arguments. The TToolCapabilities parameter relies on its default value (ReadonlyArray<string>) rather than explicitly matching the toolCapabilities: [] as ReadonlyArray<string> type from the mock literal at line 38. Pass the 5th type argument to make the type instantiation explicit and align with the mock object definition:

type MockAdapter = TextAdapter<
  'test-model',
  { validOption: string; anotherOption?: number },
  readonly ['text', 'image'],
  {
    text: unknown
    image: unknown
    audio: unknown
    video: unknown
    document: unknown
  },
  readonly string[]
>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai/tests/type-check.test.ts` around lines 11 - 22, The
MockAdapter type instantiation omits the TToolCapabilities type parameter,
causing it to fall back to the default instead of matching the mock's
toolCapabilities literal; update the MockAdapter alias (the TextAdapter
instantiation) to include the fifth type argument (e.g., readonly string[]) so
the generic list of tool capabilities is explicit and aligns with the mock
object’s toolCapabilities value.
docs/migration/migration.md (1)

13-19: ⚠️ Potential issue | 🟡 Minor

Overview list is out of date — "Provider Tools" isn’t mentioned.

Section 6 was added below but the numbered list at the top still stops at item 5 (Embeddings Removed). Users skimming the overview will miss the OpenRouter breaking rename.

Proposed fix
 1. **Adapter functions split** - Adapters are now split into activity-specific functions for optimal tree-shaking
 2. **Common options flattened** - Options are now flattened in the config instead of nested
 3. **`providerOptions` renamed** - Now called `modelOptions` for clarity
 4. **`toResponseStream` renamed** - Now called `toServerSentEventsStream` for clarity
 5. **Embeddings removed** - Embeddings support has been removed (most vector DB services have built-in support)
+6. **Provider tools moved to `/tools` subpath** - Provider-specific tool factories are now imported from each adapter's `/tools` subpath; `@tanstack/ai-openrouter`'s `createWebSearchTool` is renamed to `webSearchTool`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/migration/migration.md` around lines 13 - 19, The top overview numbered
list is missing the new Section 6 about Provider Tools/OpenRouter; update the
list to include a sixth bullet describing "Provider Tools added — OpenRouter
rename and related breaking changes" (or similar), and ensure the summary
mentions the existing renames: `providerOptions` → `modelOptions` and
`toResponseStream` → `toServerSentEventsStream` and removal of Embeddings so
readers see the OpenRouter/provider-tools note at a glance; edit the list
entries (the overview block containing "Adapter functions split", "Common
options flattened", etc.) to add the sixth item and make sure it references the
new Provider Tools/OpenRouter change so the overview and Section 6 are
consistent.
scripts/sync-provider-models.ts (1)

102-124: ⚠️ Potential issue | 🟠 Major

Template-level tool capability drift for Anthropic/Gemini newly synced models.

Both the Anthropic (line 104) and Gemini (line 124) reference templates hardcode the full tool superset for every newly synced model. The real matrix already has restricted Anthropic families (claude-3-haiku['web_search'], claude-3-5-haiku['web_search', 'web_fetch']) and narrower Gemini variants (Lite, image/video), per docs/tools/provider-tools.md and model-meta.ts. Any future haiku-class or Lite-class model picked up by this script will be minted with an over-broad supports.tools, which feeds AnthropicChatModelToolCapabilitiesByName / Gemini's capability map and silently permits unsupported tools at compile time — defeating the type gate this PR is introducing.

This matches the "capability map drift" follow-up called out in the PR description, but it's worth leaving a defensive marker so the next sync run doesn't quietly regress type safety. A minimum mitigation is to emit tools: [] (or a conservative ['web_search']) in the template and require manual review, rather than the full superset.

🛡️ Suggested conservative defaults
     referenceSupportsBody: `    extended_thinking: true,
     priority_tier: true,
-    tools: ['web_search', 'web_fetch', 'code_execution', 'computer_use', 'bash', 'text_editor', 'memory'],`,
+    // NOTE: conservative default — widen manually after verifying model capabilities.
+    tools: [],`,
     referenceSupportsBody: `    output: ['text'],
     capabilities: ['batch_api', 'caching', 'function_calling', 'structured_output', 'thinking'],
-    tools: ['code_execution', 'file_search', 'google_search', 'url_context'],`,
+    // NOTE: conservative default — widen manually after verifying model capabilities.
+    tools: [],`,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/sync-provider-models.ts` around lines 102 - 124, The templates for
new model entries (the referenceSupportsBody string used in the 'anthropic/' and
'google/' blocks) currently hardcode the full tools superset, which will
incorrectly grant unsupported tools to newly synced models; change those
template snippets to emit a conservative default tools list (e.g., tools: [] or
tools: ['web_search']) instead of the full superset, add a short comment in the
template reminding reviewers to manually map per-model tool capabilities, and
ensure any existing per-model capability overrides (the canonical capability
map) remain authoritative so the sync doesn't auto-promote unsupported tools.
packages/typescript/ai-gemini/src/model-meta.ts (1)

289-313: ⚠️ Potential issue | 🟠 Major

Remove file_search from the TTS model tool declarations.

GEMINI_2_5_PRO_TTS and GEMINI_2_5_FLASH_TTS incorrectly declare tools: ['file_search']. Official Gemini documentation explicitly states that TTS models do not support the file_search tool or any general tool use capabilities—they are specialized for audio generation from text inputs only. This appears to be a stale entry carried over from an earlier configuration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-gemini/src/model-meta.ts` around lines 289 - 313,
Remove the unsupported file_search tool from the TTS model metadata objects:
update GEMINI_2_5_PRO_TTS and GEMINI_2_5_FLASH_TTS by deleting 'file_search'
from their supports.tools arrays so their supports only reflect text->audio
capability; ensure the objects still satisfy the ModelMeta type
(GeminiToolConfigOptions & GeminiSafetyOptions & GeminiCommonConfigOptions &
GeminiCachedContentOptions) after the removal and run type checks to confirm no
other tool entries remain for TTS models.
🧹 Nitpick comments (20)
packages/typescript/ai-openrouter/src/tools/web-search-tool.ts (1)

40-51: Empty description may be rejected by stricter consumers/tool registries.

description: '' is returned unconditionally. Some tool-pipeline paths / validators treat the description as required and non-empty (and LLM-facing tool schemas generally benefit from a meaningful description). Since this is a provider-native tool executed by OpenRouter (not the model), an empty string is likely fine on the wire, but consider a short static description (e.g., 'Search the web via OpenRouter.') for clarity and to avoid future validation surprises.

Proposed tweak
-    name: 'web_search',
-    description: '',
+    name: 'web_search',
+    description: 'Search the web via OpenRouter.',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts` around lines
40 - 51, The returned tool object for web_search uses an empty description which
can break stricter validators; update the factory that returns the object (the
one creating name: 'web_search' and casting to OpenRouterWebSearchTool) to
provide a concise static description like "Search the web via OpenRouter."
instead of '', ensuring the description field on the returned object is
non-empty and stays compatible with the existing metadata and the
OpenRouterWebSearchTool cast.
packages/typescript/ai-openai/src/tools/computer-use-tool.ts (1)

29-39: Metadata shape mismatch between factory and converter.

computerUseTool stores metadata = { ...toolData }, which includes toolData.type ('computer_use_preview' per OpenAI.Responses.ComputerTool). convertComputerUseToolToAdapterFormat then casts tool.metadata as ComputerUseToolConfig and reads display_height/display_width/environment — this works, but it means the stored metadata is ComputerUseToolConfig itself (type-tagged), not a plain options object. That’s inconsistent with e.g. webSearchTool in ai-openrouter which stores a nested config. Not a bug, but worth making the pattern consistent across providers for future refactors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openai/src/tools/computer-use-tool.ts` around lines 29
- 39, The factory computerUseTool currently spreads toolData directly into
metadata causing metadata to be the full ComputerUseToolConfig (including its
type tag); change metadata to wrap the options in a nested key (e.g., metadata:
{ config: toolData } or metadata: { computerUseConfig: toolData }) so metadata
is a plain container like other providers, and update
convertComputerUseToolToAdapterFormat to read the nested config key instead of
casting metadata directly; reference computerUseTool,
convertComputerUseToolToAdapterFormat, ComputerUseToolConfig and
OpenAIComputerUseTool when making the change.
packages/typescript/ai-anthropic/tsconfig.json (1)

6-10: Consider broadening include to the whole tests/ directory rather than a single file.

Hard-coding tests/tools-per-model-type-safety.test.ts means any future test file under tests/ won't be type-checked by tsc and must be added here manually. Prefer "tests" (or "tests/**/*.ts") so new type-level tests are picked up automatically, and rely on exclude/Vitest's own include patterns for runtime filtering.

Proposed change
   "include": [
     "vite.config.ts",
     "./src",
-    "tests/tools-per-model-type-safety.test.ts"
+    "tests"
   ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-anthropic/tsconfig.json` around lines 6 - 10, The
tsconfig currently only includes the single file
"tests/tools-per-model-type-safety.test.ts", which will miss any new tests;
update the "include" array in tsconfig.json (the entry used to list
"vite.config.ts", "./src", and the single test file) to cover the whole tests
directory (e.g., "tests" or "tests/**/*.ts") so all new type-level tests are
picked up automatically while runtime filtering remains handled by Vitest.
packages/typescript/ai-gemini/tsconfig.json (1)

6-10: Same note as the Anthropic tsconfig: prefer including the tests/ directory.

Listing a single test file by name is fragile and will silently skip type-checking of any future tests added under tests/.

Proposed change
   "include": [
     "vite.config.ts",
     "./src",
-    "tests/tools-per-model-type-safety.test.ts"
+    "tests"
   ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-gemini/tsconfig.json` around lines 6 - 10, The
tsconfig.json "include" array currently references a single test file
("tests/tools-per-model-type-safety.test.ts"), which is fragile; update the
"include" array (the include property in tsconfig.json) to include the entire
tests directory (e.g., "tests" or "./tests") instead of the single filename so
that all current and future tests under tests/ are type-checked; keep the
existing entries ("vite.config.ts" and "./src") and replace or add the tests
directory entry accordingly.
docs/adapters/grok.md (1)

234-241: Minor: verify internal doc link style.

Other docs in this repo typically reference pages by slug (no .md suffix), e.g., the existing [Tools Guide](../tools/tools) on line 231 of this same file. The new links use ../tools/tools.md and ../tools/provider-tools.md, which is inconsistent and may break under the docs site router. Please confirm with pnpm test:docs.

✏️ Suggested alignment
-See [Tools](../tools/tools.md) for the general tool-definition flow, or
-[Provider Tools](../tools/provider-tools.md) for other providers'
-native-tool offerings.
+See [Tools](../tools/tools) for the general tool-definition flow, or
+[Provider Tools](../tools/provider-tools) for other providers'
+native-tool offerings.

As per coding guidelines: "Verify documentation links are valid via pnpm test:docs command".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/adapters/grok.md` around lines 234 - 241, Update the two internal links
in the "Provider Tools" section to use slug-style references (remove the .md
suffix) so they match the repo convention: change "../tools/tools.md" to
"../tools/tools" and "../tools/provider-tools.md" to "../tools/provider-tools";
then run the docs validation command (pnpm test:docs) and fix any remaining link
warnings/errors reported for the "[Tools]" and "[Provider Tools]" references.
packages/typescript/ai-openai/tsconfig.json (1)

6-10: Consider broadening the include to cover all test files.

Pinning a single test path means future type-safety tests added under tests/ won't be picked up by tsc (test:types). Consider a glob like tests/**/*.test.ts so compile-time assertions added later are automatically enforced.

♻️ Proposed change
   "include": [
     "vite.config.ts",
     "./src",
-    "tests/tools-per-model-type-safety.test.ts"
+    "tests/**/*.test.ts"
   ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openai/tsconfig.json` around lines 6 - 10, The
tsconfig's "include" currently pins only a single test file
("tests/tools-per-model-type-safety.test.ts"), which will miss future tests;
update the "include" array entry for tests to a glob such as
"tests/**/*.test.ts" (or "tests/**/*") so all test files under tests/ are picked
up by tsc for the test:types run—locate the "include" array in tsconfig.json and
replace or add the broader glob entry.
packages/typescript/ai-anthropic/tests/provider-tools-smoke.test.ts (1)

88-117: Consider asserting on type too when converting multiple tools.

The multi-tool conversion only checks names, so a regression where convertFunctionToolToAdapterFormat is mistakenly picked for a branded provider tool (e.g., due to a name-based dispatch bug) would still pass. Adding expect(converted).toContainEqual(expect.objectContaining({ type: 'web_search_20250305' })) (and equivalents) would close that gap and is especially relevant given name-based dispatch patterns exist elsewhere in this PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-anthropic/tests/provider-tools-smoke.test.ts` around
lines 88 - 117, The test "converts multiple provider tools in one call" only
asserts names, so add assertions that the returned converted array contains
objects with the expected types to guard against name-based dispatch
regressions; after calling convertToolsToProviderFormat (variable converted)
include assertions like
expect(converted).toContainEqual(expect.objectContaining({ type:
'web_search_20250305' })) and equivalents for 'code_execution_20250825' and
'bash_20250124' to ensure convertToolsToProviderFormat actually produced
provider-shaped tool objects rather than e.g. convertFunctionToolToAdapterFormat
outputs.
packages/typescript/ai-groq/src/model-meta.ts (1)

367-386: Consider deriving the capabilities map from GROQ_CHAT_MODELS.

Since Groq exposes no provider tools, every entry resolves to readonly []. A mapped type would avoid the risk of adding a new model to GROQ_CHAT_MODELS and forgetting to extend this map (which would silently fall back to never in consumers). For example:

export type GroqChatModelToolCapabilitiesByName = {
  [K in GroqChatModels]: readonly []
}

Keeping the explicit listing is fine for consistency with other providers that do have per-model tool support; flagging as optional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-groq/src/model-meta.ts` around lines 367 - 386, The
explicit per-model mapping in GroqChatModelToolCapabilitiesByName is brittle
(you must update it whenever GROQ_CHAT_MODELS changes); replace it with a
derived mapped type from the Groq model union/collection (e.g., use
GroqChatModels or GROQ_CHAT_MODELS as the key type) that maps each model name to
readonly [] so all current and future Groq models automatically get the correct
tool-capabilities type; update the GroqChatModelToolCapabilitiesByName
definition to use that mapped type instead of enumerating LLAMA_3_1_8B_INSTANT,
LLAMA_3_3_70B_VERSATILE, etc.
.changeset/provider-tools-ai-anthropic.md (1)

5-5: Nit: clarify "signatures unchanged" vs. branded return types.

Return types now widen to branded subtypes (e.g. AnthropicWebSearchTool extends Tool). Consumers who annotated variables as Tool or previously-exported config types should still assign, but anyone who captured the prior return type by name may see differences. Consider noting "runtime behavior is unchanged; factory return types are now branded subtypes of Tool" for precision.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/provider-tools-ai-anthropic.md at line 5, Update the changelog
sentence to explicitly state that runtime behavior and factory signatures remain
unchanged but the declared return types are now branded subtypes of Tool (for
example: AnthropicWebSearchTool extends Tool) and that consumers who typed
variables as Tool or used the `@deprecated` config aliases are unaffected while
callers that depended on the old named return types may observe a different
compile-time type; reference the factories (webSearchTool, codeExecutionTool,
computerUseTool, bashTool, textEditorTool, webFetchTool, memoryTool, customTool)
and the branded examples (AnthropicWebSearchTool) in the wording for clarity.
packages/typescript/ai-grok/src/model-meta.ts (1)

321-333: Consider a mapped type for GrokChatModelToolCapabilitiesByName.

Listing every model by hand works, but drifts easily when models are added/removed. A mapped type keyed on the already-maintained const tuple keeps it in sync automatically (mirroring the pattern in GrokChatModelProviderOptionsByName):

♻️ Proposed refactor
-export type GrokChatModelToolCapabilitiesByName = {
-  [GROK_4_1_FAST_REASONING.name]: typeof GROK_4_1_FAST_REASONING.supports.tools
-  [GROK_4_1_FAST_NON_REASONING.name]: typeof GROK_4_1_FAST_NON_REASONING.supports.tools
-  ...
-}
+export type GrokChatModelToolCapabilitiesByName = {
+  [K in (typeof GROK_CHAT_MODELS)[number]]: readonly []
+}

Since every Grok chat model currently has tools: [] as const, collapsing to readonly [] is equivalent. If per-model divergence is anticipated, keep the per-model form but at minimum add a const-tuple exhaustiveness check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-grok/src/model-meta.ts` around lines 321 - 333, The
explicit GrokChatModelToolCapabilitiesByName object should be rewritten as a
mapped type keyed from the shared models tuple to avoid drift: replace the
manual keys in GrokChatModelToolCapabilitiesByName with a mapped type like
Record<(typeof GROK_CHAT_MODELS)[number]['name'], (typeof
GROK_CHAT_MODELS)[number]['supports']['tools']>; mirror the pattern used by
GrokChatModelProviderOptionsByName, and ensure the shared tuple (e.g.
GROK_CHAT_MODELS) is declared as const so the mapping is exhaustive and stays in
sync when models are added/removed.
packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts (1)

25-27: Env is set after module-load; verify no adapter is constructed at import time.

beforeAll runs after top-level module evaluation. Adapter construction here happens inside each it, so this is fine — but userTool and any future top-level adapter factory would execute before this hook. If openRouterText(...) is ever moved out of an it body, this will throw. Consider setting the env synchronously at the top of the file instead of in beforeAll to make the guarantee explicit and future-proof.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts`
around lines 25 - 27, Set the OPENROUTER_API_KEY synchronously at
module-evaluation time instead of in beforeAll so adapters aren't accidentally
constructed before the env exists: move the assignment
process.env['OPENROUTER_API_KEY'] = 'sk-or-test-dummy' to the top-level of the
test file (above any imports or top-level declarations) and ensure no call to
openRouterText or any adapter factory like userTool runs at import time; this
guarantees adapters created during import will see the env and prevents future
breakage if openRouterText is moved out of an it block.
packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts (1)

44-44: Test title understates coverage.

The it title lists only the positive tools, but the body also asserts rejection of computerUseTool, googleMapsTool, and googleSearchRetrievalTool. Consider renaming to reflect both halves, e.g. "gemini-3.1-pro-preview: accepts {code_execution, file_search, google_search, url_context}; rejects {computer_use, google_maps, google_search_retrieval}", or split into two it blocks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts` at
line 44, Update the test title for the `it` block whose current description is
"gemini-3.1-pro-preview accepts code_execution, file_search, google_search,
url_context" to accurately reflect that the test both accepts and rejects tools
(e.g., "gemini-3.1-pro-preview: accepts {code_execution, file_search,
google_search, url_context}; rejects {computer_use, google_maps,
google_search_retrieval}") or split the body into two `it` blocks (one asserting
accepted tools and one asserting rejected tools) so the test name matches its
assertions; ensure you update any assertion messages/labels within that `it`
block to remain consistent with the new titles.
packages/typescript/ai/tests/tools-per-model-type-safety.test.ts (1)

53-59: Replace toMatchTypeOf with toExtend for type expectations.

Vitest's expectTypeOf().toMatchTypeOf() is deprecated in favor of .toExtend(). Update lines 54 and 58 to use .toExtend() instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts` around
lines 53 - 59, Replace deprecated expectTypeOf(...).toMatchTypeOf() calls with
expectTypeOf(...).toExtend() for the two assertions: change the assertion on
userTool to use expectTypeOf(userTool).toExtend<MockToolsOption>() and change
the assertion on supportedProviderTool to use
expectTypeOf(supportedProviderTool).toExtend<MockToolsOption>(); update the two
occurrences referencing userTool, supportedProviderTool, and MockToolsOption
accordingly.
packages/typescript/ai-anthropic/src/adapters/text.ts (1)

794-821: Pass ResolveToolCapabilities<TModel> explicitly for symmetry.

Both factory return types enumerate ResolveProviderOptions<TModel> and ResolveInputModalities<TModel> but omit the 4th argument. It currently resolves correctly via the class-level default, but explicit symmetry makes the intent obvious and is resilient if the default is ever reordered/removed.

♻️ Optional consistency fix
 ): AnthropicTextAdapter<
   TModel,
   ResolveProviderOptions<TModel>,
-  ResolveInputModalities<TModel>
+  ResolveInputModalities<TModel>,
+  ResolveToolCapabilities<TModel>
 > {

(Apply to both createAnthropicChat and anthropicText.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-anthropic/src/adapters/text.ts` around lines 794 -
821, The return types of createAnthropicChat and anthropicText omit the fourth
generic (ResolveToolCapabilities<TModel>) which currently relies on the class
default; update both function return type annotations to include
ResolveToolCapabilities<TModel> as the fourth generic parameter so signatures
read AnthropicTextAdapter<TModel, ResolveProviderOptions<TModel>,
ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>> to make intent
explicit and robust if class generics change.
packages/typescript/ai-gemini/src/tools/google-maps-tool.ts (1)

11-27: Defensive default is optional; current pattern works correctly.

When googleMapsTool() is called without arguments, metadata is undefined, and convertGoogleMapsToolToAdapterFormat returns { googleMaps: undefined }. However, tests confirm this pattern works—the @google/genai SDK serializes and handles undefined fields gracefully (standard JSON behavior omits undefined values). The identical pattern in googleSearchTool() reinforces this is intentional.

If you prefer explicit defensive defaults for clarity:

Optional improvement
 export function googleMapsTool(
   config?: GoogleMapsToolConfig,
 ): GeminiGoogleMapsTool {
   // Phantom-brand cast: '~provider'/'~toolKind' are type-only and never assigned at runtime.
   return {
     name: 'google_maps',
     description: '',
-    metadata: config,
+    metadata: config ?? ({} as GoogleMapsToolConfig),
   } as unknown as GeminiGoogleMapsTool
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-gemini/src/tools/google-maps-tool.ts` around lines 11
- 27, No change required: the current pattern in googleMapsTool and
convertGoogleMapsToolToAdapterFormat correctly allows metadata to be undefined
(the SDK will omit undefined fields); leave the implementations of
googleMapsTool(config?: GoogleMapsToolConfig) and
convertGoogleMapsToolToAdapterFormat(tool: Tool) as-is. If you prefer an
explicit defensive variant, set default metadata in googleMapsTool by using
metadata: config ?? undefined or default to an empty object, but this is
optional.
packages/typescript/ai-gemini/src/tools/file-search-tool.ts (1)

18-26: LGTM — consistent with the cross-package provider-tool branding pattern.

Factory shape and phantom-brand cast match the other provider tool files; metadata: config preserves the runtime payload expected by convertFileSearchToolToAdapterFormat.

One minor tightening (optional): you can preserve more type safety by asserting the object satisfies Tool before branding:

♻️ Optional tightening
-  return {
-    name: 'file_search',
-    description: '',
-    metadata: config,
-  } as unknown as GeminiFileSearchTool
+  const tool: Tool = {
+    name: 'file_search',
+    description: '',
+    metadata: config,
+  }
+  return tool as unknown as GeminiFileSearchTool

This way, if the underlying Tool shape ever changes, the factory fails to compile instead of silently drifting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-gemini/src/tools/file-search-tool.ts` around lines 18
- 26, The factory currently returns a branded GeminiFileSearchTool via an unsafe
cast; tighten types by first ensuring the returned object satisfies the Tool
shape (e.g., construct the object and assert it satisfies the Tool interface
using a TypeScript "satisfies" check or an explicit Tool-typed const) before
performing the phantom-brand cast to GeminiFileSearchTool in fileSearchTool;
update the return to build a Tool-typed value (retaining name, description,
metadata) and then cast that value to GeminiFileSearchTool so future Tool shape
changes cause a compile error.
packages/typescript/ai-gemini/src/tools/google-search-retriveal-tool.ts (1)

4-30: LGTM.

GoogleSearchRetrievalToolConfig + deprecated alias + GeminiGoogleSearchRetrievalTool brand follow the PR-wide pattern. Optional config parameter and resulting metadata: config (possibly undefined) preserves prior runtime behavior since convertGoogleSearchRetrievalToolToAdapterFormat passes metadata straight into googleSearchRetrieval.

Filename typo google-search-retriveal-tool.tsgoogle-search-retrieval-tool.ts is pre-existing and out of scope here, but worth fixing in a follow-up since this file is now re-exported from the new public ./tools entrypoint.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-gemini/src/tools/google-search-retriveal-tool.ts`
around lines 4 - 30, Rename the file from google-search-retriveal-tool.ts to
google-search-retrieval-tool.ts and update any imports/exports that reference it
so the module continues to export GoogleSearchRetrievalToolConfig,
GoogleSearchRetrievalTool (deprecated alias), GeminiGoogleSearchRetrievalTool,
convertGoogleSearchRetrievalToolToAdapterFormat, and googleSearchRetrievalTool
under the new filename; ensure public entrypoints (e.g., the new ./tools index)
re-export the renamed module and run the build/tests to catch any remaining
broken imports.
packages/typescript/ai-groq/src/adapters/text.ts (1)

577-617: Minor: consider mirroring Gemini’s explicit factory return types.

In createGrokText/grokText (and here in createGroqText/groqText), the return type is declared as GroqTextAdapter<TModel>, relying on the class generic defaults (ResolveProviderOptions<TModel>, ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>) to fill in the rest. Functionally this is equivalent, but createGeminiChat/geminiText in ai-gemini/src/adapters/text.ts (Lines 732-759) spell the resolved generics out explicitly. Making these factory signatures consistent across adapters helps with IDE hovers and avoids surprises if a default is ever changed. Not a blocker.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-groq/src/adapters/text.ts` around lines 577 - 617, The
factory functions createGroqText and groqText currently declare their return
type as GroqTextAdapter<TModel>, relying on class generic defaults; change both
signatures to explicitly spell out the resolved generics (e.g.,
GroqTextAdapter<TModel, ResolveProviderOptions<TModel>,
ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>>) so IDE hovers
and type resolution match the pattern used in createGeminiChat/geminiText;
update both createGroqText and groqText return type annotations to reference
GroqTextAdapter plus ResolveProviderOptions, ResolveInputModalities, and
ResolveToolCapabilities generics.
packages/typescript/ai-openai/src/model-meta.ts (1)

2297-2343: Consider deriving OpenAIChatModelToolCapabilitiesByName via a mapped type to avoid drift.

The map is hand-maintained in parallel with the model constants and with OpenAIChatModelProviderOptionsByName. The PR notes already list "capability map drift" as a known follow-up; one way to eliminate it is to tag each model constant with as const satisfies ModelMeta<...> & { readonly name: K } and build the map from a union of constants, or export the constants so a Record<keyof …, …['supports']['tools']> can be generated. As per coding guidelines ("Provide type safety per model by using model-specific provider options in adapter packages"), this also keeps per-model capability typing authoritative without manual enumeration.

Not blocking; flagging since the same drift risk will re-appear every time a chat model is added.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openai/src/model-meta.ts` around lines 2297 - 2343,
The current OpenAIChatModelToolCapabilitiesByName manual map will drift as
models change; replace it with a derived mapped type built from the existing
model constants/types: create (or use) a union/type of the model constants
(e.g., the exported model constants used to define GPT5_2, GPT_4O, etc.), ensure
each constant is typed as const satisfies ModelMeta<...> & { readonly name: K }
(or export them if not already), then define
OpenAIChatModelToolCapabilitiesByName as Record<typeof
MODEL_CONSTANTS[number]['name'], typeof
MODEL_CONSTANTS[number]['supports']['tools']> (or an equivalent mapped type over
the union) so the per-model supports.tools type is generated automatically from
the model metadata instead of being hand-enumerated.
packages/typescript/ai-openai/src/tools/index.ts (1)

31-115: LGTM — explicit named re-exports are the right move here.

Replacing export * with enumerated exports makes the public surface auditable and the new branded OpenAI*Tool types discoverable. customTool without OpenAICustomTool and function-tool without a factory re-export are both intentional per the PR design (custom is debranded; function tools come from core toolDefinition()).

One small nit: the leading comment on lines 1-3 describes the OpenAITool union below, so it would read more naturally if it sat directly above the export type OpenAITool = ... block (line 17) rather than before the imports.

♻️ Move the documentation comment next to the union it describes
-// Keep the existing discriminated union defined inline.
-// Built from the deprecated config-type aliases — matches the SDK shape that
-// `convertToolsToProviderFormat` emits.
 import type { ApplyPatchTool } from './apply-patch-tool'
@@
 import type { WebSearchTool } from './web-search-tool'

+// Keep the existing discriminated union defined inline.
+// Built from the deprecated config-type aliases — matches the SDK shape that
+// `convertToolsToProviderFormat` emits.
 export type OpenAITool =
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openai/src/tools/index.ts` around lines 31 - 115, Move
the leading documentation comment that currently sits above the imports so it
directly precedes the OpenAITool union declaration; locate the comment at the
top of packages/typescript/ai-openai/src/tools/index.ts and cut/paste it to sit
immediately above the export type OpenAITool = ... block (the union declaration)
so the comment documents that union (keep the comment text unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/provider-tools-ai-gemini.md:
- Around line 1-7: This changeset reshapes exported model metadata by moving
entries from supports.capabilities into supports.tools and renaming
grounding_with_gmaps→google_maps and search_grounding→google_search while
removing code_execution, file_search, and url_context from capabilities; update
the changeset to reflect a breaking change by changing the release type from
minor to major (or add a prominent “BREAKING CHANGE” header and migration note)
so consumers inspecting model.supports.capabilities at runtime/types know to
migrate to supports.tools and the new names.

In @.changeset/provider-tools-ai-openrouter.md:
- Around line 1-7: The changeset incorrectly uses a minor bump for a breaking
export removal; update the changeset to mark the release as major. Change the
version tag from 'minor' to 'major' in the .changeset file for
'@tanstack/ai-openrouter' and adjust the description to clearly state that
createWebSearchTool was removed from the package root and consumers must import
webSearchTool from '@tanstack/ai-openrouter/tools' (mentioning symbols
createWebSearchTool and webSearchTool/OpenRouterWebSearchTool and the tools
subpath) so the changeset accurately represents the breaking change.

In `@docs/adapters/gemini.md`:
- Around line 431-437: Update the example and prose to use the correct
FileSearch property: replace the incorrect fileIds usage inside the
fileSearchTool call with fileSearchStoreNames (an array of file search store
resource names) and change any mentions of "corpus and file IDs" to describe
that the parameter accepts file search store names; reference the FileSearch
type and the fileSearchTool call so the example uses fileSearchStoreNames:
["fileSearchStores/my-store"] instead of fileIds.

In `@docs/adapters/openai.md`:
- Around line 518-568: The examples for localShellTool, shellTool, and
applyPatchTool use openaiText("gpt-5.2") but the "Supported models" note says
only Codex CLI / agent-capable models are supported, causing a mismatch with the
compile-time supports.tools gating; fix by either replacing
openaiText("gpt-5.2") in the localShellTool, shellTool, and applyPatchTool
examples with a model id that is actually listed under supports.tools for those
tools (e.g. an agent-capable/Codex CLI model id used by your provider), or
update the "Supported models" text to explicitly include gpt-5.2 if it truly
supports local_shell/shell/apply_patch; ensure you update the examples
referencing openaiText and the Supported models lines consistently.
- Around line 344-360: The docs example for webSearchTool uses the wrong type
value; update the example call to webSearchTool(...) to pass type: "web_search"
(not "web_search_preview") so it matches the implementation and distinguishes it
from webSearchPreviewTool; locate the example that calls webSearchTool in the
docs and change the type string accordingly.

In `@docs/tools/provider-tools.md`:
- Line 37: Update the string inside the messages array (the object with role:
'user' and content: 'Summarize todays AI news.') to correct the typo by changing
"todays" to "today's" so the content reads "Summarize today's AI news."; locate
the messages array/entry and edit the content value accordingly.

In `@packages/typescript/ai-anthropic/tests/tools-per-model-type-safety.test.ts`:
- Around line 7-21: Reorder the import block to satisfy ESLint import/order and
alphabetical ordering: move external package imports (e.g., z from 'zod' and
toolDefinition from '@tanstack/ai' and TextActivityOptions from
'@tanstack/ai/adapters') above local imports (../src and ../src/tools), and
alphabetize named imports so that beforeAll is correctly ordered inside the
'vitest' import (describe, it, beforeAll -> alphabetically: beforeAll, describe,
it or follow project rule); ensure the tools import list (bashTool,
codeExecutionTool, computerUseTool, customTool, memoryTool, textEditorTool,
webFetchTool, webSearchTool) is alphabetized as required.

In `@packages/typescript/ai-openai/src/adapters/text.ts`:
- Around line 119-121: The public method signatures use the broad
OpenAITextProviderOptions instead of the generic TProviderOptions resolved
per-model; update chatStream and structuredOutput to accept
TextOptions<ResolveProviderOptions<TModel>> and
StructuredOutputOptions<ResolveProviderOptions<TModel>> respectively (or, if
implementation requires the base type, keep the existing parameter type but
narrow it immediately inside the method by casting to
ResolveProviderOptions<TModel>) so callers get model-specific type safety;
reference the chatStream and structuredOutput methods and the
ResolveProviderOptions<TModel> helper when making the change.

In `@packages/typescript/ai-openai/src/model-meta.ts`:
- Around line 40-51: Fix three issues in the tool metadata: update the
computerUseTool() branding and its converter emission so the
ProviderTool<'openai','computer_use'> brand matches the wire type (replace any
emission of 'computer_use_preview' with the correct current wire type 'computer'
or align the ProviderTool brand to the actual wire type used), remove the
deprecated local_shell entry from supports.tools (or consolidate to only
'shell') so only the maintained shell tool is present, and add missing
ProviderTool<'openai',...> factory brands for web_search_preview,
image_generation, and code_interpreter (or remove those supports.tools entries)
so every entry in supports.tools corresponds to an actual ProviderTool factory
and the type-level gating is preserved. Ensure changes touch computerUseTool(),
the converter that emits the tool type, and the supports.tools array and the
respective tool factory definitions to keep compile-time brands and runtime wire
types consistent.

In `@packages/typescript/ai-openrouter/src/tools/tool-converter.ts`:
- Around line 9-18: The current dispatch in convertToolsToProviderFormat uses
the user-controllable tool.name which is brittle; change the branching to detect
a stable provider kind/brand on the Tool (e.g., a metadata.kind or __kind
property set by webSearchTool / ProviderTool) instead of name, so
convertWebSearchToolToAdapterFormat is only called for tools with that explicit
kind/brand; update the code that creates web-search tools (webSearchTool or
ProviderTool<TProvider, 'web_search'>) to set the metadata.kind or __kind marker
and alter convertToolsToProviderFormat to check that marker before delegating to
convertWebSearchToolToAdapterFormat, otherwise call
convertFunctionToolToAdapterFormat.

In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts`:
- Around line 20-25: The converter convertWebSearchToolToAdapterFormat currently
casts tool.metadata to WebSearchToolConfig without validation; update it to
verify that tool.metadata has the expected branded shape (e.g., metadata.type
=== 'web_search' and metadata.web_search is present and has required fields)
before returning, and throw/return a clear error if the shape is invalid.
Alternatively, restrict conversion to only tools created by webSearchTool by
checking an explicit brand (metadata.type or a specific sentinel) and reject
plain toolDefinition instances; reference convertWebSearchToolToAdapterFormat,
Tool, WebSearchToolConfig, and webSearchTool when implementing the
validation/guard.

In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts`:
- Around line 7-13: Reorder and alphabetize imports to satisfy ESLint
import/order and sort-imports: move external packages (vitest, `@tanstack/ai`,
`@tanstack/ai/adapters`, zod) before the local ../src import, and alphabetize
named imports (change "describe, it, beforeAll" to "beforeAll, describe, it")
and package import order (ensure `@tanstack/`* and zod appear before ../src);
apply the same ordering pattern to the Gemini and OpenAI type-safety test files
for consistency.

In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts`:
- Around line 8-13: Reorder the imports to satisfy ESLint: alphabetically sort
the named imports so expectTypeOf appears between describe and it (import {
describe, expectTypeOf, it }), and move the value import toolDefinition before
the type-only imports (ProviderTool, TextActivityOptions, TextAdapter) so that
all value imports come before type imports; update the import list in the file
containing expectTypeOf, toolDefinition, ProviderTool, TextActivityOptions, and
TextAdapter accordingly.

---

Outside diff comments:
In `@docs/migration/migration.md`:
- Around line 13-19: The top overview numbered list is missing the new Section 6
about Provider Tools/OpenRouter; update the list to include a sixth bullet
describing "Provider Tools added — OpenRouter rename and related breaking
changes" (or similar), and ensure the summary mentions the existing renames:
`providerOptions` → `modelOptions` and `toResponseStream` →
`toServerSentEventsStream` and removal of Embeddings so readers see the
OpenRouter/provider-tools note at a glance; edit the list entries (the overview
block containing "Adapter functions split", "Common options flattened", etc.) to
add the sixth item and make sure it references the new Provider Tools/OpenRouter
change so the overview and Section 6 are consistent.

In `@packages/typescript/ai-gemini/src/model-meta.ts`:
- Around line 289-313: Remove the unsupported file_search tool from the TTS
model metadata objects: update GEMINI_2_5_PRO_TTS and GEMINI_2_5_FLASH_TTS by
deleting 'file_search' from their supports.tools arrays so their supports only
reflect text->audio capability; ensure the objects still satisfy the ModelMeta
type (GeminiToolConfigOptions & GeminiSafetyOptions & GeminiCommonConfigOptions
& GeminiCachedContentOptions) after the removal and run type checks to confirm
no other tool entries remain for TTS models.

In `@packages/typescript/ai/tests/type-check.test.ts`:
- Around line 11-22: The MockAdapter type instantiation omits the
TToolCapabilities type parameter, causing it to fall back to the default instead
of matching the mock's toolCapabilities literal; update the MockAdapter alias
(the TextAdapter instantiation) to include the fifth type argument (e.g.,
readonly string[]) so the generic list of tool capabilities is explicit and
aligns with the mock object’s toolCapabilities value.

In `@scripts/sync-provider-models.ts`:
- Around line 102-124: The templates for new model entries (the
referenceSupportsBody string used in the 'anthropic/' and 'google/' blocks)
currently hardcode the full tools superset, which will incorrectly grant
unsupported tools to newly synced models; change those template snippets to emit
a conservative default tools list (e.g., tools: [] or tools: ['web_search'])
instead of the full superset, add a short comment in the template reminding
reviewers to manually map per-model tool capabilities, and ensure any existing
per-model capability overrides (the canonical capability map) remain
authoritative so the sync doesn't auto-promote unsupported tools.

---

Nitpick comments:
In @.changeset/provider-tools-ai-anthropic.md:
- Line 5: Update the changelog sentence to explicitly state that runtime
behavior and factory signatures remain unchanged but the declared return types
are now branded subtypes of Tool (for example: AnthropicWebSearchTool extends
Tool) and that consumers who typed variables as Tool or used the `@deprecated`
config aliases are unaffected while callers that depended on the old named
return types may observe a different compile-time type; reference the factories
(webSearchTool, codeExecutionTool, computerUseTool, bashTool, textEditorTool,
webFetchTool, memoryTool, customTool) and the branded examples
(AnthropicWebSearchTool) in the wording for clarity.

In `@docs/adapters/grok.md`:
- Around line 234-241: Update the two internal links in the "Provider Tools"
section to use slug-style references (remove the .md suffix) so they match the
repo convention: change "../tools/tools.md" to "../tools/tools" and
"../tools/provider-tools.md" to "../tools/provider-tools"; then run the docs
validation command (pnpm test:docs) and fix any remaining link warnings/errors
reported for the "[Tools]" and "[Provider Tools]" references.

In `@packages/typescript/ai-anthropic/src/adapters/text.ts`:
- Around line 794-821: The return types of createAnthropicChat and anthropicText
omit the fourth generic (ResolveToolCapabilities<TModel>) which currently relies
on the class default; update both function return type annotations to include
ResolveToolCapabilities<TModel> as the fourth generic parameter so signatures
read AnthropicTextAdapter<TModel, ResolveProviderOptions<TModel>,
ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>> to make intent
explicit and robust if class generics change.

In `@packages/typescript/ai-anthropic/tests/provider-tools-smoke.test.ts`:
- Around line 88-117: The test "converts multiple provider tools in one call"
only asserts names, so add assertions that the returned converted array contains
objects with the expected types to guard against name-based dispatch
regressions; after calling convertToolsToProviderFormat (variable converted)
include assertions like
expect(converted).toContainEqual(expect.objectContaining({ type:
'web_search_20250305' })) and equivalents for 'code_execution_20250825' and
'bash_20250124' to ensure convertToolsToProviderFormat actually produced
provider-shaped tool objects rather than e.g. convertFunctionToolToAdapterFormat
outputs.

In `@packages/typescript/ai-anthropic/tsconfig.json`:
- Around line 6-10: The tsconfig currently only includes the single file
"tests/tools-per-model-type-safety.test.ts", which will miss any new tests;
update the "include" array in tsconfig.json (the entry used to list
"vite.config.ts", "./src", and the single test file) to cover the whole tests
directory (e.g., "tests" or "tests/**/*.ts") so all new type-level tests are
picked up automatically while runtime filtering remains handled by Vitest.

In `@packages/typescript/ai-gemini/src/tools/file-search-tool.ts`:
- Around line 18-26: The factory currently returns a branded
GeminiFileSearchTool via an unsafe cast; tighten types by first ensuring the
returned object satisfies the Tool shape (e.g., construct the object and assert
it satisfies the Tool interface using a TypeScript "satisfies" check or an
explicit Tool-typed const) before performing the phantom-brand cast to
GeminiFileSearchTool in fileSearchTool; update the return to build a Tool-typed
value (retaining name, description, metadata) and then cast that value to
GeminiFileSearchTool so future Tool shape changes cause a compile error.

In `@packages/typescript/ai-gemini/src/tools/google-maps-tool.ts`:
- Around line 11-27: No change required: the current pattern in googleMapsTool
and convertGoogleMapsToolToAdapterFormat correctly allows metadata to be
undefined (the SDK will omit undefined fields); leave the implementations of
googleMapsTool(config?: GoogleMapsToolConfig) and
convertGoogleMapsToolToAdapterFormat(tool: Tool) as-is. If you prefer an
explicit defensive variant, set default metadata in googleMapsTool by using
metadata: config ?? undefined or default to an empty object, but this is
optional.

In `@packages/typescript/ai-gemini/src/tools/google-search-retriveal-tool.ts`:
- Around line 4-30: Rename the file from google-search-retriveal-tool.ts to
google-search-retrieval-tool.ts and update any imports/exports that reference it
so the module continues to export GoogleSearchRetrievalToolConfig,
GoogleSearchRetrievalTool (deprecated alias), GeminiGoogleSearchRetrievalTool,
convertGoogleSearchRetrievalToolToAdapterFormat, and googleSearchRetrievalTool
under the new filename; ensure public entrypoints (e.g., the new ./tools index)
re-export the renamed module and run the build/tests to catch any remaining
broken imports.

In `@packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts`:
- Line 44: Update the test title for the `it` block whose current description is
"gemini-3.1-pro-preview accepts code_execution, file_search, google_search,
url_context" to accurately reflect that the test both accepts and rejects tools
(e.g., "gemini-3.1-pro-preview: accepts {code_execution, file_search,
google_search, url_context}; rejects {computer_use, google_maps,
google_search_retrieval}") or split the body into two `it` blocks (one asserting
accepted tools and one asserting rejected tools) so the test name matches its
assertions; ensure you update any assertion messages/labels within that `it`
block to remain consistent with the new titles.

In `@packages/typescript/ai-gemini/tsconfig.json`:
- Around line 6-10: The tsconfig.json "include" array currently references a
single test file ("tests/tools-per-model-type-safety.test.ts"), which is
fragile; update the "include" array (the include property in tsconfig.json) to
include the entire tests directory (e.g., "tests" or "./tests") instead of the
single filename so that all current and future tests under tests/ are
type-checked; keep the existing entries ("vite.config.ts" and "./src") and
replace or add the tests directory entry accordingly.

In `@packages/typescript/ai-grok/src/model-meta.ts`:
- Around line 321-333: The explicit GrokChatModelToolCapabilitiesByName object
should be rewritten as a mapped type keyed from the shared models tuple to avoid
drift: replace the manual keys in GrokChatModelToolCapabilitiesByName with a
mapped type like Record<(typeof GROK_CHAT_MODELS)[number]['name'], (typeof
GROK_CHAT_MODELS)[number]['supports']['tools']>; mirror the pattern used by
GrokChatModelProviderOptionsByName, and ensure the shared tuple (e.g.
GROK_CHAT_MODELS) is declared as const so the mapping is exhaustive and stays in
sync when models are added/removed.

In `@packages/typescript/ai-groq/src/adapters/text.ts`:
- Around line 577-617: The factory functions createGroqText and groqText
currently declare their return type as GroqTextAdapter<TModel>, relying on class
generic defaults; change both signatures to explicitly spell out the resolved
generics (e.g., GroqTextAdapter<TModel, ResolveProviderOptions<TModel>,
ResolveInputModalities<TModel>, ResolveToolCapabilities<TModel>>) so IDE hovers
and type resolution match the pattern used in createGeminiChat/geminiText;
update both createGroqText and groqText return type annotations to reference
GroqTextAdapter plus ResolveProviderOptions, ResolveInputModalities, and
ResolveToolCapabilities generics.

In `@packages/typescript/ai-groq/src/model-meta.ts`:
- Around line 367-386: The explicit per-model mapping in
GroqChatModelToolCapabilitiesByName is brittle (you must update it whenever
GROQ_CHAT_MODELS changes); replace it with a derived mapped type from the Groq
model union/collection (e.g., use GroqChatModels or GROQ_CHAT_MODELS as the key
type) that maps each model name to readonly [] so all current and future Groq
models automatically get the correct tool-capabilities type; update the
GroqChatModelToolCapabilitiesByName definition to use that mapped type instead
of enumerating LLAMA_3_1_8B_INSTANT, LLAMA_3_3_70B_VERSATILE, etc.

In `@packages/typescript/ai-openai/src/model-meta.ts`:
- Around line 2297-2343: The current OpenAIChatModelToolCapabilitiesByName
manual map will drift as models change; replace it with a derived mapped type
built from the existing model constants/types: create (or use) a union/type of
the model constants (e.g., the exported model constants used to define GPT5_2,
GPT_4O, etc.), ensure each constant is typed as const satisfies ModelMeta<...> &
{ readonly name: K } (or export them if not already), then define
OpenAIChatModelToolCapabilitiesByName as Record<typeof
MODEL_CONSTANTS[number]['name'], typeof
MODEL_CONSTANTS[number]['supports']['tools']> (or an equivalent mapped type over
the union) so the per-model supports.tools type is generated automatically from
the model metadata instead of being hand-enumerated.

In `@packages/typescript/ai-openai/src/tools/computer-use-tool.ts`:
- Around line 29-39: The factory computerUseTool currently spreads toolData
directly into metadata causing metadata to be the full ComputerUseToolConfig
(including its type tag); change metadata to wrap the options in a nested key
(e.g., metadata: { config: toolData } or metadata: { computerUseConfig: toolData
}) so metadata is a plain container like other providers, and update
convertComputerUseToolToAdapterFormat to read the nested config key instead of
casting metadata directly; reference computerUseTool,
convertComputerUseToolToAdapterFormat, ComputerUseToolConfig and
OpenAIComputerUseTool when making the change.

In `@packages/typescript/ai-openai/src/tools/index.ts`:
- Around line 31-115: Move the leading documentation comment that currently sits
above the imports so it directly precedes the OpenAITool union declaration;
locate the comment at the top of
packages/typescript/ai-openai/src/tools/index.ts and cut/paste it to sit
immediately above the export type OpenAITool = ... block (the union declaration)
so the comment documents that union (keep the comment text unchanged).

In `@packages/typescript/ai-openai/tsconfig.json`:
- Around line 6-10: The tsconfig's "include" currently pins only a single test
file ("tests/tools-per-model-type-safety.test.ts"), which will miss future
tests; update the "include" array entry for tests to a glob such as
"tests/**/*.test.ts" (or "tests/**/*") so all test files under tests/ are picked
up by tsc for the test:types run—locate the "include" array in tsconfig.json and
replace or add the broader glob entry.

In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts`:
- Around line 40-51: The returned tool object for web_search uses an empty
description which can break stricter validators; update the factory that returns
the object (the one creating name: 'web_search' and casting to
OpenRouterWebSearchTool) to provide a concise static description like "Search
the web via OpenRouter." instead of '', ensuring the description field on the
returned object is non-empty and stays compatible with the existing metadata and
the OpenRouterWebSearchTool cast.

In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts`:
- Around line 25-27: Set the OPENROUTER_API_KEY synchronously at
module-evaluation time instead of in beforeAll so adapters aren't accidentally
constructed before the env exists: move the assignment
process.env['OPENROUTER_API_KEY'] = 'sk-or-test-dummy' to the top-level of the
test file (above any imports or top-level declarations) and ensure no call to
openRouterText or any adapter factory like userTool runs at import time; this
guarantees adapters created during import will see the env and prevents future
breakage if openRouterText is moved out of an it block.

In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts`:
- Around line 53-59: Replace deprecated expectTypeOf(...).toMatchTypeOf() calls
with expectTypeOf(...).toExtend() for the two assertions: change the assertion
on userTool to use expectTypeOf(userTool).toExtend<MockToolsOption>() and change
the assertion on supportedProviderTool to use
expectTypeOf(supportedProviderTool).toExtend<MockToolsOption>(); update the two
occurrences referencing userTool, supportedProviderTool, and MockToolsOption
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

Comment on lines +1 to +7
---
'@tanstack/ai-gemini': minor
---

Expose provider-tool factories (`codeExecutionTool`, `fileSearchTool`, `googleSearchTool`, `googleSearchRetrievalTool`, `googleMapsTool`, `urlContextTool`, `computerUseTool`) on a new `/tools` subpath, each returning a branded type gated against the selected model's `supports.tools` list.

Note: `supports.capabilities` entries that described tools (`code_execution`, `file_search`, `grounding_with_gmaps` → renamed `google_maps`, `search_grounding` → renamed `google_search`, `url_context`) have been relocated to the new `supports.tools` field. The `capabilities` array loses those entries. This is a model-meta shape change but not a runtime break.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consider flagging the supports.capabilities reshape as breaking for consumers reading model metadata.

Renaming/relocating entries (grounding_with_gmapsgoogle_maps, search_groundinggoogle_search, and removal of code_execution/file_search/url_context from capabilities) is a visible shape change to exported model metadata. Downstream code that inspects model.supports.capabilities at runtime/type-level will break, even if no adapter runtime path changes. A minor bump may be appropriate only if these maps are considered internal; otherwise this warrants a major or at least a prominent note in the migration guide.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/provider-tools-ai-gemini.md around lines 1 - 7, This changeset
reshapes exported model metadata by moving entries from supports.capabilities
into supports.tools and renaming grounding_with_gmaps→google_maps and
search_grounding→google_search while removing code_execution, file_search, and
url_context from capabilities; update the changeset to reflect a breaking change
by changing the release type from minor to major (or add a prominent “BREAKING
CHANGE” header and migration note) so consumers inspecting
model.supports.capabilities at runtime/types know to migrate to supports.tools
and the new names.

Comment on lines +1 to +7
---
'@tanstack/ai-openrouter': minor
---

**Breaking export change.** `createWebSearchTool` has been removed from the package root. Import `webSearchTool` from `@tanstack/ai-openrouter/tools` instead. See Migration Guide §6 for the before/after snippet.

Alongside: the new `/tools` subpath exposes `webSearchTool` (branded `OpenRouterWebSearchTool`) and the existing `convertToolsToProviderFormat`. A new `supports.tools` channel on each chat model gates provider tools at the type level.
Copy link
Copy Markdown
Contributor

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:

#!/bin/bash
# Check current version and any prior breaking-change changesets for precedent.
fd -t f 'package.json' packages/typescript/ai-openrouter --exec jq -r '.name + " " + .version' {} \;
# Look at recent changesets for how past breaking changes were bumped
fd -t f -e md . .changeset | head -50

Repository: TanStack/ai

Length of output: 466


🏁 Script executed:

# Check the other provider-tools changesets to see how breaking changes were bumped
cat .changeset/provider-tools-ai-anthropic.md
cat .changeset/provider-tools-ai-core.md
cat .changeset/provider-tools-ai-openai.md

Repository: TanStack/ai

Length of output: 1566


🏁 Script executed:

# Check .changeset configuration to understand versioning policy
cat .changeset/config.json 2>/dev/null || cat .changeset/config.js 2>/dev/null || echo "No config found"

Repository: TanStack/ai

Length of output: 498


🏁 Script executed:

# Look for versioning policy documentation
fd -t f -e md '(CONTRIBUTING|README|CHANGELOG)' . | head -5 | xargs -I {} grep -l "major\|minor\|breaking\|pre-1.0\|0\." {} 2>/dev/null || echo "No policy found in docs"

Repository: TanStack/ai

Length of output: 114


Change semantic version to major for this breaking export removal.

This changeset describes an actual breaking change—createWebSearchTool is removed from the package root without a deprecation path. The similar provider-tools changesets (@tanstack/ai-anthropic, @tanstack/ai-openai) are marked minor because they add new /tools exports while preserving backward compatibility via deprecated aliases. The openrouter change removes an export entirely, which is a breaking change and should be major per semver, regardless of the 0.x version number.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/provider-tools-ai-openrouter.md around lines 1 - 7, The changeset
incorrectly uses a minor bump for a breaking export removal; update the
changeset to mark the release as major. Change the version tag from 'minor' to
'major' in the .changeset file for '@tanstack/ai-openrouter' and adjust the
description to clearly state that createWebSearchTool was removed from the
package root and consumers must import webSearchTool from
'@tanstack/ai-openrouter/tools' (mentioning symbols createWebSearchTool and
webSearchTool/OpenRouterWebSearchTool and the tools subpath) so the changeset
accurately represents the breaking change.

Comment thread docs/adapters/gemini.md
Comment on lines +431 to +437
tools: [
fileSearchTool({
fileIds: ["file-abc123"],
}),
],
});
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f 'file-search' packages/typescript/ai-gemini/src/tools | xargs -r cat
rg -nP -C3 "fileSearchTool|FileSearch" packages/typescript/ai-gemini/src

Repository: TanStack/ai

Length of output: 6954


🏁 Script executed:

# Read the documentation file around the mentioned lines to see the prose
head -n 450 docs/adapters/gemini.md | tail -n +400

Repository: TanStack/ai

Length of output: 1795


🏁 Script executed:

# Search for the actual FileSearch type definition or usage examples
rg -n "corpus" docs/adapters/gemini.md -C3
rg -n "FileSearch" docs/adapters/gemini.md -C5

Repository: TanStack/ai

Length of output: 836


🏁 Script executed:

# Look for test files or examples that use fileSearchTool
find packages/typescript/ai-gemini -name "*.test.*" -o -name "*.spec.*" | xargs -r grep -l "fileSearchTool" 2>/dev/null
rg -l "fileSearchTool" packages/typescript/ai-gemini --type ts --type tsx

Repository: TanStack/ai

Length of output: 171


🏁 Script executed:

cat packages/typescript/ai-gemini/tests/tools-per-model-type-safety.test.ts | grep -A 10 -B 5 fileSearchTool

Repository: TanStack/ai

Length of output: 2104


🏁 Script executed:

# Also check if there are any other documentation examples or comments mentioning corpus
rg -n "corpus|fileSearchTool" docs/ --type md -C 2

Repository: TanStack/ai

Length of output: 3270


🏁 Script executed:

# Search for FileSearch usage pattern in the entire codebase
rg "fileSearchTool|FileSearch" packages/typescript/ai-gemini -A 5 -B 2

Repository: TanStack/ai

Length of output: 9947


🏁 Script executed:

# Check package.json for `@google/genai` version
rg "@google/genai" packages/typescript/ai-gemini -A 2 -B 2

Repository: TanStack/ai

Length of output: 8313


🌐 Web query:

Google Gemini AI FileSearch type definition configuration schema

💡 Result:

The Google Gemini API FileSearch is a tool for Retrieval Augmented Generation (RAG) that enables semantic search over uploaded files stored in File Search stores. It is configured within the tools array of a generateContent request. ## Python SDK Configuration (from official docs) python from google import genai from google.genai import types tools=[ types.Tool( file_search=types.FileSearch( file_search_store_names=[file_search_store.name] ) )] Optional chunking_config can be specified during file upload/import for advanced control. ## TypeScript/JavaScript SDK (from googleapis/js-genai types.ts) typescript export declare interface FileSearch { / The names of the file_search_stores to retrieve from. Example: fileSearchStores/my-file-search-store-123 */ fileSearchStoreNames?: string[]; / The number of file search retrieval chunks to retrieve. */ topK?: number; / Metadata filter to apply to the file search retrieval documents. */ filter?: string; } ## Protobuf Definition (from google.ai.generativelanguage.v1beta.content.proto) proto message FileSearch { message RetrievalResource { string rag_store_name = 1; // e.g., "ragStores/my-rag-store-123" or "fileSearchStores/..." } message RetrievalConfig { int32 top_k = 1; // Number of chunks to retrieve } repeated RetrievalResource retrieval_resources = 1; RetrievalConfig retrieval_config = 2; } Note: Early docs use file_search_store_names; protobuf uses retrieval_resources with rag_store_name. Official Python/REST examples confirm file_search_store_names=[...]. ## Usage in REST API json { "tools": [{ "fileSearch": { "fileSearchStoreNames": ["fileSearchStores/my-store"] } }] } FileSearch supports models like Gemini 2.5 Pro/Flash, Gemini 3 series. Files are uploaded via Files API, then imported to stores via uploadToFileSearchStore or similar. Supports PDF, DOCX, TXT, code files, etc. Responses include groundingMetadata with citations. Official documentation: https://ai.google.dev/gemini-api/docs/file-search

Citations:


Fix example to use correct fileSearchStoreNames property.

The example uses fileIds, but the FileSearch type from @google/genai (v1.43.0) defines the property as fileSearchStoreNames (an array of file search store names, not individual file IDs). The prose also incorrectly mentions "corpus"—that property does not exist in the FileSearch interface. Users copying this example will encounter a type error.

Update the example to:

fileSearchTool({
  fileSearchStoreNames: ["fileSearchStores/my-store"],
})

Also revise the prose from "corpus and file IDs" to accurately describe what fileSearchStoreNames accepts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/adapters/gemini.md` around lines 431 - 437, Update the example and prose
to use the correct FileSearch property: replace the incorrect fileIds usage
inside the fileSearchTool call with fileSearchStoreNames (an array of file
search store resource names) and change any mentions of "corpus and file IDs" to
describe that the parameter accepts file search store names; reference the
FileSearch type and the fileSearchTool call so the example uses
fileSearchStoreNames: ["fileSearchStores/my-store"] instead of fileIds.

Comment thread docs/adapters/openai.md
Comment on lines +344 to +360
### `webSearchTool`

Enables the model to run a web search and return grounded results with
citations. Pass a `WebSearchToolConfig` object (typed from the OpenAI SDK)
to configure the tool.

```typescript
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { webSearchTool } from "@tanstack/ai-openai/tools";

const stream = chat({
adapter: openaiText("gpt-5.2"),
messages: [{ role: "user", content: "What's new in AI this week?" }],
tools: [webSearchTool({ type: "web_search_preview" })],
});
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

OpenAI Responses API web_search tool type values supported (web_search vs web_search_preview)

💡 Result:

The OpenAI Responses API supports two web_search tool type values: "web_search" (the generally available version) and "web_search_preview" (the earlier preview version). Both can be specified in the tools array, e.g., {"type": "web_search"} or {"type": "web_search_preview"}. The preview variant ignores the external_web_access parameter and always behaves as if it is true (live access).

Citations:


🏁 Script executed:

# Find the openai.md file and check lines around 344-360
fd -t f "openai.md" | head -5

Repository: TanStack/ai

Length of output: 78


🏁 Script executed:

# Search for webSearchTool and webSearchPreviewTool definitions
rg "webSearchTool|webSearchPreviewTool" --type typescript --type json -B 2 -A 2 | head -80

Repository: TanStack/ai

Length of output: 89


🏁 Script executed:

# Check the actual types/exports in the codebase
fd -t f "\.(ts|js)$" | xargs rg "export.*webSearchTool" | head -20

Repository: TanStack/ai

Length of output: 342


🏁 Script executed:

# Read the openai.md file around lines 344-360
sed -n '340,365p' docs/adapters/openai.md | cat -n

Repository: TanStack/ai

Length of output: 1092


🏁 Script executed:

# Check the webSearchTool implementation
cat -n packages/typescript/ai-openai/src/tools/web-search-tool.ts

Repository: TanStack/ai

Length of output: 1269


🏁 Script executed:

# Search for webSearchPreviewTool
rg "webSearchPreviewTool" --type ts -B 2 -A 10 2>/dev/null | head -100

Repository: TanStack/ai

Length of output: 7978


🏁 Script executed:

# Verify the webSearchPreviewTool implementation to confirm the type difference
cat -n packages/typescript/ai-openai/src/tools/web-search-preview-tool.ts

Repository: TanStack/ai

Length of output: 1583


webSearchTool example uses incorrect type value.

The example uses type: "web_search_preview", which is for webSearchPreviewTool. The regular webSearchTool should use type: "web_search" to match the implementation.

Suggested fix
-  tools: [webSearchTool({ type: "web_search_preview" })],
+  tools: [webSearchTool({ type: "web_search" })],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/adapters/openai.md` around lines 344 - 360, The docs example for
webSearchTool uses the wrong type value; update the example call to
webSearchTool(...) to pass type: "web_search" (not "web_search_preview") so it
matches the implementation and distinguishes it from webSearchPreviewTool;
locate the example that calls webSearchTool in the docs and change the type
string accordingly.

Comment thread docs/adapters/openai.md
Comment on lines +518 to +568
```typescript
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { localShellTool } from "@tanstack/ai-openai/tools";

const stream = chat({
adapter: openaiText("gpt-5.2"),
messages: [{ role: "user", content: "Run the test suite and summarise failures" }],
tools: [localShellTool()],
});
```

**Supported models:** Codex CLI and agent-capable models. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).

### `shellTool`

A function-style shell tool that exposes shell execution as a structured
function call. Takes no arguments.

```typescript
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { shellTool } from "@tanstack/ai-openai/tools";

const stream = chat({
adapter: openaiText("gpt-5.2"),
messages: [{ role: "user", content: "Count lines in all JS files" }],
tools: [shellTool()],
});
```

**Supported models:** Codex CLI and agent-capable models. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).

### `applyPatchTool`

Lets the model apply unified-diff patches to modify files directly. Takes no
arguments — include it in the `tools` array to enable patch application.

```typescript
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { applyPatchTool } from "@tanstack/ai-openai/tools";

const stream = chat({
adapter: openaiText("gpt-5.2"),
messages: [{ role: "user", content: "Fix the import paths in src/index.ts" }],
tools: [applyPatchTool()],
});
```

**Supported models:** Codex CLI and agent-capable models. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Shell/patch tool examples contradict their own “Supported models” notes and the type‑gating feature.

localShellTool, shellTool, and applyPatchTool examples all use openaiText("gpt-5.2"), but each section states they are only supported on "Codex CLI and agent-capable models". Since this PR’s central feature is compile-time gating via supports.tools, copy-pasting these snippets will either (a) hit a type error if gpt-5.2’s supports.tools excludes local_shell/shell/apply_patch, or (b) silently work and contradict the stated support matrix — whichever is the case, one of the two is wrong.

Please pick a model listed in the expanded supports.tools for each tool (e.g. a Codex/agent-capable model id) in the examples, or update the "Supported models" text to match reality.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/adapters/openai.md` around lines 518 - 568, The examples for
localShellTool, shellTool, and applyPatchTool use openaiText("gpt-5.2") but the
"Supported models" note says only Codex CLI / agent-capable models are
supported, causing a mismatch with the compile-time supports.tools gating; fix
by either replacing openaiText("gpt-5.2") in the localShellTool, shellTool, and
applyPatchTool examples with a model id that is actually listed under
supports.tools for those tools (e.g. an agent-capable/Codex CLI model id used by
your provider), or update the "Supported models" text to explicitly include
gpt-5.2 if it truly supports local_shell/shell/apply_patch; ensure you update
the examples referencing openaiText and the Supported models lines consistently.

Comment on lines 40 to 51
tools?: Array<
| 'web_search'
| 'web_search_preview'
| 'file_search'
| 'image_generation'
| 'code_interpreter'
| 'mcp'
| 'computer_use'
| 'local_shell'
| 'shell'
| 'apply_patch'
>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Extract every OpenAI tool-kind string referenced on both sides so we can
# cross-check brand strings vs. supports.tools vs. wire `type:` values.

echo "=== ProviderTool<'openai', ...> brand kinds ==="
rg -nP "ProviderTool<\s*'openai'\s*,\s*'([a-z_]+)'" --type=ts packages/typescript/ai-openai

echo
echo "=== supports.tools entries (unique) ==="
rg -nP "^\s*'(web_search|web_search_preview|file_search|image_generation|code_interpreter|mcp|computer_use|computer_use_preview|local_shell|shell|apply_patch)'," --type=ts packages/typescript/ai-openai/src/model-meta.ts \
  | sed -E "s/.*'([^']+)'.*/\1/" | sort -u

echo
echo "=== wire 'type:' values emitted by converters ==="
rg -nP --type=ts -C1 "type:\s*'[a-z_]+'" packages/typescript/ai-openai/src/tools

Repository: TanStack/ai

Length of output: 5264


🌐 Web query:

OpenAI Responses API tool types: is there a distinct "shell" tool or only "local_shell"? Also confirm the exact name for the computer-use tool (computer_use vs computer_use_preview).

💡 Result:

In the OpenAI Responses API, there is a "shell" tool that supports both hosted (e.g., container_auto) and local execution modes. The older "local_shell" tool is deprecated and marked as outdated in the documentation, recommending use of the "shell" tool instead. There is no distinct generic "shell" tool separate from "local_shell"; "shell" encompasses both hosted and local shell capabilities. The computer-use tool is named "computer" (type: "computer"). The older preview version was "computer_use_preview" (type: "computer_use_preview"), which is deprecated in favor of "computer". Official docs confirm migration from computer_use_preview to "computer".

Citations:


Fix type mismatch between computerUseTool() brand and wire type emission, and verify supports.tools entries align with all tool factories.

The verification found critical issues:

  1. Type mismatch on computer_use tool: The computerUseTool() factory is branded as ProviderTool<'openai', 'computer_use'> but the converter in computer-use-tool.ts:19 emits type: 'computer_use_preview'. This breaks the type-to-wire contract. Additionally, OpenAI API documentation indicates the current tool type should be 'computer' (not the preview variant), suggesting a broader version mismatch.

  2. Deprecated local_shell coexists with current shell: Both tools exist in the codebase (emitting types 'local_shell' and 'shell' respectively). Web search confirms 'shell' is the current maintained tool and 'local_shell' is deprecated. Having both in supports.tools risks breaking if OpenAI discontinues the preview variant.

  3. Missing ProviderTool brands: Three entries in supports.toolsweb_search_preview, image_generation, and code_interpreter—lack corresponding ProviderTool<'openai', ...> factory brands, creating unreachable entries that cannot gate tool arguments at compile time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openai/src/model-meta.ts` around lines 40 - 51, Fix
three issues in the tool metadata: update the computerUseTool() branding and its
converter emission so the ProviderTool<'openai','computer_use'> brand matches
the wire type (replace any emission of 'computer_use_preview' with the correct
current wire type 'computer' or align the ProviderTool brand to the actual wire
type used), remove the deprecated local_shell entry from supports.tools (or
consolidate to only 'shell') so only the maintained shell tool is present, and
add missing ProviderTool<'openai',...> factory brands for web_search_preview,
image_generation, and code_interpreter (or remove those supports.tools entries)
so every entry in supports.tools corresponds to an actual ProviderTool factory
and the type-level gating is preserved. Ensure changes touch computerUseTool(),
the converter that emits the tool type, and the supports.tools array and the
respective tool factory definitions to keep compile-time brands and runtime wire
types consistent.

Comment on lines 9 to 18
export function convertToolsToProviderFormat(
tools: Array<Tool>,
): Array<OpenRouterTool> {
return tools.map((tool) => convertFunctionToolToAdapterFormat(tool))
return tools.map((tool) => {
if (tool.name === 'web_search') {
return convertWebSearchToolToAdapterFormat(tool)
}
return convertFunctionToolToAdapterFormat(tool)
})
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Dispatching on tool.name === 'web_search' is brittle.

name is user-controllable via toolDefinition({ name: 'web_search', ... }), so any user function tool that happens to be named web_search will be routed through convertWebSearchToolToAdapterFormat, which expects a WebSearchToolConfig shape and will produce a malformed OpenRouter payload (or throw) at runtime. Now that provider tools are phantom-branded via ProviderTool<TProvider, TKind>, prefer dispatching on a stable marker (e.g., a metadata.kind/__kind brand set by webSearchTool()) rather than the public name.

♻️ Suggested shape
-  return tools.map((tool) => {
-    if (tool.name === 'web_search') {
-      return convertWebSearchToolToAdapterFormat(tool)
-    }
-    return convertFunctionToolToAdapterFormat(tool)
-  })
+  return tools.map((tool) => {
+    if ((tool as { metadata?: { kind?: string } }).metadata?.kind === 'openrouter.web_search') {
+      return convertWebSearchToolToAdapterFormat(tool)
+    }
+    return convertFunctionToolToAdapterFormat(tool)
+  })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function convertToolsToProviderFormat(
tools: Array<Tool>,
): Array<OpenRouterTool> {
return tools.map((tool) => convertFunctionToolToAdapterFormat(tool))
return tools.map((tool) => {
if (tool.name === 'web_search') {
return convertWebSearchToolToAdapterFormat(tool)
}
return convertFunctionToolToAdapterFormat(tool)
})
}
export function convertToolsToProviderFormat(
tools: Array<Tool>,
): Array<OpenRouterTool> {
return tools.map((tool) => {
if ((tool as { metadata?: { kind?: string } }).metadata?.kind === 'openrouter.web_search') {
return convertWebSearchToolToAdapterFormat(tool)
}
return convertFunctionToolToAdapterFormat(tool)
})
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openrouter/src/tools/tool-converter.ts` around lines 9
- 18, The current dispatch in convertToolsToProviderFormat uses the
user-controllable tool.name which is brittle; change the branching to detect a
stable provider kind/brand on the Tool (e.g., a metadata.kind or __kind property
set by webSearchTool / ProviderTool) instead of name, so
convertWebSearchToolToAdapterFormat is only called for tools with that explicit
kind/brand; update the code that creates web-search tools (webSearchTool or
ProviderTool<TProvider, 'web_search'>) to set the metadata.kind or __kind marker
and alter convertToolsToProviderFormat to check that marker before delegating to
convertWebSearchToolToAdapterFormat, otherwise call
convertFunctionToolToAdapterFormat.

Comment on lines +20 to +25
export function convertWebSearchToolToAdapterFormat(
tool: Tool,
): WebSearchToolConfig {
const metadata = tool.metadata as WebSearchToolConfig
return metadata
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

convertWebSearchToolToAdapterFormat relies on metadata already being the full config.

The converter casts tool.metadata directly to WebSearchToolConfig and returns it. This is correct given webSearchTool() stores metadata: { type: 'web_search', web_search: {...} }, but it silently trusts any Tool with name === 'web_search' to have that exact metadata shape. If a user constructs a plain toolDefinition({ name: 'web_search', ... }) it will be mis-converted. Consider either validating metadata shape here or keying the converter off the branded tool only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openrouter/src/tools/web-search-tool.ts` around lines
20 - 25, The converter convertWebSearchToolToAdapterFormat currently casts
tool.metadata to WebSearchToolConfig without validation; update it to verify
that tool.metadata has the expected branded shape (e.g., metadata.type ===
'web_search' and metadata.web_search is present and has required fields) before
returning, and throw/return a clear error if the shape is invalid.
Alternatively, restrict conversion to only tools created by webSearchTool by
checking an explicit brand (metadata.type or a specific sentinel) and reject
plain toolDefinition instances; reference convertWebSearchToolToAdapterFormat,
Tool, WebSearchToolConfig, and webSearchTool when implementing the
validation/guard.

Comment on lines +7 to +13
import { describe, it, beforeAll } from 'vitest'
import { openRouterText } from '../src'
import { webSearchTool } from '../src/tools'
import type { TextActivityOptions } from '@tanstack/ai/adapters'
import type { ProviderTool } from '@tanstack/ai'
import { toolDefinition } from '@tanstack/ai'
import { z } from 'zod'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix ESLint import order / sort-imports violations.

Static analysis flags beforeAll ordering and @tanstack/ai/zod needing to precede the local ../src import. Same pattern repeats in the Gemini and OpenAI type-safety tests — please apply consistently.

Proposed fix
-import { describe, it, beforeAll } from 'vitest'
-import { openRouterText } from '../src'
-import { webSearchTool } from '../src/tools'
-import type { TextActivityOptions } from '@tanstack/ai/adapters'
-import type { ProviderTool } from '@tanstack/ai'
-import { toolDefinition } from '@tanstack/ai'
-import { z } from 'zod'
+import { beforeAll, describe, it } from 'vitest'
+import { z } from 'zod'
+import { toolDefinition } from '@tanstack/ai'
+import type { ProviderTool } from '@tanstack/ai'
+import type { TextActivityOptions } from '@tanstack/ai/adapters'
+import { openRouterText } from '../src'
+import { webSearchTool } from '../src/tools'
🧰 Tools
🪛 ESLint

[error] 7-7: Member 'beforeAll' of the import declaration should be sorted alphabetically.

(sort-imports)


[error] 12-12: @tanstack/ai import should occur before import of ../src

(import/order)


[error] 13-13: zod import should occur before import of ../src

(import/order)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-openrouter/tests/tools-per-model-type-safety.test.ts`
around lines 7 - 13, Reorder and alphabetize imports to satisfy ESLint
import/order and sort-imports: move external packages (vitest, `@tanstack/ai`,
`@tanstack/ai/adapters`, zod) before the local ../src import, and alphabetize
named imports (change "describe, it, beforeAll" to "beforeAll, describe, it")
and package import order (ensure `@tanstack/`* and zod appear before ../src);
apply the same ordering pattern to the Gemini and OpenAI type-safety test files
for consistency.

Comment on lines +8 to +13
import { describe, it, expectTypeOf } from 'vitest'
import { z } from 'zod'
import type { ProviderTool } from '../src/index'
import type { TextActivityOptions } from '../src/activities/chat/index'
import type { TextAdapter } from '../src/activities/chat/adapter'
import { toolDefinition } from '../src/index'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

ESLint violations flagged by static analysis.

expectTypeOf should be sorted, and the toolDefinition value import should precede the type imports per import/order.

Proposed fix
-import { describe, it, expectTypeOf } from 'vitest'
-import { z } from 'zod'
-import type { ProviderTool } from '../src/index'
-import type { TextActivityOptions } from '../src/activities/chat/index'
-import type { TextAdapter } from '../src/activities/chat/adapter'
-import { toolDefinition } from '../src/index'
+import { describe, expectTypeOf, it } from 'vitest'
+import { z } from 'zod'
+import { toolDefinition } from '../src/index'
+import type { ProviderTool } from '../src/index'
+import type { TextAdapter } from '../src/activities/chat/adapter'
+import type { TextActivityOptions } from '../src/activities/chat/index'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { describe, it, expectTypeOf } from 'vitest'
import { z } from 'zod'
import type { ProviderTool } from '../src/index'
import type { TextActivityOptions } from '../src/activities/chat/index'
import type { TextAdapter } from '../src/activities/chat/adapter'
import { toolDefinition } from '../src/index'
import { describe, expectTypeOf, it } from 'vitest'
import { z } from 'zod'
import { toolDefinition } from '../src/index'
import type { ProviderTool } from '../src/index'
import type { TextAdapter } from '../src/activities/chat/adapter'
import type { TextActivityOptions } from '../src/activities/chat/index'
🧰 Tools
🪛 ESLint

[error] 8-8: Member 'expectTypeOf' of the import declaration should be sorted alphabetically.

(sort-imports)


[error] 13-13: ../src/index import should occur before type import of ../src/index

(import/order)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai/tests/tools-per-model-type-safety.test.ts` around
lines 8 - 13, Reorder the imports to satisfy ESLint: alphabetically sort the
named imports so expectTypeOf appears between describe and it (import {
describe, expectTypeOf, it }), and move the value import toolDefinition before
the type-only imports (ProviderTool, TextActivityOptions, TextAdapter) so that
all value imports come before type imports; update the import list in the file
containing expectTypeOf, toolDefinition, ProviderTool, TextActivityOptions, and
TextAdapter accordingly.

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