Description
When using the Agent component with a “human operator takeover” flow, a human operator’s reply can overwrite the previous agent’s agentName in the UI. The root cause appears to be how order is advanced and how UIMessages are combined.
From the docs:
- Messages are ordered by
order and stepOrder per thread.[[Message ordering](https://docs.convex.dev/agents/messages#message-ordering)]
- When
saveMessage or generateText is called, the message is added at the thread’s next order with stepOrder = 0, and response messages share that order with increasing stepOrder.[[Message ordering](https://docs.convex.dev/agents/messages#message-ordering)]
listUIMessages / useUIMessages return UIMessages that combine multiple MessageDocs into a single UI bubble when there are multiple tool calls followed by an assistant message.[[Messages](https://docs.convex.dev/agents/messages); [Showing messages in React](https://docs.convex.dev/agents/messages#showing-messages-in-react)]
Observed behavior
- An AI agent sends an assistant reply (e.g.
agentName: "support-bot").
- A human operator sends a reply using
saveMessage with role: "assistant" and agentName: "human:<name>". This is similar to the “Saving a message from a human as an agent” pattern in the docs.[[Human agents](https://docs.convex.dev/agents/human-agents)]
- The operator’s assistant message ends up sharing the same
order as the prior agent reply.
- On the client,
useUIMessages returns a single UIMessage where the previous agent’s content is merged with the operator’s agentName, so the agent’s reply renders as if it came from the operator.
The optimistic UI path (which inserts at maxOrder + 1) does not show this problem; only the server-side save does.
Workaround I’m using
Because there’s no documented way to manually advance order, I’m currently working around this by:
- Inserting a hidden
role: "user" message with a special internal marker (e.g. "[internal:operator]") right before saving the operator’s assistant message.
- Filtering that marker out in the UI render (similar to how internal markers like
[internal:compose] are typically hidden).
This forces the operator turn onto a new order, so it no longer merges with the previous agent reply.
What I’m asking
- Is this merging behavior (human assistant messages reusing the previous assistant’s
order) expected?
- If not, could the Agent component:
- Either advance
order for standalone assistant saveMessage calls that are not part of an LLM response, or
- Expose a supported way to force a new
order / prevent merging for specific messages?
Right now the only reliable lever I’ve found is the hidden user-message anchor, which feels like a hack and adds extra internal messages to the thread.
Description
When using the Agent component with a “human operator takeover” flow, a human operator’s reply can overwrite the previous agent’s
agentNamein the UI. The root cause appears to be howorderis advanced and howUIMessages are combined.From the docs:
orderandstepOrderper thread.[[Message ordering](https://docs.convex.dev/agents/messages#message-ordering)]saveMessageorgenerateTextis called, the message is added at the thread’s nextorderwithstepOrder = 0, and response messages share thatorderwith increasingstepOrder.[[Message ordering](https://docs.convex.dev/agents/messages#message-ordering)]listUIMessages/useUIMessagesreturnUIMessages that combine multipleMessageDocsinto a single UI bubble when there are multiple tool calls followed by an assistant message.[[Messages](https://docs.convex.dev/agents/messages); [Showing messages in React](https://docs.convex.dev/agents/messages#showing-messages-in-react)]Observed behavior
agentName: "support-bot").saveMessagewithrole: "assistant"andagentName: "human:<name>". This is similar to the “Saving a message from a human as an agent” pattern in the docs.[[Human agents](https://docs.convex.dev/agents/human-agents)]orderas the prior agent reply.useUIMessagesreturns a singleUIMessagewhere the previous agent’s content is merged with the operator’sagentName, so the agent’s reply renders as if it came from the operator.The optimistic UI path (which inserts at
maxOrder + 1) does not show this problem; only the server-side save does.Workaround I’m using
Because there’s no documented way to manually advance
order, I’m currently working around this by:role: "user"message with a special internal marker (e.g."[internal:operator]") right before saving the operator’s assistant message.[internal:compose]are typically hidden).This forces the operator turn onto a new
order, so it no longer merges with the previous agent reply.What I’m asking
order) expected?orderfor standalone assistantsaveMessagecalls that are not part of an LLM response, ororder/ prevent merging for specific messages?Right now the only reliable lever I’ve found is the hidden user-message anchor, which feels like a hack and adds extra internal messages to the thread.