Shared Conversation Context
In a multi-agent flow, one ConversationComposite is shared across all agents that participate in a single user session. How much of that shared context each agent sees is controlled by the handoff configuration.
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:
- The full conversation (all turns, all agents) is saved as a single episode in episodic memory
- The user sees a continuous message thread, not multiple separate chats
- Tool call history from the orchestrator's turn is accessible in the specialist's context (if configured)
Context Sharing Modes
| Mode | What Specialist Sees | Use Case |
|---|---|---|
| Handoff Summary Only | Orchestrator-generated summary + user message | Default — minimal context, fastest |
| Recent History | Last N messages (configurable) + user message | Context-sensitive specialists needing conversation thread |
| Full History | All messages in the conversation | Rarely used — high token cost, reserved for complex tasks |
| Forked Context | New ConversationComposite branched from parent | When 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; }
}