Skip to content

Preserve tool-call providerMetadata across transcript compaction and replay #741

Description

@rrmlima

Summary

Command Code 1.32.2 drops provider-specific metadata when converting AI SDK fullStream tool-call events into its internal tool_use transcript representation.

The installed @ai-sdk/openai-compatible@2.0.35 already supports Google-compatible signed tool calls end to end at the provider boundary:

  • it parses tool_calls[].extra_content.google.thought_signature into providerMetadata.<provider>.thoughtSignature;
  • it serializes providerOptions.google.thoughtSignature back to extra_content.google.thought_signature on a replayed assistant tool call.

However, Command Code's fullStream handling currently reduces a tool-call event to the equivalent of:

{ type: "tool_use", id: toolCallId, name: toolName, input }

The event's providerMetadata is not retained. As a result, signed or otherwise stateful tool-call metadata cannot survive Command Code's transcript persistence, compaction, resume, or replay paths.

This was found while investigating a real Gemini / Google Antigravity failure after compaction. That production incident also has a proxy-side metadata-loss component, so this report does not attribute the entire incident exclusively to Command Code. It tracks the independent Command Code round-trip gap that remains even when an OpenAI-compatible endpoint correctly emits extra_content.

Expected Behavior

Opaque metadata attached to an AI SDK tool-call event should remain associated with that exact tool call through:

  1. AI SDK fullStream consumption;
  2. Command Code's internal tool_use representation;
  3. JSONL transcript persistence;
  4. compaction and session resume;
  5. reconstruction of the next assistant tool-call message sent to the provider.

For Google-compatible endpoints, a real providerMetadata.google.thoughtSignature should be returned byte-for-byte as tool_calls[].extra_content.google.thought_signature for the same historical call.

Command Code should never invent, transform, merge between calls, log, or render the opaque value.

Actual Behavior

The provider layer can expose providerMetadata on the AI SDK tool-call event, but Command Code's tool-call-to-tool_use conversion does not copy it. The persisted transcript retains id, name, and input, but not the provider metadata needed to reconstruct the signed assistant call.

When a Gemini / Antigravity conversation later replays that historical call, the provider can reject it with:

HTTP 400 Invalid request
Function call is missing a thought_signature in functionCall parts.

A real occurrence happened immediately after Command Code compacted a long conversation. The tool call itself had already completed, but the compacted continuation failed before a new tool could run.

Steps to reproduce the issue

Deterministic contract reproduction:

  1. Configure a custom OpenAI-compatible provider in Command Code.

  2. Have the mock/provider return a streamed assistant tool call containing:

    {
      "index": 0,
      "id": "call_test_1",
      "function": { "name": "test_tool", "arguments": "{}" },
      "extra_content": {
        "google": { "thought_signature": "opaque-signature-fixture-long-enough" }
      }
    }
  3. Confirm the underlying AI SDK tool-call event contains providerMetadata.<provider>.thoughtSignature.

  4. Inspect the Command Code internal/session tool_use record. It retains the call ID, name, and input but omits providerMetadata.

  5. Return the tool result and inspect the next request to the mock/provider. The replayed assistant tool_calls[] entry no longer contains extra_content.google.thought_signature.

  6. Repeat after /compact, --resume, or --continue; the metadata remains absent.

Real-provider manifestation:

  1. Route a Gemini model with signed tool-call semantics through an OpenAI-compatible endpoint.
  2. Trigger a tool call and continue until Command Code compacts the session.
  3. Resume the tool loop.
  4. Observe Gemini / Antigravity rejecting the replayed historical function call because its signature is missing.

Command Code Version

1.32.2

Installed provider stack observed during the investigation:

  • ai@6.0.116
  • @ai-sdk/openai-compatible@2.0.35

Operating System

Linux

Terminal/IDE

SSH / Command Code CLI

Shell

bash

Session file (optional)

Not attached because the full JSONL contains private project context. Relevant records were inspected locally and only redacted structural evidence is included here.

Fix prompt (optional)

Preserve bounded opaque provider metadata when mapping AI SDK tool-call events into Command Code internal tool_use events, and restore it when rebuilding the corresponding assistant tool call.

Suggested requirements:

  • Copy the AI SDK tool-call event's providerMetadata into the exact internal tool_use record.
  • Persist it with that call through JSONL storage, compaction, resume, and fork paths.
  • When converting transcript history back into AI SDK prompt content, restore it as the tool call's provider options/metadata so the provider adapter can serialize its native extension.
  • Keep metadata isolated per tool-call ID, including parallel calls with identical names or arguments.
  • Treat metadata as opaque and preserve values byte-for-byte.
  • Never synthesize a missing signature.
  • Apply a serialization depth/byte bound and reject non-JSON values.
  • Do not print the metadata in normal logs, telemetry, TUI rendering, or error messages.
  • If retaining generic provider metadata is considered too broad, minimally support the already-defined Google thoughtSignature field without changing other providers' payloads.

The likely loss point is the fullStream case "tool-call" conversion that currently constructs tool_use from only toolCallId, toolName, input, and providerExecuted.

Additional context

Redacted real failure:

Command Code Trace ID: 830cf76cb8b824c6e7609bb93afa40ca
Model: gemini-3.7-flash via an OpenAI-compatible BYOK endpoint
Sequence: successful write_file tool call -> automatic compaction -> continuation
Result: HTTP 400, missing thought_signature on the historical functionCall

Why this matters beyond one provider:

  • Provider metadata is part of the AI SDK tool-call contract, not display-only reasoning text.
  • Dropping it makes Command Code unable to round-trip providers whose tool calls carry signed or stateful continuation data.
  • Compaction makes the defect visible, but resume, retry, session fork, and any history reconstruction can exercise the same path.

Acceptance criteria:

  • A streamed Google thought signature survives AI SDK parsing, internal tool-use conversion, JSONL persistence, compaction, and replay.
  • The same behavior works for non-streaming tool-call responses.
  • Multiple parallel tool calls retain their own metadata without copying values between calls.
  • /compact, --resume, --continue, and session forks preserve the association.
  • Tool calls without provider metadata remain byte-for-byte behaviorally unchanged.
  • Non-Google providers receive no new fields unless they originally supplied metadata that their adapter knows how to replay.
  • Opaque metadata is absent from normal logs, telemetry, TUI output, and error text.
  • A regression test verifies the next OpenAI-compatible request contains the original extra_content.google.thought_signature on the same tool-call ID.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions