Skip to content

V2 AI package: audit and close critical protocol correctness gaps #41932

Closed

Description

@opencode-agent

Summary

Track correctness and parity gaps in the V2 native @opencode-ai/ai package by wire protocol.

This is not a feature wishlist. It excludes citations, provider-hosted tools, and unsupported optional capabilities. Scope is critical flow integrity: normalization, ordering, streamed-state settlement, persistence/replay, request intent, terminal outcomes, transport classification, and accounting.

Anthropic Messages (/v1/messages)

P0 — tool/result integrity

P0 — output and continuation integrity

  • Preserve Anthropic compaction blocks and deltas. Closed as out of scope: this is an unsupported optional capability, which this tracker explicitly excludes.
  • Map compaction stop reasons and retain continuation state. Closed as out of scope with the unsupported optional capability above.

P1 — request normalization

P1 — stream and accounting integrity

  • Validate lifecycle ordering: require message_start, reject duplicate/out-of-order starts, reject events after terminal completion, and settle open fragments at EOF.
  • Aggregate raw usage once at terminal completion, including split start/delta counters and compaction/advisor/fallback iterations. Existing tracker: V2: align Anthropic streaming usage aggregation with upstream SDKs #40713.
  • Retain response identity and stop diagnostics. Closed as an unbounded non-critical requirement; concrete provider finish details are retained by feat(core): retain provider finish details #43332 and additional fields should be driven by focused consumer needs.

Bedrock Converse

P0 — tool/result integrity

OpenAI Chat Completions (/v1/chat/completions)

P0 — output integrity

P0 — tool integrity

  • Classify and retain truncated tool arguments correctly. Distinguish provider truncation, transport interruption, and adapter premature finalization; never execute malformed input; add bounded safe recovery. Existing report: fix(llm): handle truncated OpenAI tool arguments #36766.

P1 — stream integrity

OpenAI Responses (/v1/responses)

P0 — tool integrity

P0 — output integrity

P1 — reasoning lifecycle

Gemini generateContent

P0 — function-call integrity

P0 — safety outcome integrity

  • Preserve prompt-level safety blocks (promptFeedback.blockReason) as a typed terminal outcome. Candidate-less blocked prompts currently lose the safety reason and become generic incomplete-stream failures. Completed in fix(ai): preserve Gemini prompt safety blocks #43070.

Shared V2 runner/history layer

P0 — successful execution contract

P1 — persistence and replay

  • Distinguish normally ended fragments from cleanup-interrupted text, reasoning, and tool input. Closed as not planned: interruption is represented by the owning step's terminal outcome rather than duplicating it across fragment events. Existing tracker: V2: distinguish interrupted streamed fragments from completed fragments #36874.
  • Do not persist interrupted raw tool input as an executable call.

P1 — request intent

  • Keep variants effective after native package overrides.

P1 — transport and error classification

Protocol conformance matrix

Add fixtures and differential tests for every native protocol covering:

  1. Single, sequential, and parallel tool calls.
  2. Adjacent user/tool/synthetic/system messages.
  3. Missing, duplicate, reordered, and truncated stream boundaries.
  4. Pending tools/fragments at every terminal event.
  5. Text, refusal, safety block, content filter, empty response, and normal stop outcomes.
  6. Same-origin replay and incompatible provider/model/protocol switching.
  7. Request options and model variants at the actual wire body/header boundary.
  8. Split streaming usage, cache counters, executor iterations, and fallback accounting.
  9. Transport reset before output, during reasoning/text, during tool input, and after tool execution.

Suggested implementation order

  1. Anthropic Messages and Bedrock parallel-result normalization.
  2. Pending tool settlement across Anthropic, Responses, Chat, and Gemini.
  3. Refusal, safety, and empty-success outcome invariants.
  4. Replay provenance and interrupted-fragment semantics.
  5. Request option/variant normalization.
  6. Transport classification and usage aggregation.

Activity

  1. added
    bugSomething isn't working
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Aug 12, 2026
  2. PranavShukla2 commented on Aug 16, 2026

    @PranavShukla2

    I'd like to pick up one narrow item from this list: the Gemini generateContent P0 for normalizing an omitted zero-argument call input.

    Since this issue is already assigned, is that piece unclaimed? Happy to open a focused bug issue for it and keep the PR to just that change plus a decoder test.

    First contribution here, so let me know if you'd rather I go through a different route.

  3. jemstelos commented on Aug 19, 2026

    @jemstelos

    hey there @rekram1-node, found a bug while using Gemini google-vertex models which basically crashes a session anytime parallel tool calls are attempted - went down a rabbit hole and here's what my coding agent found:

    Discovered the Gemini generateContent section doesn't have the parallel-tool-result coalescing fix that Anthropic (#41948) and Bedrock (#41780) already got. Looks like the same bug, just not ported over yet:

    • Coalesce parallel tool results into the immediately following user turn (same pattern as fix(ai): batch Anthropic parallel tool results #41948/fix(ai): merge Bedrock tool result messages #41780)
      What's happening: gemini.ts's lowerMessages pushes a new contents entry per tool-result message instead of merging consecutive ones into a single user turn (the "system" branch a few lines above already does this merge — the "tool" branch just never got it). With 2+ parallel tool calls, that means multiple user turns instead of one turn with N functionResponse parts, which Vertex rejects with:
      provider.invalid-request Please ensure that the number of function response
      parts is equal to the number of function call parts of the function call turn.
      Once that happens the session is permanently stuck — every later message fails the same way.
      Repro: default google-vertex provider, no config changes needed. Prompt something that triggers parallel tool calls (e.g. "search for X across these three files"), then send a follow-up — it fails immediately. Still reproduces on current beta (0.0.0-beta-17639); gemini.ts hasn't changed here.
      Fix, mirroring fix(ai): batch Anthropic parallel tool results #41948:
    -    contents.push({ role: "user", parts })
    +    const previous = contents.at(-1)
    +    if (previous?.role === "user" && previous.parts.every((part) => "functionResponse" in part))
    +      contents[contents.length - 1] = { role: "user", parts: [...previous.parts, ...parts] }
    +    else contents.push({ role: "user", parts })
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

2.0bugSomething isn't workingcoreAnything 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