<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ran Shemtov</title>
    <description>The latest articles on DEV Community by Ran Shemtov (@ran_st).</description>
    <link>https://dev.to/ran_st</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3259932%2Fcaaad910-e0c6-4a23-b3e4-972a3c4f6fed.png</url>
      <title>DEV Community: Ran Shemtov</title>
      <link>https://dev.to/ran_st</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ran_st"/>
    <language>en</language>
    <item>
      <title>Agent Streams Are a Mess. Here's How We Got Ours to Make Sense</title>
      <dc:creator>Ran Shemtov</dc:creator>
      <pubDate>Thu, 19 Jun 2025 13:36:07 +0000</pubDate>
      <link>https://dev.to/ran_st/agent-streams-are-a-mess-heres-how-we-got-ours-to-make-sense-2f3d</link>
      <guid>https://dev.to/ran_st/agent-streams-are-a-mess-heres-how-we-got-ours-to-make-sense-2f3d</guid>
      <description>&lt;p&gt;I'm Ran, one of the people behind CopilotKit, a developer tool for building AI-native interfaces. Over the past year, we've spent a lot of time wiring agent frameworks into real frontends. stitching tool calls, tracking partial updates, and guessing when messages end. At some point, we realized we couldn't keep patching forever.&lt;br&gt;
Here's why, and how we started standardizing instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  💬 The Problem With Agent Streams
&lt;/h2&gt;

&lt;p&gt;Agent frameworks are growing fast. But they're all speaking different stream dialects.&lt;br&gt;&lt;br&gt;
Some emit partial deltas. Others send state snapshots.&lt;br&gt;&lt;br&gt;
Some give you raw tool call fragments. Others just dump the whole state tree.&lt;br&gt;&lt;br&gt;
None of them tell your UI what's actually happening, at least not in a way that's consistent or structured.&lt;/p&gt;

&lt;p&gt;So every time you integrate a new framework, you end up doing the same thing:&lt;br&gt;&lt;br&gt;
guessing what the stream means, stitching events together by hand, and hoping your frontend logic doesn't fall apart the next time the backend changes.&lt;/p&gt;

&lt;p&gt;If your product is tightly coupled to a framework's quirks, switching runtimes means rewriting everything downstream.&lt;br&gt;&lt;br&gt;
And if you're consuming the stream directly in your UI, the pain is even sharper.&lt;br&gt;&lt;br&gt;
You can keep patching. Or you can start defining what the stream should look like.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔌 We Didn't Plan to Build a Spec
&lt;/h2&gt;

&lt;p&gt;I'm one of the people behind CopilotKit, and for a while, my job boiled down to this: take whatever stream the agent framework gave us, and try to make the frontend behave.&lt;/p&gt;

&lt;p&gt;Tool calls came in fragments. Messages had no clear end.&lt;br&gt;&lt;br&gt;
Sometimes state updated silently, sometimes it didn't.&lt;/p&gt;

&lt;p&gt;We built logic to detect what kind of event was coming through, track it across updates, and do our best to keep the UI in sync.&lt;/p&gt;

&lt;p&gt;LangGraph was a good example. It's flexible, powerful, and emits a raw stream that gives you everything, except clear semantics.&lt;/p&gt;

&lt;p&gt;To support it, we wrote a stream adapter that watched every chunk and tried to reconstruct intent in real time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was this a tool call? If yes, was it starting or midway through?&lt;/li&gt;
&lt;li&gt;If it had a name but no arguments, we assumed it was the beginning.&lt;/li&gt;
&lt;li&gt;If it had arguments, we guessed it was the middle.&lt;/li&gt;
&lt;li&gt;If the next chunk didn't fit either, maybe that meant it ended.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same with messages. We'd accumulate content until a new event looked unrelated, then we'd assume the message was complete.&lt;/p&gt;

&lt;p&gt;Tool call arguments? We had to track them manually and hope they lined up with the right context.&lt;/p&gt;

