Skip to content

Python: Python AG-UI: terminal MessagesSnapshotEvent always rewrites full transcript; prefer incremental-only option #7807

Description

@likebean

Summary

AgentFrameworkAgent (python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py) emits a terminal MessagesSnapshotEvent at the end of most runs whenever there is accumulated text, tool calls/results, or reasoning. That snapshot is built as request messages + this turn’s assistant/tool output — i.e. a full transcript replacement, not a delta.

For clients such as @assistant-ui/react-ag-ui, MESSAGES_SNAPSHOT triggers a full replace of the local message list (applyExternalMessages). That forces the AG-UI request payload (and thus the snapshot) to carry the entire UI history on every turn, even when the app already persists history server-side via Agent Framework HistoryProvider.

This is costly and easy to get wrong. Incremental AG-UI events (TEXT_MESSAGE_*, TOOL_CALL_*, …) already describe the turn; a mandatory full snapshot at the end is redundant for many apps and actively harmful for HistoryProvider-based designs.

Current behavior

Near end of run_agent_stream:

should_emit_snapshot = (
    flow.pending_tool_calls or flow.tool_results or flow.accumulated_text or flow.reasoning_messages
)
...
snapshot_event = _build_messages_snapshot(flow, snapshot_messages)  # snapshot_messages ≈ request messages
...
yield snapshot_event  # unless A2UI / predictive suppressions

So for a normal chat turn with text, the client always receives a terminal full-list snapshot.

Notably, the code already suppresses the terminal snapshot for A2UI runs, with a comment that other AG-UI frameworks often emit no terminal snapshot and that the next turn’s history can be reconstructed from streamed events. That same reasoning applies to plain chat + HistoryProvider.

Why this is painful

  1. UI wipe / truncation bugs
    If the server trims the AG-UI messages field to “latest user only” (to avoid duplicating HistoryProvider context for the model), the terminal snapshot also becomes “latest user + this assistant reply”. assistant-ui then replaces the panel and older bubbles disappear, even though SQLite/history still has the full original transcript.

  2. Bandwidth & coupling
    Every successful turn re-sends the whole conversation in SSE, even though the client already has prior messages and already applied streaming deltas.

  3. Split-brain with AF persistence
    Apps that intentionally put durability in HistoryProvider / SessionStateProvider (no AG-UI Thread Snapshot store) still get CopilotKit-style full-list reconciliation forced on them.

Expected / proposed direction

Please consider one or more of:

  1. Opt-out / opt-in for terminal MessagesSnapshotEvent on AgentFrameworkAgent / AgentConfig (e.g. emit_messages_snapshot: bool = True for backward compatibility, or default False for non-snapshot-store setups).

  2. Document clearly that MESSAGES_SNAPSHOT is a full authoritative list, so request messages must be the full UI transcript if the client applies the event as a replace — and that HistoryProvider users should either keep the full client transcript in the AG-UI payload for snapshot purposes, or disable the terminal snapshot.

  3. Longer-term: support an incremental end-of-turn signal (only new messages / ids for this run) instead of requiring a full list, for clients that already own history locally or via AF providers.

Comparison

Other AG-UI stacks (e.g. Agno’s AG-UI path) often rely on streaming deltas + a separate session/history API and do not emit a terminal full MESSAGES_SNAPSHOT for ordinary chat. MAF’s A2UI path already skips the terminal snapshot for ordering reasons; plain chat would benefit from the same flexibility.

Environment

  • Package: agent-framework-ag-ui (Python)
  • Client: @assistant-ui/react-ag-ui + HttpAgent (applies MESSAGES_SNAPSHOT as full replace)
  • Persistence: AF HistoryProvider (no AG-UI snapshot_store)

Questions for maintainers

  • Is the terminal full snapshot considered required by AG-UI / CopilotKit compatibility, or is it a convenience that can be optional?
  • Would you accept a PR adding emit_messages_snapshot (or similar) on the AG-UI agent config, defaulting to current behavior?

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonUsage: [Issues, PRs], Target: PythontriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions