Skip to content

Guided tour

FabrCore 2.0 · Release and package availability

These guides track the current 2.0 source. Stable 2.0.0 publication is pending; package commands show the release target. Until it is published, follow the source quick start or use a matching available prerelease set. Release migration · Runtime modes

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.

  1. Use the agent's principal-delivery API with the intended targets and correlation. Register an IPrincipalMessageRelay for a custom external channel.
  2. Configure retry, expiry, checkpoint and deduplication behavior under the documented Host delivery options. Persist before acknowledging durable work.
  3. 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.
Agent API · reference snippet
// 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

  1. Start a controlled investigation, close the initiating connection, then have the agent send a result mentioning SR-1042 through the principal delivery API.
  2. 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.
  3. 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.