&lt;p&gt;It worked for a while. Then bugs started to show up.&lt;br&gt;&lt;br&gt;
Unclosed tool calls. Missing arguments. Messages that never finalized.&lt;/p&gt;

&lt;p&gt;Users reported them. We patched things. Then broke something else.&lt;/p&gt;

&lt;p&gt;Each framework brought new quirks. Each bug brought more glue code.&lt;/p&gt;

&lt;p&gt;And every time we thought "maybe this one is stable now," a small change upstream broke everything again.&lt;/p&gt;

&lt;p&gt;At some point, we realized we weren't just adapting streams.&lt;br&gt;&lt;br&gt;
We were inventing structure. So we wrote it down.&lt;/p&gt;

&lt;p&gt;Not as a one-off adapter, but as a shared format anyone could use.&lt;br&gt;&lt;br&gt;
Something that defined how these streams should actually behave when they reach the UI.&lt;br&gt;&lt;br&gt;
That became &lt;strong&gt;AG-UI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🪄 Enter AG-UI: The Adapter That Actually Adapts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AG-UI&lt;/strong&gt;, at its core, is a protocol.&lt;/p&gt;

&lt;p&gt;It defines a common structure for how agent runtimes can emit events, and how frontends can consume them.&lt;/p&gt;

&lt;p&gt;It's not a library, and it doesn't tell you how to build your agent.&lt;br&gt;&lt;br&gt;
It just describes the shape of the data that moves between runtime and UI.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;br&gt;&lt;br&gt;
Agent frameworks can stream whatever they want internally, but when it comes to the frontend, they emit a consistent set of event types.&lt;/p&gt;

&lt;p&gt;These include things like when a message starts, when a tool is called, or when shared state updates.&lt;/p&gt;

&lt;p&gt;Once you have that structure, the frontend doesn't need to guess what's happening.&lt;br&gt;&lt;br&gt;
You can build components that listen for specific events and respond accordingly.&lt;br&gt;&lt;br&gt;
And you can swap out the agent backend without rewriting the UI every time.&lt;/p&gt;

&lt;p&gt;That's the role AG-UI plays. It sits between your agent runtime and your React tree, and gives both sides something to agree on.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 How AG-UI Works in Practice
&lt;/h2&gt;

&lt;p&gt;AG-UI defines a set of structured events that describe what's happening during an agent run: in terms of message flow, tool usage, shared state and more.&lt;/p&gt;

&lt;p&gt;Some of these events map directly to what frameworks already emit. Others, like &lt;code&gt;STATE_SNAPSHOT&lt;/code&gt;, represent higher-level behaviors that aren't always explicitly available in the raw stream, but make sense to the "Agentic Experience" (the term I use to describe usage of AI Agents that is made for humans to work with).&lt;/p&gt;

&lt;p&gt;There are two ways these events get produced:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Native support
&lt;/h3&gt;

&lt;p&gt;Some frameworks can choose to emit AG-UI events directly. This means they adopt the event structure internally and produce streams that conform to the spec out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Adapter-based support
&lt;/h3&gt;

&lt;p&gt;When native support isn't available, a translation layer can be created. Typically as a subclass of AG-UI's abstract &lt;code&gt;Agent&lt;/code&gt; class. Each agent implementation (e.g. &lt;code&gt;LangGraphAgent&lt;/code&gt;) wraps the native stream and transforms it into AG-UI events in real time.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Agent&lt;/code&gt; base class provides a shared interface with methods and properties like &lt;code&gt;run()&lt;/code&gt;, &lt;code&gt;runId&lt;/code&gt;, &lt;code&gt;messages&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt;, and more. The &lt;code&gt;run()&lt;/code&gt; method returns an observable stream of AG-UI events, no matter which backend is used.&lt;/p&gt;

&lt;p&gt;Once this structure is in place, the frontend doesn't need to know or care which framework is running behind the scenes. It just listens to a clean stream of well-defined events.&lt;/p&gt;

&lt;h2&gt;
  
  
  📑 The Event Types That Make Up an Agent Run
