ag-ui-protocol / ag-ui-protocol/ag-ui
[Question]: How should clients hydrate/render a long thread when MESSAGES_SNAPSHOT is a full replace?
- 主要語言
- Python
- 星號
- 15.9k
- 分支
- 1.4k
- 平均合併
- 1 天 17 小時
- 30 天內合併 PR
- 163
描述
## Context
`MESSAGES_SNAPSHOT` is documented as a **complete** conversation transcript that the frontend uses to establish or refresh message state:
```ts
interface MessagesSnapshotEvent {
type: EventType.MESSAGES_SNAPSHOT
messages: Message[] // Complete array of all messages
}
```
Typical hydrate/reconnect flows emit something like:
`RUN_STARTED` → `STATE_SNAPSHOT` → `MESSAGES_SNAPSHOT` → `RUN_FINISHED`
That is a snapshot (one array), not event-by-event replay of `TEXT_MESSAGE_*` / `TOOL_CALL_*`. Compaction of streaming deltas into a snapshot is already understood.
This is **not** a duplicate of:
- #2159 — paginated retrieval for ADK `POST /agents/state`
- #2186 / #2230 / #1386 — whether `RunAgentInput.messages` must be the full history on each run
- #1160 — listing saved `threadId`s
Those are adjacent. This issue is about **what the snapshot itself is allowed to contain**, and how a chat UI should treat it when a **single thread's tail is already huge**.
## Problem
For a thread with tens of thousands of messages (or fewer messages but large tool payloads / attachments):
1. Shipping the full `messages[]` on hydrate is a large JSON parse + memory hit before any pixel is painted.
2. Many chat UIs want “show the **latest viewport** first, load older messages on scroll-up” (plus a virtualized list so only visible rows are DOM).
3. A naive client that does `setMessages(snapshot.messages)` **replace-all** fights that UX:
- After the user prepends older pages from an app-owned history API, the next `MESSAGES_SNAPSHOT` wipes them.
- If the server windows the snapshot to “last N messages”, anything not in that array looks deleted unless the client keeps a separate `olderPages` store.
- Cutting the window mid tool-call / HITL interrupt breaks pairing, approval resume, and regenerate.
`STATE_SNAPSHOT` + `STATE_DELTA` already exist for large **state**. There is no equivalent incremental/windowed contract for **transcript** snapshots.
## Questions for maintainers and implementers
What is the intended pattern in production?
1. **Is `MESSAGES_SNAPSHOT` required to be the entire durable transcript?** Or may a server emit only a **tail window** (complete turns, including tool pairs / pending interrupts), with older history owned by an application API (`GET /threads/{id}/messages?before=`)?
2. **If a tail window is valid**, how should clients merge?
- Treat snapshot as authority over `liveTail` only, and never let it replace prepended `olderPages`?
- Or is replace-all still the spec, and paginated history must live entirely outside AG-UI?
3. **Frontend rendering:** the array is oldest→newest, but a chat viewport wants the **bottom** first. Is the expected client technique virtualization + stick-to-bottom (`column-reverse` / `scrollToIndex(n-1)`), with AG-UI staying silent on paint order? (Snapshot delivery is not sequential playback, but a non-virtualized `map()` still walks index 0…n-1 and will jank.)
4. **Should the protocol grow a window/cursor** on hydrate (e.g. `limit`, `beforeMessageId`, `hasMore` on the snapshot or a sibling history resource), or is that permanently application state like the thread index in #1160?
5. Any production setups (CopilotKit, LangGraph, ADK, Microsoft Agent Framework AG-UI, custom HttpAgent) that already do last-viewport-first without breaking HITL resume?
## What we are considering (for discussion, not a proposal lock-in)
Split ownership:
- AG-UI snapshot = current **live tail** (complete turns + shared state + interrupt outcome). Hydrate stays small.
- App DB = full archive with cursor pagination. Scroll-up prepends; not a second AG-UI run.
- Client store = `olderPages + liveTail`. New snapshots update `liveTail` only; ids de-dupe overlap.
- Model context is a server-side window/summary; the client does not resend tens of thousands of messages (#2186).
If that contradicts “snapshot messages are the conversation”, we would rather follow the spec than invent a merge model.
Thanks — looking for how people actually ship this, and whether a protocol-level window is wanted or explicitly out of scope.
貢獻指南
評估
這個 Issue 還沒有評估資料。