Skip to content

[Bug] Bundled Responses SDK namespace mismatch breaks multi-turn conversations when store: false #17926

Closed

Description

@Dulani

Describe the bug

The bundled Responses SDK (packages/opencode/src/provider/sdk/copilot/responses/) has a providerOptions namespace mismatch that breaks multi-turn conversations when store: false is set.

How it works: When an @ai-sdk/openai-compatible provider is configured with useResponses: true, OpenCode routes it through the bundled copilot Responses SDK (see provider.ts ~line 1238). This SDK is the only built-in Responses API adapter for custom providers — we're not choosing to use it; OpenCode routes us through it automatically.

The bug: The SDK stores reasoning metadata under the "openai" namespace but reads from "copilot":

Write side (openai-responses-language-model.ts ~line 530):

providerMetadata: {
  openai: { itemId: part.id, reasoningEncryptedContent: part.encrypted_content }
}

Read side (openai-responses-language-model.ts ~line 197 and convert-to-openai-responses-input.ts ~line 186):

parseProviderOptions({ provider: "copilot", providerOptions, schema })
// looks for providerOptions["copilot"] — but the data is in providerOptions["openai"]

The AI SDK framework passes providerMetadata back as providerOptions verbatim on subsequent turns. So on turn 2, the reasoning data lives at providerOptions["openai"] but the SDK reads from providerOptions["copilot"] → undefined. The reasoning item is dropped, and the server returns:

Item 'msg_...' of type 'message' was provided without its required 'reasoning' item: 'rs_...'

Who's affected:

  1. Any @ai-sdk/openai-compatible provider with useResponses: true — the upstream code in provider.ts routes these through the bundled Responses SDK automatically
  2. GitHub Copilot users with store: false (e.g. zero-data-retention orgs). With store: true (the default), reasoning replays as item_reference and the namespace path is never hit.

Why it's hidden: Issue #2941 reported this symptom and was closed, but the fix (commit 40836e9) addressed itemId stripping — a different root cause. The namespace mismatch was never fixed.

To reproduce

  1. Configure an @ai-sdk/openai-compatible provider with useResponses: true and a model that produces reasoning output (e.g. gpt-5.2)
  2. Send a message that triggers reasoning
  3. Send a follow-up message in the same conversation
  4. Second message fails with: Item 'msg_...' of type 'message' was provided without its required 'reasoning' item

Minimal fix

Change provider: "copilot" to provider: "openai" in both read-side calls:

openai-responses-language-model.ts ~line 197:

- const openaiOptions = await parseProviderOptions({ provider: "copilot", providerOptions, schema })
+ const openaiOptions = await parseProviderOptions({ provider: "openai", providerOptions, schema })

convert-to-openai-responses-input.ts ~line 186:

- const providerOptions = await parseProviderOptions({ provider: "copilot", providerOptions: part.providerOptions, schema })
+ const providerOptions = await parseProviderOptions({ provider: "openai", providerOptions: part.providerOptions, schema })

This aligns the read namespace with the existing write namespace. The SDK was adapted from @ai-sdk/openai which uses "openai" — the "copilot" on the read side was likely introduced during the fork.

This does not affect GitHub Copilot's default behavior: With store: true, reasoning is replayed as item_reference, so the encrypted_content path (and this namespace) is never exercised.

My agent and I have been running a local build with this fix for several weeks against four gpt-5 models via an Azure OpenAI gateway with useResponses: true. Multi-turn conversations with reasoning models work correctly.

Environment

  • OpenCode version: 1.2.27 (bug still present)
  • Provider: @ai-sdk/openai-compatible with useResponses: true
  • Models tested: gpt-5-nano, gpt-5-mini, gpt-5.2, gpt-5.2-codex

