MODULE 05 · LESSON 5.12
Deliver results after the original turn
Deliver completion after the original request has returned.
Lesson 32 of 86 · FabrCore 2.0
Overview
SendToUserAsync targets the principal through a delivery policy/relay rather than assuming the original socket is still open. This is useful for a completed investigation or reminder. Storage lifetime and the receiving channel determine what survives disconnection.
Delivery targets a principal, not a stale socket
An investigation may finish after the user closes its original chat connection. SendToUserAsync asks the configured principal delivery policy/relay to route the result. Include enough request context that the receiving UI can place the update. The current transport and provider determine retention and which client/channel receives it.
Separate business completion from notification success
The investigation may be complete while its notification is waiting for delivery. Record those states separately so a disconnected client does not cause the business work to repeat. Likewise, an eligible relay accepting a message is not proof a human viewed it. Applications that need acknowledgement must model that explicitly.
Send an out-of-turn result
Download the Operations Desk source. The README lists project setup, package prerequisites and local ports.
- Use the agent's principal-delivery API with the intended targets and correlation. Register an IPrincipalMessageRelay for a custom external channel.
- Configure retry, expiry, checkpoint and deduplication behavior under the documented Host delivery options. Persist before acknowledging durable work.
- Test a result arriving after the web panel is closed, then reopen it and inspect unread state. Add Microsoft proactive delivery only after that channel is configured.
// Let Core choose the most recently active eligible endpoint across installed relays.
await SendToUserAsync("Report ready");
// Select a provider and provider-owned endpoint explicitly.
await SendToUserAsync(
"Your code is 123456",
messageType: "verification.code",
target: new PrincipalDeliveryTarget("sms", "verified-phone-1"));
// Select the provider but let it choose the most recently active eligible endpoint.
// This target keeps external-delivery precedence even while local observers are connected.
await SendToUserAsync(
"Your report is ready",
target: new PrincipalDeliveryTarget("m365copilot"));
// Preserve a structured AgentMessage for provider-specific mapping.
await SendToUserAsync(new AgentMessage
{
MessageType = "report.ready",
DataType = "application/vnd.contoso.report+json",
Data = JsonSerializer.SerializeToUtf8Bytes(report)
});
Close the initiating view before completion
- Start a controlled investigation, close the initiating connection, then have the agent send a result mentioning SR-1042 through the principal delivery API.
- Reconnect through the supported receiving channel and inspect its delivery/history behavior. Confirm the result targets the same principal and is not sent to whichever user last opened the page.
- Inspect the recorded business outcome separately from the notification outcome. If retention or relay support is unavailable, report that limitation instead of re-running the investigation as a delivery retry.
This verifies out-of-turn routing for your configured channel. Mobile background push and Microsoft proactive delivery have their own additional bindings and prerequisites.
If the result is different
Different channels have different delivery guarantees. A relay's successful API call is not proof a human saw the message; an Android background socket is not push notification infrastructure.
Go deeper
Explore the related documentation.