ChatDock Message Filter Callback
ChatDock's OnMessageReceived parameter has changed from EventCallback<AgentMessage> to Func<AgentMessage, Task<bool>>?. The callback now controls whether an agent message appears in the chat history — return true to display, false to suppress.
Consumers currently using EventCallback<AgentMessage> OnMessageReceived must update their callback signature to Func<AgentMessage, Task<bool>> and return a bool. If you don't need filtering, return Task.FromResult(true) to preserve existing behavior.
Why This Changed
Agents often send structured messages — status updates, state changes, telemetry — that parent components need to process but should not appear as chat bubbles. Previously there was no way to intercept these; they all rendered as raw JSON in the chat window.
With the new callback, the parent component decides per-message whether it belongs in the chat UI. When no callback is set, all messages display (backwards compatible for simple usage). The "thinking" message type continues to be handled internally by ChatDock.
Migration
Before:
<ChatDock OnMessageReceived="HandleMessage" ... />
@code {
// Fire-and-forget — message already in chat by the time this runs
private async Task HandleMessage(AgentMessage message)
{
await UpdateDashboard(message);
}
}
After:
<ChatDock OnMessageReceived="HandleMessage" ... />
@code {
private Task<bool> HandleMessage(AgentMessage message)
{
if (message.MessageType == "status")
{
// Process for activity log, state updates, etc.
ProcessStatusUpdate(message);
return Task.FromResult(false); // suppress from chat
}
return Task.FromResult(true); // show in chat
}
}
If you don't need filtering, the minimal migration is changing the return type:
private Task<bool> HandleMessage(AgentMessage message)
{
// Existing logic unchanged
_activityLog.Insert(0, $"Agent: {message.Message}");
return Task.FromResult(true); // preserve existing behavior
}
Message Type Conventions
ChatDock routes messages based on MessageType. The callback is invoked for all types except "thinking":
| MessageType | ChatDock Behavior |
|---|---|
"thinking" | Shows as typing indicator (auto-fades). Handled internally — callback is not invoked. |
null or "chat" | Callback receives it. Displayed in chat by default. |
"status" | Callback receives it. Agent convention for status updates — suppressing recommended. |
| (any other value) | Callback receives it. Consumer decides whether to display. |
The "status" convention is useful for agents that report progress, state transitions, or telemetry. The parent component can process these for dashboards or logs without cluttering the chat window.
Practical Example: Activity Dashboard
A common pattern is an agent that sends both chat responses and status updates. The parent processes statuses for a sidebar activity feed while only showing conversational messages in the chat:
@page "/dashboard"
<ChatDock UserHandle="@_userHandle"
AgentHandle="dashboard-agent"
AgentType="DashboardAgent"
OnMessageReceived="HandleMessage" />
<div class="activity-sidebar">
<h4>Activity</h4>
@foreach (var entry in _statusLog)
{
<div class="activity-entry">@entry</div>
}
</div>
@code {
private string _userHandle = "user-123";
private List<string> _statusLog = new();
private Task<bool> HandleMessage(AgentMessage message)
{
switch (message.MessageType)
{
case "status":
_statusLog.Insert(0,
$"[{DateTime.Now:HH:mm}] {message.Message}");
return Task.FromResult(false);
default:
return Task.FromResult(true);
}
}
}
Sending Status Messages from Agents
On the agent side, set MessageType to "status" for messages that should be processed but not displayed:
await fabrAgentHost.SendMessage(new AgentMessage
{
FromHandle = fabrAgentHost.GetHandle(),
ToHandle = userHandle,
MessageType = "status",
Message = "Indexing 42 documents..."
});
The parent's OnMessageReceived callback receives this message. If it returns false, the message never enters the chat history. The agent does not need to know whether the UI will display it.
Summary
OnMessageReceivedchanged fromEventCallback<AgentMessage>toFunc<AgentMessage, Task<bool>>?- Return
trueto display a message in chat,falseto suppress it - When no callback is set, all messages display (backwards compatible)
"thinking"messages are still handled internally by ChatDock- Use
MessageType = "status"for agent messages that should be processed but not shown as chat bubbles
Built with FabrCore on .NET 10.
Builder of FabrCore and OpenCaddis.