Disclosure: This issue was drafted with AI assistance and reviewed/edited by a human before posting.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 17, 2026
  2. added a commit that references this issue on Mar 25, 2026
    62da9bd
  3. Dulani commented on Apr 2, 2026

    @Dulani
    Author

    Update from the author — the fix described above is wrong (or at least, not the right layer)

    After living with the copilot SDK namespace fix for a while, my agent and I realized we were patching the wrong layer. The "Minimal fix" section above changes vendored code inside the bundled copilot Responses SDK — it works, but it's fragile and misidentifies the root problem.

    What we actually ship now: a ~10-line bun patch on @ai-sdk/azure itself, plus two small routing changes in provider.ts. Zero modifications to the copilot SDK.

    The @ai-sdk/azure patch (patches/@ai-sdk%2Fazure@3.0.49.patch)

    --- a/dist/index.js
    +++ b/dist/index.js
    @@ -69,10 +69,14 @@
         let fullUrl;
         if (options.useDeploymentBasedUrls) {
           fullUrl = new URL(`${baseUrlPrefix}/deployments/${modelId}${path}`);
    +    } else if (options.baseURL) {
    +      fullUrl = new URL(`${baseUrlPrefix}${path}`);
         } else {
           fullUrl = new URL(`${baseUrlPrefix}/v1${path}`);
         }
    -    fullUrl.searchParams.set("api-version", apiVersion);
    +    if (!options.baseURL) {
    +      fullUrl.searchParams.set("api-version", apiVersion);
    +    }
         return fullUrl.toString();

    (Same change in dist/index.mjs.)

    This teaches @ai-sdk/azure to respect a custom baseURL without forcing deployment-based URL paths or appending ?api-version=. Our Azure API gateway (and likely others like LiteLLM, Azure API Management, or Cloudflare AI Gateway) handles versioning and routing itself.

    The provider.ts routing change

    // Route Azure providers (and any useResponses: true) through sdk.responses()
    // instead of sdk.languageModel() — this uses the native Responses API path
    // rather than falling through to the bundled copilot SDK
    : (model.providerID.includes("azure") || provider.options?.["useResponses"] === true) 
        && sdk.responses !== undefined
      ? sdk.responses(model.api.id)
      : sdk.languageModel(model.api.id)

    With this routing, Azure models go through @ai-sdk/azure's own .responses() method — which handles the "openai" namespace correctly — instead of being routed through the bundled copilot SDK (which uses "copilot").

    Note on AI SDK v6

    In v6, @ai-sdk/azure changed its providerOptions key from "openai" to "azure" (migration guide). The original fix above ("copilot" → "openai") would be wrong in v6. Using the native .responses() path means the SDK handles its own namespace — we don't have to.

    Tracking the permanent fix

    Our @ai-sdk/azure patch is a workaround until the SDK supports custom baseURL natively:

    • Upstream: vercel/ai #13956 — @ai-sdk/azure: baseURL mode appends /v1 and api-version — breaks custom gateways (open, unassigned)
    • Related: anomalyco/opencode #18089 — @ai-sdk/openai headers not forwarded to HTTP requests when using Responses API with APIM (open)

    Once vercel/ai #13956 lands, the bun patch goes away and only the provider.ts routing change remains.

    Who this applies to

    Anyone routing @ai-sdk/azure through a non-standard base URL (API gateways, proxies, APIM) who hits the rs_ / reasoning item errors. The copilot SDK fix from the original issue still works for @ai-sdk/openai-compatible with useResponses: true on AI SDK v5, but if you're using @ai-sdk/azure directly, this is the cleaner path.

    I should have caught this before filing. The copilot SDK analysis was correct but led to the wrong fix location. Leaving the original issue body intact for reference.

    Disclosure: This update was drafted with AI assistance and reviewed/edited by a human before posting.

  4. Dulani commented on Apr 6, 2026

    @Dulani
    Author

    Verified fixed in upstream v1.3.17

    The namespace mismatch has been resolved in the upstream codebase between v1.3.7 and v1.3.17.

    Verification:

    • packages/opencode/src/provider/sdk/copilot/responses/openai-responses-language-model.ts line 197: provider: "openai"
    • packages/opencode/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts line 187: provider: "openai"

    Both locations now consistently use the "openai" namespace, which resolves the multi-turn conversation failure when store: false.

    Tested with RAND-patched build 1.3.17-rand-patch - multi-turn conversations with reasoning models now work correctly.

    Closing as resolved. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions