Skip to main content

Conversation & Chat History

AgentFlow stores each multi-turn thread as a conversation. A conversation contains the visible user/assistant chat history, execution metadata, tool and sub-agent events, attachments, artifacts, markers, and stream recovery state. Persistent cross-conversation personalization lives in Memory. This page covers conversation-local state only.

Vocabulary

TermMeaning
ConversationDurable thread metadata: id, title, agent, source, timestamps, and active-generation state
Chat historyFlat user/assistant messages returned by /messages and used by SDK detail()
TimelineFull UI reconstruction, including tool calls, sub-agent calls, artifacts, approvals, and feedback
Conversation partsAtomic persisted rows that rebuild chat history and timeline state
Chat-history compactionConversation-local summarization used when a long thread approaches the model context window
MemoryPersistent per-user or per-agent facts across conversations; see Memory

Conversation Lifecycle

SDK users usually create or resume a conversation handle, then send messages through it:
from agentflow import AsyncAgentFlow

async with AsyncAgentFlow.from_profile("local") as client:
    agent_id = (await client.agents.by_name("MainAgent")).id
    conversation = await client.conversations.create(agent_id=agent_id)

    first = await conversation.send("What open opportunities do we have?")
    second = await conversation.send("Filter that to renewals over $100K")
Raw REST callers should provide stable conversation_id and message_id values when chatting so retries can be de-duplicated:
POST /api/v1/agent/{agent_id}/chat
{
  "message": "What open opportunities do we have?",
  "conversation_id": "conv_new_001",
  "message_id": "msg_001",
  "stream": true
}

Reading Conversations

page = await client.conversations.list(limit=20)

for conv in page.conversations:
    print(conv.title, conv.message_count, conv.has_active_generation)

detail = await client.conversations.detail("conv_001")
for msg in detail.messages:
    print(f"[{msg.role}] {msg.content}")
GET /api/v1/conversations?limit=50&offset=0
GET /api/v1/conversations/{conversation_id}
GET /api/v1/conversations/{conversation_id}/messages
GET /api/v1/conversations/{conversation_id}/timeline
/messages is intentionally flat and user-facing. /timeline is richer and reconstructs the execution tree for product UI.

Conversation Parts

Conversation parts are the durable source of truth. They are ordered by seq and linked by call_id, parent_call_id, and root_call_id.
Stored part typePublic role/viewSource
useruser messageUser chat request or manual message append
agent_textassistant messageStreamed assistant text
tool_requestTimeline eventLLM tool call
tool_responseTimeline eventTool result
sub_agent_requestTimeline eventDelegation to a sub-agent
sub_agent_responseTimeline eventSub-agent result
system_eventHidden or timeline system eventArtifact summaries, artifact actions, compaction notices
Hidden model-only context attached to a user turn is stored with that user part’s enriched model input, not as the user-visible message body.

Timeline And Stream Recovery

Live chat streams are detached from browser lifetime. If a tab closes, the run continues in the background and clients can reconnect to the durable conversation stream:
last_event_id = None

async for event in client.conversations.watch("conv_001", last_event_id=last_event_id):
    last_event_id = event.sse_id
    render(event.content)
The stream emits:
EventPurpose
conversation_snapshotInitial durable timeline snapshot
conversation_updateUpdated durable snapshot when parts, runs, markers, or feedback change
conversation_stream_completeTerminal marker once no active generation remains

Chat-History Compaction

Long conversations can exceed model context windows. AgentFlow compacts conversation-local chat history by summarizing older turns while keeping recent turns intact. This is not persistent user memory. It is a per-conversation context-window optimization.
SettingDefaultDescription
max_conversation_messages50Trigger compaction when history exceeds this count
target_conversation_messages30Target message count after compaction
preserve_recent_turns10Always keep this many recent user turns
Run compaction manually:
POST /api/v1/agent/{agent_id}/conversations/{conversation_id}/compress

Titles And Metadata

Titles are generated from early user messages and can be overwritten:
await conversation.set_title("Q3 Pipeline Review")

title = await client.conversations.generate_title(
    conversation.id,
    seed_message="What does our Q3 pipeline look like?",
    force=True,
)
Conversation metadata also includes source, source_id, source_metadata, last_read_at, has_active_generation, and message_count for list and sidebar workflows.