mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-28 21:45:51 +08:00
Every protocol wrote its own provider metadata onto stream events through an untyped `providerMetadata(key, Record<string, unknown>)` one-liner — copied verbatim into anthropic-messages, bedrock-converse and gemini — and read it back in request lowering with `isRecord(x) && typeof x.signature === "string"`. Writer and reader of the same namespace never met at compile time, so a typo on either side only showed up as a signature that failed to round-trip. `ProviderShared.providerMetadata(schema)` now pairs a typed `write` with a synchronous `read` (`decodeUnknownOption`). Each of anthropic-messages, bedrock-converse, gemini, open-responses and openai-chat declares one `Schema.Struct` of the fields it publishes under its `providerMetadataKey` and uses the pair; the hand-written readers are deleted, and responses-compaction's raw metadata literal goes through the typed writer. Fields use `lenient` because the old readers were independent per field: a malformed `signature` never dropped `redactedData`. A new test pins that. Every reader was probed against v2 for undefined/null/array/string/ empty-object metadata and unknown keys; behaviour is unchanged. Bedrock keeps its own two-field struct rather than importing Anthropic's, since no protocol depends on another protocol's internals. Raw usage passthroughs stay raw: they forward each provider's usage object and are never read back.