8bbbceef92
Two issues from the review of the LLM package's six adapters. H1: Inconsistent apiKey precedence. Five of six adapters spread the caller's headers first then set the auth header (apiKey wins), but `OpenAICompatibleChat.model` did the opposite (caller headers won). That meant a user passing both `apiKey` and `headers.authorization` would get auth from a different source depending on which adapter they routed through. Flip the OpenAI-compatible adapter to match the rest, and add a comment documenting the rule: apiKey wins, callers who want their own auth header should omit `apiKey` entirely. H4: Gemini tool-schema sanitization was split across two functions that both ran on every Gemini request — `convertJsonSchema` in the adapter (lossy projection: drop empty objects, derive nullable from type-array, allowlist of preserved keys, recursive properties/items) and `sanitizeGeminiSchemaNode` registered as a default `tool-schema` patch (fix-up: integer enums to strings, dangling required filtering, untyped array typing, scalar property stripping). Both passes only ran on Gemini models; debugging a tool schema rejection meant checking both files. Fold the patch's rules into the adapter as `sanitizeToolSchemaNode`, running before the existing projection step (renamed `projectToolSchemaNode`). Compose them in `convertToolSchema` and use that in `lowerTool`. Delete the patch from `provider/patch.ts` and `ProviderPatch.defaults`. The behavior is unchanged — same input, same output — but the rules now live in one file with a header comment explaining the two concerns. The matching test in `gemini.test.ts` no longer needs to opt into a patch list; it now asserts the adapter alone produces the sanitized shape.