ag-ui-protocol / ag-ui-protocol/ag-ui

[Question]: How should clients hydrate/render a long thread when MESSAGES_SNAPSHOT is a full replace?

未關閉
#2,475 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
主要語言
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 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。