Portal Community

The Single Conversation Principle

Regardless of how many agents participate, the user experiences one conversation. The ConversationComposite belongs to the session — not to a specific agent. When the orchestrator hands off to a specialist, the specialist operates on the same conversationId.

This has important implications:

Context Sharing Modes

ModeWhat Specialist SeesUse Case
Handoff Summary OnlyOrchestrator-generated summary + user messageDefault — minimal context, fastest
Recent HistoryLast N messages (configurable) + user messageContext-sensitive specialists needing conversation thread
Full HistoryAll messages in the conversationRarely used — high token cost, reserved for complex tasks
Forked ContextNew ConversationComposite branched from parentWhen specialist needs an isolated context that must not pollute main session

Context Forking

For cases where a specialist needs to run extended reasoning without polluting the main conversation thread, Octopus supports context forking:

// Forked context: specialist gets a copy of the conversation
var fork = await _conversationRepo.ForkAsync(
    parentConversationId: conversation.Id,
    targetAgentId: specialistAgentId,
    includeHistoryMessages: 5);  // include last 5 messages as starting context

// The fork runs independently
var specialistResponse = await _agentExecutor.ExecuteAsync(specialistAgent, fork, userMessage);

// Merge the specialist's output back as a single "result" message in the main conversation
await _conversationRepo.AppendAsync(
    conversation.Id,
    new LLMMessage(Role.Assistant, specialistResponse.Content,
        metadata: new { source = "specialist", agentId = specialistAgentId }));

Conversation Metadata

Each message in the ConversationComposite carries metadata that identifies which agent produced it — useful for the AgentMonitor micro-frontend:

public class LLMMessage
{
    public string Role { get; set; }
    public string Content { get; set; }
    public MessageMetadata? Metadata { get; set; }
}

public class MessageMetadata
{
    public Guid? AgentId { get; set; }        // which agent produced this message
    public string? AgentDisplayName { get; set; }
    public bool IsHandoffSummary { get; set; }
    public bool IsToolResult { get; set; }
    public string? ToolName { get; set; }
    public DateTimeOffset Timestamp { get; set; }
}