Files
Ben Taylor 8806f668d1 fix(runtime): coalesce consecutive same-role Anthropic messages before dispatch (#5360)
## Summary

In multi-turn conversations using the Anthropic adapter, a model turn
that contains both assistant text and a tool call can be replayed as two
consecutive `{role: "assistant"}` entries in the Anthropic payload.
Anthropic expects one message object per turn with alternating roles, so
that split payload can blur turn boundaries on the next request. This PR
coalesces same-role Anthropic messages before dispatch so one assistant
turn stays one assistant message.

## Root cause


[`convertMessageToAnthropicMessage`](https://github.com/CopilotKit/CopilotKit/blob/005aebbededbfdd7978b3c1ee221580b74dd088d/packages/runtime/src/service-adapters/anthropic/utils.ts#L142-L219)
maps each CopilotKit message independently. A mixed assistant turn, a
`TextMessage(role=assistant)` followed by an `ActionExecutionMessage`,
therefore becomes two separate assistant entries. The Anthropic Messages
API requires all content blocks for one assistant turn to be sent in a
single message object: https://docs.anthropic.com/en/api/messages


[`AnthropicAdapter.process()`](https://github.com/CopilotKit/CopilotKit/blob/005aebbededbfdd7978b3c1ee221580b74dd088d/packages/runtime/src/service-adapters/anthropic/anthropic-adapter.ts#L282-L366)
already deduplicates `tool_result` blocks on the user side, but it
previously forwarded the mapped assistant-side payload without a
coalescing pass. Rebasing onto current `main` also exposed test-fixture
drift in the two new regression tests, so the final branch updates those
fixtures to the current object-form `TextMessage` constructor and
removes one stale `ActionInput` field while keeping the production fix
unchanged.

## Changes

- `packages/runtime/src/service-adapters/anthropic/utils.ts`: add
`coalesceConsecutiveSameRoleMessages(messages)` to merge adjacent
equal-role
  Anthropic messages by concatenating their `content` arrays.
-
`packages/runtime/src/service-adapters/anthropic/anthropic-adapter.ts`:
call
`coalesceConsecutiveSameRoleMessages` before
`limitMessagesToTokenCount`.
-
`packages/runtime/tests/service-adapters/anthropic/anthropic-adapter.test.ts`:
add same-role regression coverage, align the new fixtures with the
current
object-form `TextMessage` constructor, and remove the stale `parameters`
field
  from the action fixture.
- `.changeset/coalesce-anthropic-same-role-messages.md`: patch changeset
for
  `@copilotkit/runtime`.

## Scope

The `tool_result` allowlist deduplication stays unchanged. Token
trimming and the orphan-removal post-processor still run after
coalescing, so `tool_use` and `tool_result` pairing remains intact. The
OpenAI, Google, LangChain, and Groq adapters are unaffected.

The reported regression and the explicit regression coverage are
assistant-side. The coalescing helper itself is role-agnostic for
adjacent equal-role array content, but this PR does not add a separate
user-side regression case.

## Related PRs and Issues

Prior attempted fix: #2864, which addressed unrelated message callbacks
rather than the adapter payload.

## Test plan

- [x] `pnpm -C packages/runtime exec vitest run
tests/service-adapters/anthropic/anthropic-adapter.test.ts`
  10/10 tests pass, including the two same-role coalescing cases.
- [x] `pnpm -C packages/runtime exec vitest run
tests/service-adapters/anthropic/utils-token-trimming.test.ts`
  9/9 tests pass.
- [ ] CI green (`static / quality`, `test / unit` on Node 20/22/24)

## Upstream

Closes #2910.
Reported by @alonronin.
2026-07-25 08:47:11 -05:00
..
2026-04-10 23:38:59 +00:00
…

CopilotKit - Runtime

banner

✨ Why CopilotKit?

  • Minutes to integrate - Get started quickly with our CLI
  • Framework agnostic - Works with React, Next.js, AGUI and more
  • Production-ready UI - Use customizable components or build with headless UI
  • Built-in security - Prompt injection protection
  • Open source - Full transparency and community-driven
class-support-ecosystem

🧑‍💻 Real life use cases

Deploy deeply-integrated AI assistants & agents that work alongside your users inside your applications.

headless-ui

Documentation

To get started with CopilotKit, please check out the documentation.

Analytics & Privacy

CopilotKit uses Scarf for anonymous usage analytics to help improve the product. Scarf handles all privacy compliance and does not store raw IP addresses. This helps us understand how CopilotKit is being used and prioritize improvements.

Opting Out

To disable analytics, set the environment variable:

export COPILOTKIT_TELEMETRY_DISABLED=true

Or use the DO_NOT_TRACK standard:

export DO_NOT_TRACK=1