Skip to content

Restore stored history when it is ahead of the join snapshot - #334

Open
nicklaunches wants to merge 1 commit into
CopilotKit:mainfrom
nicklaunches:pr/restore-stored-history
Open

Restore stored history when it is ahead of the join snapshot#334
nicklaunches wants to merge 1 commit into
CopilotKit:mainfrom
nicklaunches:pr/restore-stored-history

Conversation

@nicklaunches

Copy link
Copy Markdown

The problem

Opening a channel does two things: it loads the thread from the durable store, and it joins the
realtime gateway. The join replaces the agent's messages with the gateway's own snapshot of that
thread, and that snapshot can lag the store.

When it did, the transcript came back missing the end of the last conversation: a turn that had
finished, been persisted, and been answered in full, gone from the view. Stored history was only
restored into an empty agent, a guard written to protect a message typed while history was still
loading, so the stale snapshot won every time. The gap was there on every reload, and nothing on
screen explained it, since there was no unreadable count to mark the missing messages.

What changed

ChannelChat applies the stored thread when the store is ahead of the agent: it holds more messages
than the agent does, and every message the agent holds is in the store.

The second half of that condition is exactly what the old empty-agent check was protecting, stated
directly rather than by proxy. A message typed while history was loading has not been persisted yet,
so it is not in the store, so the store is not applied and the message is not overwritten. A run
still streaming has messages the store has not seen, so its snapshot is never rolled back either.

How it was verified

  • bun run --cwd app typecheck clean.
  • bun test app/tests/channel-event-patch.test.ts app/tests/channel-unread.test.ts app/tests/chat-messages.test.ts app/tests/repair-history.test.ts gives 35 pass, 0 fail.
  • bunx biome check app/src/components/channels/channel-chat.tsx clean.

🤖 Generated with Claude Code

Opening a channel joins the realtime gateway, and the join replaces the
agent's messages with the gateway's snapshot of the thread. That snapshot
can lag the durable store, so a turn that had finished, been persisted and
answered in full came back without its last exchange. History was restored
only into an empty agent, so the stale snapshot stayed for good: the gap
was there on every reload, with no unreadable count to explain it.

The store now wins when it holds more than the agent does and holds
everything the agent already has. The second half is the guard this
replaces: a message typed while history was loading is not in the store
yet, and a run still streaming has messages the store has not seen, so
neither is rolled back.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaHWJ1niprhBc5NzJ9pxme
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant