Loading workspace insights... Statistics interval
7 days30 daysLatest CI Pipeline Executions
01777d0e fix(ai-openrouter): emit canonical openrouter:web_* wire shape (closes #603 wire-format bug)
Both webSearchTool() and webFetchTool() previously emitted the non-SDK shape
{type: 'web_*', web_*: {...config...}}. TypeScript and the SDK's outbound Zod
schema accept that without error (because 'web_search' happens to be a member
of ChatWebSearchShorthandType), but the nested sub-object is not a field on
any ChatFunctionTool union member, so the serializer silently strips it before
the HTTP send. Net result: every option callers passed (engine, maxResults,
searchPrompt, maxContentTokens, allowedDomains, blockedDomains, ...) was
dropped on the wire and OpenRouter applied defaults.
Verified empirically via aimock's request journal: pre-fix wire bytes were
literally [{"type":"web_search"},{"type":"web_fetch"}].
The fix:
- Bump @openrouter/sdk from 0.12.14 to 0.12.35 to pick up the canonical input
types WebFetchServerTool / WebFetchServerToolConfig / WebSearchConfig.
- Re-source WebSearchToolConfig and WebFetchToolConfig from the SDK directly
(per "use vendor types directly in adapters" feedback).
- Both factories now emit {type: 'openrouter:web_*', parameters: {...}} — the
SDK's canonical shape — and parameters survive serialization with snake_case
conversion.
- Drop the now-unnecessary ChatRequest['tools'] cast in adapters/text.ts
(the SDK union now natively accepts WebFetchServerTool).
Breaking change to webSearchTool()'s option surface:
- Removed: searchPrompt (never modelled by OpenRouter; was silently dropped).
- Added: allowedDomains, excludedDomains, maxTotalResults, searchContextSize,
userLocation.
- engine union widens to auto/native/exa/firecrawl/parallel.
Tests:
- Updated web-fetch-tool.test.ts for the new parameters shape.
- Added web-tools-wire-format.test.ts (unit-level) that runs the actual
ChatRequest$outboundSchema validator and asserts parameters survive.
- Added testing/e2e/tests/openrouter-web-tools-wire.spec.ts that drives the
full adapter -> SDK -> aimock path and queries aimock's /v1/_requests
journal to verify the captured wire bytes contain parameters.
Docs updated to reflect the new option surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>