Skip to main content

AI agents: Reading conversation history

Get the conversation messages

To read a conversation's messages, call GetConversationMessages (or GetConversationMessagesAsync) and pass the conversation document ID.

GetConversationMessages returns an AiConversationMessagesResult object that includes a Messages list, holding the conversation's messages in chronological order, oldest first.
If no conversation with the given ID exists, the method returns null.

Passing only a conversation ID returns the whole conversation in one call: PageSize, the maximum number of messages in a page, defaults to int.MaxValue, so no page limit applies.
To read the conversation in fixed-size pages, set PageSize and read it a page at a time, as explained in Page through the conversation.

At the default Simple detail level, the result holds the messages between the user and the LLM.
See Control the level of detail for the other detail levels and the message types each level includes.

Example: Read a conversation

// Read the whole conversation (Simple view)
AiConversationMessagesResult result =
await store.AI.GetConversationMessagesAsync("Chats/1-A");

// Messages are returned in chronological order (oldest first)
foreach (AiConversationMessage message in result.Messages)
{
Console.WriteLine($"[{message.Role}] {message.Content}");
}

Besides the messages, the result includes, among others:

  • LastMessageAt
    The time the last message was added to the conversation.
  • TotalUsage
    The cumulative token usage across the whole conversation.
  • HasMoreMessages
    true when the conversation holds more messages beyond the returned page.

See GetConversationMessages and AiConversationMessagesResult in the Syntax section.

Page through the conversation

A single call to GetConversationMessages returns one page of messages, up to PageSize.
By default, the page holds the conversation's most recent messages.

To select another page, pass a GetConversationMessagesOptions object instead of a bare conversation ID:

  • Set Before to a timestamp to read the page of messages immediately before it.
  • Set After to a timestamp to read the page of messages immediately after it.

Before and After are mutually exclusive: a call sets one or the other, never both.

The returned page is always ordered oldest to newest. Before, After, and PageSize decide only the messages a page covers, not their order.

Example: Page back through a conversation

// Read the most recent page
var page = await store.AI.GetConversationMessagesAsync(
new GetConversationMessagesOptions
{
ConversationId = "Chats/1-A",
PageSize = 20
});

// Read the previous page
var previousPage = await store.AI.GetConversationMessagesAsync(
new GetConversationMessagesOptions
{
ConversationId = "Chats/1-A",
// the oldest message's Timestamp
Before = page.Messages[0].Timestamp,
PageSize = 20
});

In the example above, Before is set to the Timestamp of the oldest message on the page just read. However, Before and After accept any DateTime value, not only a message's Timestamp, so a page can start at any point in time, without first reading a page.

Consecutive pages are contiguous, with no gap and no overlap. On the result, HasMoreMessages is true when the conversation holds more messages beyond the page just returned. The further messages are older when Before is set (or when neither Before nor After is set), and newer when After is set. To read the whole conversation one page at a time, call GetConversationMessages again while HasMoreMessages is true.

Catch up on new messages

To catch up after new messages arrive, pass the Timestamp of the newest message already retrieved, using After. GetConversationMessages then returns only the messages added since the last read, not the whole conversation.

// Read only the messages added after the newest message already retrieved
var newMessages = await store.AI.GetConversationMessagesAsync(
new GetConversationMessagesOptions
{
ConversationId = "Chats/1-A",
After = lastSeenTimestamp
});

See GetConversationMessagesOptions in the Syntax section.

Control the level of detail

Use the DetailLevel option to control the level of detail in the result.
Simple, the default, returns only the messages between the user and the LLM, the visible part of the conversation.
The higher levels include additional detail: system prompts, tool calls, summaries, and internal messages.
Raise the level when debugging an agent or inspecting its steps.

Each level includes a different set of messages:

Detail levelMessages returned
SimpleUser messages (including those that carry only an attachment) and the LLM's messages that have content. System prompts, tool calls, summaries, and internal messages are excluded.
DetailedEverything in Simple, plus system prompts and tool calls with their results. Summaries and internal messages are excluded.
FullEverything in Detailed, plus summaries and internal messages. Intended for debugging.

Example: Request the detailed view

// Request the detailed view, e.g. for debugging
var detailed = await store.AI.GetConversationMessagesAsync(
new GetConversationMessagesOptions
{
ConversationId = "Chats/1-A",
DetailLevel = AiConversationDetailLevel.Detailed,
PageSize = 50
});

See AiConversationDetailLevel in the Syntax section.

Inspect tool calls and sub-conversations

At the Detailed or Full detail level, the ToolCalls list of a message from the LLM holds the tool calls the LLM made. Each entry is an AiToolCallResult object, giving the tool's Name, the Arguments the LLM passed, and the Result the tool returned.

An agent can hand work to another agent (a sub-agent).
The exchange with a sub-agent is held in its own conversation (a sub-conversation).

When a tool call invoked a sub-agent, the tool call's SubConversationId points to the resulting sub-conversation. The result's SubConversationIds list gathers every sub-conversation started during this conversation. Pass any ID from SubConversationIds to GetConversationMessages to read the matching sub-conversation.