&lt;/h2&gt;

&lt;p&gt;AG-UI includes a small, focused set of event types designed to support most real-time agent interactions:&lt;/p&gt;

&lt;h3&gt;
  
  
  🗨️ Message Events
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TEXT_MESSAGE_START&lt;/code&gt;: A message has begun streaming (e.g. assistant starts typing).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TEXT_MESSAGE_END&lt;/code&gt;: The message has completed. Useful for locking in output or triggering animations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ Tool Call Lifecycle
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TOOL_CALL_START&lt;/code&gt;: A tool is being invoked. Includes metadata like &lt;code&gt;toolCallId&lt;/code&gt; and &lt;code&gt;toolCallName&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TOOL_CALL_ARGS&lt;/code&gt;: The arguments being passed to the tool. Enables live rendering of param inputs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TOOL_CALL_END&lt;/code&gt;: The tool call has returned. Includes the result payload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tool event shares the same &lt;code&gt;toolCallId&lt;/code&gt; so the frontend can track the call as a lifecycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 State and Snapshot Events
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;STATE_SNAPSHOT&lt;/code&gt;: A structured snapshot of current state, useful for syncing or debugging.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE_SNAPSHOT&lt;/code&gt;: A full or partial message state reconstruction. Handy when full messages aren't emitted explicitly by the backend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧩 Custom Events
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CUSTOM&lt;/code&gt;: Lets you define custom signals not covered by the core spec, like &lt;code&gt;"PredictState"&lt;/code&gt;. These can still be handled uniformly by shared UI components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these events form a stable contract between agent logic and UI behavior, making it possible to build reusable components that work across frameworks, without needing to reverse-engineer every stream format from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌱 Ecosystem &amp;amp; Adoption: Growing the Standard
&lt;/h2&gt;

&lt;p&gt;AG-UI seems to resonate with the people actually building with agents.&lt;/p&gt;

&lt;p&gt;We've seen everything from community-built adapters, to blog posts, LinkedIn threads, and YouTube videos.&lt;/p&gt;

&lt;p&gt;It's already integrated with several agent runtimes, either natively or through adapters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ LangGraph&lt;/li&gt;
&lt;li&gt;✅ Mastra&lt;/li&gt;
&lt;li&gt;✅ Agno&lt;/li&gt;
&lt;li&gt;✅ Vercel AI SDK&lt;/li&gt;
&lt;li&gt;✅ AG2&lt;/li&gt;
&lt;li&gt;✅ LlamaIndex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some emit AG-UI events directly. Others, like LangGraph, are supported via wrapper classes that map their native stream into structured events using the AG-UI spec.&lt;/p&gt;

&lt;p&gt;Adapters are easy to write. If a framework emits a stream, it can be made to emit AG-UI. No changes to the runtime required.&lt;/p&gt;

&lt;p&gt;The SDKs in JavaScript and Python are small and purpose-built to sit between an agent backend and a frontend UI.&lt;/p&gt;

&lt;p&gt;Once that layer is in place, everything downstream gets simpler: shared components, dev-tools, run replay, even multi-agent orchestration.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Finalized Conclusion
&lt;/h2&gt;

&lt;p&gt;Building UIs on top of agent frameworks has meant writing the same stream logic over and over.&lt;/p&gt;

&lt;p&gt;AG-UI came out of trying to stop that, first for ourselves, then for others.&lt;/p&gt;

&lt;p&gt;If you're building an AI product, and you're tired of translating agent internals into frontend updates, AG-UI is worth a look.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Curious? Start here:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check out the docs
&lt;/li&gt;
&lt;li&gt;Try the TypeScript or Python SDKs
&lt;/li&gt;
&lt;li&gt;Join the discussions
&lt;/li&gt;
&lt;li&gt;Or just build your own adapter and emit events in your own stack. The protocol is open and extensible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The protocol is open. The stream is yours to shape.&lt;/p&gt;

</description>
      <category>agentframework</category>
      <category>ai</category>
      <category>opensource</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
