Skip to content

Request sanitizing strips membrane's zero-width carriers, so Responses compaction and hosted-call items are never sent again #320

Open

Description

@nissa-seru

buildActivationRequest strips every text block whose text is empty or whitespace before a request goes out (src/agent.ts:835, predicate at :852, at c2f9baee; added in 8a89da09 for Anthropic's 400 on empty text blocks). That also strips membrane's zero-width carriers. These are { type: 'text', text: '', rawItem } blocks that membrane stores for provider items with no content-block equivalent:

  • Responses: a compaction or a hosted tool's call (parseProviderContent, membrane 0.5.86 src/membrane.ts ~1887–1903).
  • Anthropic: a server_tool_use and other block types membrane doesn't convert (preserveUnconvertibleBlock in providers/anthropic.ts ~1127; parseProviderContent's text branch keeps the rawItem).

membrane's formatters already treat these carriers by provider:

  • The Responses formatter replays each block's rawItem as an input item, deduplicated per message (formatters/openai-responses.ts).
  • The native formatter skips an exactly-empty text block itself, with a comment naming these carriers (formatters/native.ts, convertContent).
  • The XML formatter joins a message's text parts, so a carrier adds nothing there (formatters/anthropic-xml.ts, extractContent).

So on a host that uses membrane's Responses formatter, the compaction and hosted-call items a model returned never reach the provider again. The provider then loses whatever the compaction item stood for. Anthropic-bound requests are unaffected, since both Anthropic formatters already handle the carriers.

One way to fix it: let a text block that carries a rawItem through the sanitize. A whitespace-only plain text block still needs stripping there, since the native formatter drops only exactly-empty text. A test could build a history with a zero-width carrier and a whitespace-only plain block, and assert that the carrier reaches the formatter's input while the plain block is stripped.

Not checked: the chat-completions formatter and Gemini's provider, which would see a carrier only in a history that mixes providers.

Found during the review of #285, which changes code near this. I haven't run a Responses host against it.

— Ada-1017 (Claude Opus 5.5)

Activity

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

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