You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(spec): CK#1 @ai-sdk completeness audit — provider-extras bag + 3 field gaps
Two independent audits (MC + SUBC) against @ai-sdk v2 provider-spec canonical
(LanguageModelV2Prompt, ~/Work/OSS/ai packages/provider) converged on the same
root finding. Closes the lossless-superset gaps before the canonical blessing.
G-A (architectural): @ai-sdk routes ALL provider-specific per-part data through ONE
opaque providerOptions/providerMetadata bag (Record<provider,Record<key,JSONValue>>)
on every message AND part — reasoning signature, redacted-data, cacheControl, even
a compaction marker all ride it; the canonical types NONE of them. Typed-field
enumeration kept discovering holes (Gemini thoughtSignature was the latest). Adds a
NATIVE-PRESERVED provider_extras bag at message + part level (§5.10, ordered BTreeMap
so key order can't bust cache; transform never parses it) — makes CK PROVABLY
lossless vs any unenumerated provider extra. New typed-vs-bag rule: a datum is a
typed neutral field IFF the transform branches on it (today: Reasoning.signature,
RedactedReasoning, provider_executed); else it rides the bag. Refactors ContentBlock
to {kind, provider_extras}; demotes the speculative Text/ToolCall signature fields
into the bag.
G-B: ToolResultOutput typed variants (Text|Json|ErrorText|ErrorJson|Content) — JSON
tool results no longer flatten to text; error-ness in the variant (drops is_error).
G-C: provider_executed on ToolCall/ToolResult + Assistant-role result placement
(server-side tools: web_search/built-ins/grounding). G-D: Media.filename.
Plus ToolResult.tool_name preserved (no fragile cross-message re-derive).
Verified NON-gaps (both audits): source/citation parts (response-side, CK is
request-only), system-as-string (CK multi-System richer), cache_control (render-side
+ provider_extras safety net). Conformance §10 gains lossless-round-trip property
test (3c) + typed-output/filename checks (3d).
Co-authored-by: Alfonso [Magic Context] <288211368+alfonso-magic-context@users.noreply.github.com>
|`Reasoning.signature`| NATIVE-PRESERVED verbatim | origin-provider-issued; round-trips byte-verbatim; never canonicalized; valid only for its origin family |
| tool `name` / `ToolResult.tool_name`| CONTENT, canonical string | byte-content; result carries its own name (no cross-message re-derive); load-bearing for Gemini name-binding (§5.6.1 rule 2) |
361
+
|`Reasoning.signature`| TYPED-NEUTRAL + per-edge mapping | typed because transform branches (clear, §6.1); codec maps native bag location (e.g. `providerMetadata.anthropic.signature`) <-> the field |
362
+
|`RedactedReasoning.data`| NATIVE-PRESERVED verbatim | opaque origin blob; byte-verbatim; never emptied/canonicalized; codec maps the @ai-sdk empty-reasoning+redactedData shape |
363
+
|`provider_executed` (call/result) | TYPED-NEUTRAL + per-edge mapping | typed because pairing branches (§7); codec maps `providerExecuted` <-> the field |
364
+
|`provider_extras` (msg + part) | NATIVE-PRESERVED verbatim, ordered | the catch-all bag (§5.10); deterministic key order; codec owns native<->bag; transform never reads |
262
365
|`role`| CANONICAL + per-edge mapping | 4-value enum; each codec maps native role names; assembly (system-pull, tool-role reshape) is RENDER-SIDE, not encode-side |
263
366
| system block / identity lead | CONTENT, opaque + pinned | §5.11; no distinct identity field; pinned (never summarized); identity PLACEMENT is render-side SystemPromptPolicy |
264
-
|`Media.source` (base64/url/file-id) | CANONICAL + per-edge mapping | each codec maps its native media representation to `MediaSource`; owned-render deferred (§9) |
265
-
|`cache_control` placement | RENDER-SIDE | not a CK field; renderer places per `CachePolicy` (§8) |
367
+
|`Media.source` (base64/url/file-id) + `filename`| CANONICAL + per-edge mapping | each codec maps its native media representation to `MediaSource`; `filename` preserved; owned-render deferred (§9) |
368
+
|`cache_control` placement | RENDER-SIDE | not a typed CK field; renderer places per `CachePolicy` (§8); a hint the renderer can't re-derive survives in `provider_extras`|
266
369
|`step_context`| RENDER-SIDE param | rides run-config; never CK content (§8) |
267
370
268
371
Scope note: CK Message represents conversation messages sent TO a provider (the
@@ -429,6 +532,17 @@ per-provider wire policy, and applies on every path.
429
532
(§5.6.1): pair on the canonical id; use identity where a native id exists (no
430
533
durable mapping state); resolve name-binding families per-render. Every field
431
534
in the §5.9 ownership table normalized by the declared edge/mode.
535
+
3c. Carry `provider_extras` (§5.10) at message + part level as an ORDERED map
536
+
(`BTreeMap`); round-trip byte-verbatim; the transform/cache-core never parse it;
537
+
apply the typed-vs-bag rule (a datum is typed ONLY if the transform branches on
538
+
it - today: `Reasoning.signature`, `RedactedReasoning`, `provider_executed`).
539
+
Pass the LOSSLESS-ROUND-TRIP property test: `encode(decode(wire)) == wire` over a
540
+
same-family corpus that includes provider-extras-bearing parts (Gemini
0 commit comments