-
-
Notifications
You must be signed in to change notification settings - Fork 54.8k
Description
Description
When an agent uses the message tool during a turn AND the turn produces an auto-reply, the sends race because they use independent code paths:
- Auto-reply → core delivery pipeline →
ChannelOutboundAdapter.sendText() - Message tool →
ChannelMessageActionhandler → direct channel send
Both converge on the same underlying channel send function, but with no ordering coordination. On channels with timestamp-based message ordering (Matrix, potentially others), this causes messages to appear out of order.
Steps to Reproduce
- Configure a Matrix channel
- Have the agent send a message via the
messagetool (e.g. sending media) - Have the agent also produce a text auto-reply in the same turn
- Observe that the auto-reply and tool-sent message appear in unpredictable order
Expected Behavior
All outbound messages to the same room/channel should be serialized to preserve send order, regardless of whether they originate from the auto-reply path or the message tool path.
Proposed Fix
Introduce a per-room/channel send queue at the channel plugin level that both the outbound adapter and tool-action handler route through. Each send awaits the previous one, ensuring consistent ordering without artificial delays.
// Conceptual example
class ChannelSendQueue {
private queues = new Map<string, Promise<void>>();
async send(roomId: string, fn: () => Promise<Result>): Promise<Result> {
const prev = this.queues.get(roomId) ?? Promise.resolve();
const next = prev.then(() => fn());
this.queues.set(roomId, next.then(() => {}));
return next;
}
}This could live in the core outbound layer (benefiting all channels) or be implemented per-channel plugin.
Environment
- OpenClaw 2026.2.6
- Matrix channel plugin (@openclaw/matrix 2026.2.3)
- Matrix server: Tuwunel (Conduit fork)