Portal Community

Handoff Context

When a handoff occurs, a HandoffContext object is passed to the target agent. It contains exactly what the target agent needs to continue the conversation intelligently:

public class HandoffContext
{
    public Guid SourceAgentId { get; set; }
    public Guid ConversationId { get; set; }
    public string UserMessage { get; set; }          // the message that triggered routing
    public string HandoffReason { get; set; }         // why the handoff was initiated
    public List<LLMMessage> RecentHistory { get; set; }  // last N messages from the conversation
    public Dictionary<string, object> ContextData { get; set; }  // key facts extracted by orchestrator
    public string SummaryForTarget { get; set; }      // brief summary for the target agent's context
}

AgentHandoffService

public class AgentHandoffService : IAgentHandoffService
{
    public async Task<HandoffResult> TransferAsync(
        Guid conversationId,
        Guid targetAgentId,
        HandoffContext context,
        CancellationToken ct = default)
    {
        // 1. Load target agent
        var targetAgent = await _agentRepo.GetCompositeAsync(targetAgentId, context.TenantId);

        // 2. Build the target agent's initial context with handoff summary
        var handoffMessage = new LLMMessage(Role.System,
            $"[Handoff from {context.SourceAgentId}] {context.SummaryForTarget}");

        // 3. Update conversation to reference the new agent
        await _conversationRepo.UpdateAgentAsync(conversationId, targetAgentId);

        // 4. Invoke target agent with handoff context
        var response = await _agentExecutor.ExecuteAsync(
            targetAgent,
            conversation: await _conversationRepo.GetAsync(conversationId),
            newMessage: context.UserMessage,
            prependMessages: new[] { handoffMessage },
            ct: ct);

        return new HandoffResult
        {
            TargetAgentId = targetAgentId,
            Response = response,
            HandoffSucceeded = true
        };
    }
}

Context Included in Handoff

ItemAlways IncludedConfigurable
User's triggering messageYesNo
Handoff reason (from orchestrator)YesNo
Summary for target agentYesTemplate in team config
Recent message historyNoMaxHandoffHistoryMessages setting
Tool call historyNoIncludeToolHistoryOnHandoff setting
Full conversation historyNoNot recommended — use episodic memory instead

User Experience During Handoff

By default, handoffs are transparent to the user — they continue chatting without knowing which agent responded. Optionally, the chat-app can be configured to show a "Now speaking with: HR Agent" indicator:

// TypeScript: chat-app handoff event
interface HandoffEvent {
  type: 'agent_handoff';
  fromAgent: { id: string; displayName: string };
  toAgent: { id: string; displayName: string };
  handoffReason: string;
  showToUser: boolean;  // controlled by team config
}

// SSE event sent during handoff:
// event: handoff
// data: {"type":"agent_handoff","toAgent":{"id":"...","displayName":"HR Agent"},"showToUser":true}
Handoff vs. New Conversation

A handoff keeps the same ConversationComposite — the session ID does not change. This means episodic memory still accumulates correctly. The conversation will appear as a single session in the user's history, even though multiple agents responded.