Skip to content

Agent-to-Agent (A2A) Communication

A2A enables workflows to communicate with each other—whether sending chat messages, data payloads, or invoking Temporal operations like signals, queries, and updates.

Overview

Source Target Communication Method
Any workflow Built-in workflow SendChatAsync, SendDataAsync
Any workflow Custom workflow SendSignalAsync, QueryAsync, UpdateAsync

All A2A operations are accessed via XiansContext.A2A within workflow or activity contexts.


Communicating with Built-in Workflows

Built-in workflows handle A2A messages through message listener methods (OnUserChatMessage, OnUserDataMessage).

Sending Chat Messages

C#
// Send to a specific workflow instance
var response = await XiansContext.A2A.SendChatAsync(targetWorkflow, new A2AMessage 
{ 
    Text = "Hello" 
});

// Send to a built-in workflow by name
var response = await XiansContext.A2A.SendChatToBuiltInAsync("WebWorkflow", new A2AMessage 
{ 
    Text = "Process this request" 
});

// Shorthand for text-only messages
var response = await XiansContext.A2A.SendTextAsync("WebWorkflow", "Hello");

Sending Data Messages

C#
var message = new A2AMessage
{
    Text = "Process order",
    Data = new { orderId = "12345", items = new[] { "item1", "item2" } }
};

var response = await XiansContext.A2A.SendDataToBuiltInAsync("OrderProcessor", message);

Receiving Messages (Target Workflow)

C#
var workflow = agent.Workflows.DefineBuiltIn(name: "OrderProcessor");

// Handle chat messages
workflow.OnUserChatMessage(async context =>
{
    var incomingText = context.Message.Text;
    await context.ReplyAsync($"Processed: {incomingText}");
});

// Handle data messages
workflow.OnUserDataMessage(async context =>
{
    var orderData = context.Message.Data;
    await context.ReplyAsync("Order received", new { status = "confirmed" });
});

Communicating with Custom Workflows

Custom workflows use Temporal's native signal, query, and update mechanisms. These are ideal for structured, typed communication.

Sending Signals (Fire-and-Forget)

C#
await XiansContext.A2A.SendSignalAsync(
    workflowId: "order-workflow-123",
    signalName: "ProcessOrder",
    new { orderId = "12345", priority = "high" }
);

Querying State (Read-Only)

C#
var state = await XiansContext.A2A.QueryAsync<OrderState>(
    workflowId: "order-workflow-123",
    queryName: "GetOrderStatus",
    "12345"  // query arguments
);

Sending Updates (Request-Response)

Updates are synchronous operations that modify state and return a result. Requires Temporal Server 1.20+.

C#
var result = await XiansContext.A2A.UpdateAsync<ProcessResult>(
    workflowId: "order-workflow-123",
    updateName: "UpdateOrderStatus",
    new { orderId = "12345", status = "shipped" }
);

Receiving in Custom Workflows

C#
[Workflow("MyAgent:OrderWorkflow")]
public class OrderWorkflow
{
    private readonly Queue<object> _requests = new();
    private int _processedCount = 0;

    [WorkflowRun]
    public async Task RunAsync()
    {
        while (true)
        {
            await Workflow.WaitConditionAsync(() => _requests.Count > 0);
            var request = _requests.Dequeue();
            // Process request...
        }
    }

    [WorkflowSignal("ProcessOrder")]
    public Task ProcessOrder(object request)
    {
        _requests.Enqueue(request);
        return Task.CompletedTask;
    }

    [WorkflowQuery("GetOrderStatus")]
    public OrderState GetOrderStatus(string orderId)
    {
        return new OrderState { OrderId = orderId, Count = _processedCount };
    }

    [WorkflowUpdate("UpdateOrderStatus")]
    public Task<ProcessResult> UpdateOrderStatus(object request)
    {
        _processedCount++;
        return Task.FromResult(new ProcessResult { Status = "Updated" });
    }
}

A2AMessage Properties

Property Description
Text Primary text content
Data Structured data payload (any serializable object)
Metadata Key-value pairs for custom metadata
Authorization Auth token passed to target context
ThreadId Conversation/correlation tracking
ParticipantId Original user/caller identifier
Scope Defaults to "A2A"
Hint Processing hint for the target

Preserving Context

Use A2AMessage.FromContext() to forward context fields when chaining A2A calls:

C#
workflow.OnUserChatMessage(async context =>
{
    // Preserve original context when forwarding
    var message = A2AMessage.FromContext(context, text: "Forward this");
    message.Metadata = new Dictionary<string, string> { ["source"] = "forwarder" };

    var response = await XiansContext.A2A.SendChatToBuiltInAsync("TargetWorkflow", message);
});

Error Handling

Use Try* variants for non-throwing error handling:

C#
var (success, response, error) = await XiansContext.A2A.TrySendChatToBuiltInAsync(
    "WebWorkflow", 
    new A2AMessage { Text = "Hello" }
);

if (!success)
{
    logger.LogWarning("A2A failed: {Error}", error);
}

Quick Reference

C#
// To Built-in workflows
await XiansContext.A2A.SendTextAsync("WorkflowName", "message");
await XiansContext.A2A.SendChatToBuiltInAsync("WorkflowName", message);
await XiansContext.A2A.SendDataToBuiltInAsync("WorkflowName", message);

// To Custom workflows  
await XiansContext.A2A.SendSignalAsync(workflowId, "SignalName", args);
var result = await XiansContext.A2A.QueryAsync<T>(workflowId, "QueryName", args);
var result = await XiansContext.A2A.UpdateAsync<T>(workflowId, "UpdateName", args);