Skip to content

Sanitize replayed turns before cross-provider request builds - #540

Merged
TheGreatAxios merged 5 commits into
mainfrom
cl-6905-cross-provider-turn-replay-is-unrepaired-foreign-thinking
Aug 23, 2026
Merged

Sanitize replayed turns before cross-provider request builds#540
TheGreatAxios merged 5 commits into
mainfrom
cl-6905-cross-provider-turn-replay-is-unrepaired-foreign-thinking

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Summary

The vendored inference layer exports a repair pass (transformMessages,
createIDNormalizer in vendor/intx-inference/src/transform.ts) that was
never called from anywhere. Consequences:

  • Switching provider/model mid-session replayed a foreign provider's
    thinking-block signature verbatim, which the new provider 400s on. The
    poisoned turn is already persisted, so every subsequent request in that
    session fails identically.
  • Output-only block types with no cross-provider wire shape (refusal,
    citation, redacted_thinking, audio, video, code execution) crash the
    Anthropic block builder outright once persisted and replayed.
  • Dangling tool_calls only got synthesized error results on the reactor's
    gate-timeout path, not on ordinary cross-provider replay.

src/provider/replay-sanitizer.ts wraps the adapter registry
(withReplaySanitizer) so every resolved adapter's buildRequest first
sanitizes persisted history before building the outgoing request:

  1. Drops output-only unmappable block types the builders don't handle
    (redacted_thinking, citation, audio, video,
    code_execution_request, code_execution_result) and converts
    refusal blocks to plain text.
  2. Strips opaque provider signatures from foreign-model turns so a
    different provider is never handed back a signature it didn't mint.
  3. Delegates to the vendored transformMessages for what it already does
    correctly: dropping thinking blocks from foreign-model turns and
    answering dangling tool_calls with the same synthetic
    tool_result/isError shape the reactor's gate-timeout path uses.

Wired into src/provider/inference-dependencies.ts between
loadAdapterRegistry and createDependencies, so it covers the shared
registry used by the primary agent, sub-agents, and the compaction
summarizer.

vendor/ is untouched — this reuses the exported vendor helpers rather than
duplicating their logic.

Test plan

  • bunx prettier --check / bunx eslint on touched files
  • bun run typecheck
  • bun run build
  • bun test ./src ./tests ./evals (5122 pass, 1 pre-existing unrelated
    failure in src/agent/lsp-availability.test.ts reproduces identically
    on main in this environment)
  • New regression tests in src/provider/replay-sanitizer.test.ts:
    grok-signed thinking history builds cleanly against both the
    Anthropic and Google paths with no signature/thinking on the wire; a
    persisted refusal block doesn't break the Anthropic builder; a
    dangling tool_call is repaired with a synthetic error result

Fixes CL-6905

The vendored transformMessages/createIDNormalizer repair layer was never
wired into the adapter registry, so switching provider/model mid-session
replayed foreign thinking-block signatures verbatim (provider 400s) and
persisted refusal/citation/audio/video blocks crashed the Anthropic block
builder. Dangling tool_calls also went unrepaired outside the gate-timeout
path.

Wrap the adapter registry so every resolved adapter's buildRequest first
strips output-only unmappable block types and opaque provider signatures,
then runs the vendored transformMessages to drop foreign thinking blocks
and answer orphaned tool_calls with synthetic error results.
@linear-code

linear-code Bot commented Aug 23, 2026

Copy link
Copy Markdown

CL-6905

@TheGreatAxios
TheGreatAxios merged commit 04bf0d0 into main Aug 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant