ConversationComposite
The ConversationComposite is the stateful session object for one conversation. It holds the live message history, working memory context, and tool call records — and is the unit that gets saved as an episode at conversation end.
Session Lifecycle
Session Start
A new ConversationComposite is created when a user sends their first message. The session ID is either provided by the client (for resuming) or generated fresh.
Active Turns
Each user message + agent response pair adds to Messages. Tool calls are recorded in ToolCalls. Working memory context is rebuilt on each turn.
Session End
The session ends via explicit close (user ends chat), timeout (configurable idle timeout), or agent decision. At close, the full conversation is persisted as an episode.
Episode Persisted
The episodic memory store saves the full message history. The episode is indexed for future semantic search if embedding is enabled.
Message Types in the Conversation
| Role | When Added | Content |
|---|---|---|
system | Session start | Assembled system prompt from AgentComposite |
context | Before each user turn | Injected semantic + episodic memory results |
user | Each user message | The user's text input |
assistant | Each LLM response | Agent's response text or tool call directive |
tool | After tool execution | Tool execution result fed back to LLM |
Context Preservation Across Turns
The WorkingMemoryManager rebuilds the context window on every turn. It reads from the ConversationComposite's message list and prunes as needed to stay within the token budget:
public class WorkingMemoryManager
{
public async Task<IReadOnlyList<LLMMessage>> BuildContextAsync(
ConversationComposite conversation,
AgentComposite agent,
string newUserMessage)
{
var budget = agent.MemoryConfig.MaxWorkingMemoryTokens;
var messages = new List<LLMMessage>();
// 1. System prompt (always first, always included)
messages.Add(new LLMMessage(Role.System, agent.SystemPrompt));
// 2. Semantic memory retrieval
var retrieved = await _semanticStore.SearchAsync(
agent.Id, newUserMessage, agent.MemoryConfig.SemanticTopK);
if (retrieved.Any())
messages.Add(new LLMMessage(Role.Context, FormatRetrieved(retrieved)));
// 3. Message history (with pruning)
var history = PruneToFit(conversation.Messages, budget - CountTokens(messages));
messages.AddRange(history);
// 4. Current user message
messages.Add(new LLMMessage(Role.User, newUserMessage));
return messages;
}
}
Tool Call Recording
Every tool call made during a conversation is recorded in the ToolCalls collection. This serves two purposes: audit trail and context for subsequent turns.
public class ToolCallRecord
{
public Guid Id { get; init; }
public string ToolName { get; set; }
public JsonDocument Input { get; set; }
public JsonDocument? Output { get; set; }
public bool Succeeded { get; set; }
public string? ErrorMessage { get; set; }
public DateTimeOffset CalledAt { get; init; }
public TimeSpan Duration { get; set; }
}
Multi-Session Context
When a user returns for a new session, the new ConversationComposite does not automatically have the history of past sessions. Past context arrives through episodic memory retrieval — the MemoryOrchestrator searches past episodes for semantically relevant content and injects it as context messages.
For web chat: session ID is stored in the browser session. For embedded widgets: the host application can provide a stable session ID to allow resuming. For API callers: pass sessionId in the request body. If omitted, a new session is always created.