Example: Read tool calls and sub-conversations

// Read the conversation with tool calls included
var trace = await store.AI.GetConversationMessagesAsync(
new GetConversationMessagesOptions
{
ConversationId = "Chats/1-A",
DetailLevel = AiConversationDetailLevel.Detailed,
PageSize = 50
});

foreach (AiConversationMessage message in trace.Messages)
{
if (message.ToolCalls == null)
continue;

foreach (AiToolCallResult toolCall in message.ToolCalls)
{
// Inspect the tool call and the result returned
Console.WriteLine($"{toolCall.Name}({toolCall.Arguments}) -> {toolCall.Result}");

// If the tool call invoked a sub-agent, read the matching sub-conversation
if (toolCall.SubConversationId != null)
{
var subConversation = await store.AI.GetConversationMessagesAsync(
new GetConversationMessagesOptions
{
ConversationId = toolCall.SubConversationId,
DetailLevel = AiConversationDetailLevel.Detailed
});

Console.WriteLine($" sub-conversation with {subConversation.Messages.Count} messages");
}
}
}

See AiConversationMessage and AiToolCallResult in the Syntax section.
See Multi-agents for the full sub-agent picture.

Syntax

Methods

Conversation history

Reads messages from an AI agent conversation.
The conversation-ID overload returns the whole conversation at the default Simple detail level.
The options overload gives full control over paging and the level of detail.

// Async, returns the whole conversation
public Task<AiConversationMessagesResult> GetConversationMessagesAsync(
string conversationId, CancellationToken token = default)

// Async, with paging and detail options
public Task<AiConversationMessagesResult> GetConversationMessagesAsync(
GetConversationMessagesOptions parameters, CancellationToken token = default)

// Sync, returns the whole conversation
public AiConversationMessagesResult GetConversationMessages(string conversationId)

// Sync, with paging and detail options
public AiConversationMessagesResult GetConversationMessages(GetConversationMessagesOptions parameters)

Usage:

var result = await store.AI.GetConversationMessagesAsync("Chats/1-A");

ParameterTypeDescription
conversationIdstringThe conversation document ID. The overload that takes only this ID returns the whole conversation at the default Simple detail level.
parametersGetConversationMessagesOptionsThe Before and After boundaries, page size, and detail level.
tokenCancellationTokenOptional cancellation token.
Return value
AiConversationMessagesResultThe conversation's messages, in chronological order, with paging and usage metadata.

Classes

Conversation history classes

Parameters for reading messages from an AI agent conversation.

class GetConversationMessagesOptions
{
string ConversationId
DateTime? Before
DateTime? After
int PageSize
AiConversationDetailLevel DetailLevel
}

PropertyTypeDescription
ConversationIdstringThe conversation document ID. Required.
BeforeDateTime?Return messages older than this timestamp (exclusive). Interpreted as UTC. Used to page back to earlier messages. Cannot be combined with After.
AfterDateTime?Return messages newer than this timestamp (exclusive). Interpreted as UTC. Used to catch up on new messages. Cannot be combined with Before.
PageSizeintMaximum number of messages to return. Default: int.MaxValue. Must be greater than 0; a value of 0 or less throws an ArgumentOutOfRangeException.
DetailLevelAiConversationDetailLevelThe level of detail to include in the returned messages. Default: Simple.

A single message in a conversation.

class AiConversationMessage
{
AiMessageRole Role
string Content
List<string> Attachments
DateTime Timestamp
List<AiToolCallResult> ToolCalls
AiUsage Usage
string SubConversationId
}

PropertyTypeDescription
RoleAiMessageRoleWhat the message is: a system prompt, a user message, a message from the LLM, a summary, or an internal message.
See AiMessageRole.
ContentstringThe text content. When the stored message has several text parts, they are joined with line breaks. null for a message from the LLM that only initiated tool calls.
AttachmentsList<string>Names of the attachments associated with this message, if any.
TimestampDateTimeThe time the message was recorded (UTC). Unique within the conversation and rising in message order, so a Timestamp value can be used to page through the conversation.
ToolCallsList<AiToolCallResult>Tool calls initiated by the LLM in this message, with their results inlined. null when the message has no tool calls.
UsageAiUsageToken usage for this message, or null for messages the LLM did not generate (such as user messages and system prompts).
See AiUsage.
SubConversationIdstringFor an Internal role message, the ID of the sub-conversation the message relates to.

Enums

Conversation history enums

Controls the level of detail the returned messages carry.

enum AiConversationDetailLevel
{
Simple,
Detailed,
Full
}

ValueDescription
SimpleUser messages (including those that carry only an attachment) and the LLM's messages that have content. System prompts, tool calls, summaries, and internal messages are excluded.
DetailedAdds system prompts and tool calls with their results. Summaries and internal messages are excluded.
FullEverything in Detailed, plus summaries and internal messages. Intended for debugging.

In this article