Consistent ordering of tool calls between streaming results and history?

Hi Folks,

I apologize if this has been answered before. I couldn’t find anything quite like it via search.

Currently, when streaming results from an Agent, the tool call events are interleaved with the run content events. This makes it nice to display the tool calls interleaved with the agent response in the UI.

However, I can’t seem to find a way to query the session history via any AgentOS endpoint that will maintain that original temporal ordering. For example /sessions/{session_id}/runs will return all of the runs for the session, but in each run the input content and tools are hoisted and merged to the top of the object, making it not possible to restore the tool call order. If you look at the events array for the run, the events are merged as well, eliminating all of the interleaved RunContent and ToolCall* events.

I haven’t yet found an API endpoint that maintains this ordering. What I’ve been doing so far, for consistency, is to just hoist all streaming tool call events above the agent response for a particular run in the UI, but that is not ideal since often the LLM generates some response text confirming it just performed an action related to a given tool.

If the events field of a run contained the “true” history of all events that were streamed for that run, this would be a non-issue. Additionally, I could use the same code-path to hydrate the UI both when streaming results, and when loading from history. i.e. a load from history would just replay all of the events for the run.

Is this possible? I may be totally missing something.

Cheers!

Hi @sccolbert, thank you for reaching out and supporting Agno. I’ve shared this with the team, we’re working through all queries one by one and will get back to you soon. If it’s urgent, please let us know. We appreciate your patience!

Hi @sccolbert /sessions/{session_id}/runs this endpoint does return the events array which contains the exact order of the events as they occurred during streaming. We also use this same endpoint in our app to load a run in the chat page . Can you tell me more about your use case , so that i can understand your query better? Get Session Runs - Agno Get Run by ID - Agno

Hi @monalisha and thanks for responding!

Sure. Here’s an example prompt that you can run against this simple agent to reproduce:

db = SqliteDb(db_file="agno.db")


claude_agent = Agent(
    db=db,
    name='Claude',
    model=OpenRouter(id='anthropic/claude-sonnet-4.5'),
    tools=[HackerNewsTools()],
    markdown=True,
    add_history_to_context=True,
    num_history_runs=3,
    enable_agentic_memory=True
)

Here’s the prompt to give it:

write a limerick, then fetch the top hacker news story, then remember i like that topic, then summarize the story

It will give you a response similar to this image below. The red arrow shows the location where the ToolCall* events were interleaved within the RunContent events when using SSE streaming.

However, if you load that session from session/{session_id}/runs here is what the events field looks like. You can see that all of the RunContent events are removed, and we only get the single RunContentCompleted event, which doesn’t maintain the interleaving of the tool calls.

Hi @sccolbert , I tested this on my end and it seems to be correct . In my /runs call when its streaming, the event order is RunStarted–>ToolCallStarted–>ToolCallCompleted –> RunContent –> RunContentCompleted —> RunCompleted and hence when i load the run , the tool calls are not interleaved and you get the content in the `RunContentCompleted` event. Basically the tool calls happen at the start .To test this , you can check the order in your streaming call , that is the /runs call . Let me know if the order is different in the streaming call .