Progress Notifications in FabrCore
Long-running agent operations need progress feedback. FabrCore's SendMessage with MessageKind.OneWay is the primitive for this — no dedicated streaming API required.
The Pattern
When an agent performs a long-running task, it can send progress updates back to the caller using fire-and-forget messages. The caller's handle is available via message.FromHandle:
public override async Task<AgentMessage> OnMessage(
AgentMessage message)
{
var callerHandle = message.FromHandle;
var items = GetWorkItems(message);
for (int i = 0; i < items.Count; i++)
{
// Send progress notification
await AgentHost.SendMessage(callerHandle,
new AgentMessage
{
Message = $"Processing {i + 1}/{items.Count}",
Kind = MessageKind.OneWay,
Channel = "progress",
MessageType = "status-update"
});
await ProcessItem(items[i]);
}
return new AgentMessage
{
Message = $"Completed {items.Count} items"
};
}
Handling Progress on the Caller Side
The calling agent receives progress messages through its own OnMessage. Route them using the channel and message type metadata:
public override async Task<AgentMessage> OnMessage(
AgentMessage message)
{
if (message.Channel == "progress"
&& message.Kind == MessageKind.OneWay)
{
// Forward to UI or log it
logger.LogInformation(
"Progress from {Agent}: {Status}",
message.FromHandle, message.Message);
return new AgentMessage();
}
// Normal message handling
return await base.OnMessage(message);
}
Forwarding to the Client
If your UI needs to display progress, the client SDK's event system bridges the gap. Use SendEvent to push progress to connected clients:
// In the orchestrator agent's progress handler
await AgentHost.SendEvent(new AgentEvent
{
EventType = "progress",
Data = message.Message
});
The client SDK receives events through the OnEvent callback, which you can wire up to your Blazor component, React state, or any other UI framework.
Why Not a Streaming API?
Teams sometimes request a dedicated streaming or progress API. Here's why the messaging primitive is sufficient:
OneWay messages are already fire-and-forget. They don't block the sender or require the receiver to respond. That's exactly the semantics you need for progress notifications.
Events bridge to the client. The SendEvent mechanism already pushes real-time data to connected clients over WebSocket. Adding a separate streaming channel would be redundant.
You control the granularity. Some operations need per-item updates, others need percentage-based progress, others need milestone notifications. A generic streaming API would either be too rigid or too abstract. With SendMessage, you send exactly the updates your use case needs.
Learn More
Check out the progress notification documentation for the complete reference.
Builder of FabrCore and OpenCaddis.