You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Bug][Desktop] Continuing a gateway-created session from desktop sends only the first N messages to the model — UI shows full transcript, request body truncated (stale live-history snapshot) #81951
When a session is created and processed by the gateway (messaging platform) and the user then continues the same session from the desktop app, the model receives only the first N messages of the conversation plus the new user message — the middle of the transcript is silently missing from the API request, with no summary and no compression involved. The desktop UI shows the full transcript (it reads from state.db), but the request body sent to the model provider is truncated.
Data is not lost (all rows are intact in state.db) — this is a stale in-memory session["history"] snapshot in the hermes serve backend that never caught up with the gateway's concurrent writes.
Environment
macOS, Hermes desktop app (Electron, backend via hermes serve)
Messaging-platform gateway (a reply-quote is routed as a new thread session)
Model with 1M context window, compression threshold ~512K — compression was never triggered (session was only ~67K tokens)
User sends a reply-quote on the messaging platform; it is routed as a new thread session; the gateway creates the session.
01:47–01:52
Gateway process handles the session: 44 messages written to state.db, 15 API calls, last request in=66890 tokens (full history). Turn ends normally.
01:54:16
User sends a new message from the desktop app (platform=desktop history=9; log: "Stored system prompt ... has stale runtime identity; rebuilding"). The desktop serve process takes over the gateway-created session.
01:54:36
Desktop's API call #1: in=41547 — ~25K tokens (~35 messages) less than the gateway's last request.
Hard evidence
1. The actual request body captured at the provider gateway (15 messages total; contents elided):
[0] system (stale: carries the pre-update memory snapshot — memory was persisted after the gateway turn)
[1] user (first user turn) <- msg #808
[2] assistant (tool_calls: session_search, skill_view x2) <- msg #809
[3-5] tool x3 <- msgs #810-812
[6] assistant (tool_calls: terminal x3, initial probing) <- msg #813
[7-9] tool x3 (probe outputs) <- msgs #814-816
[10] user (NEW message) <- msg #852
[11-14] assistant + tool (follow-up calls) <- msgs #853-856
The request contains the session's first 9 messages (one complete probing turn) + the new user message + its follow-up tool calls.
The system prompt still carries the pre-update memory snapshot (memory was persisted after the gateway turn).
2. agent.log token accounting
01:52:52 gateway: API call #15 in=66890 (full 44-msg history)
01:54:36 desktop: API call #1 in=41547 (9-msg history + new msg; ~25K tokens missing)
3. state.db is complete
All rows active=1, zerocompacted=1 rows, no summary message, no session rotation (compression locks empty, no child session).
get_messages_as_conversation(<session>) returns the full transcript.
sessions.compression_ineffective_count=39 — compression never actually produced a summary.
Root cause hypothesis
The desktop serve backend registers a live session record (session["history"] in _sessions) while the session is still young (the desktop app watches sessions.changed events and picks up the gateway-created session early — when only the first 9 messages existed in DB). The gateway then writes 35 more messages + updates memory + rebuilds the system prompt, but the desktop live record's history snapshot is never refreshed.
UI display reads from state.db (get_resume_conversations / get_messages_as_conversation) → full transcript shown to the user.
Model inference uses session["history"] (run_kwargs["conversation_history"] = list(history) in tui_gateway/server.py) → stale 9-message snapshot sent to the provider.
Result: the user sees the complete conversation in the desktop UI while the model is blind to the middle 35 messages — and since nothing signals the truncation (no summary, no compaction notice), the model confidently answers from incomplete context. In our case it even invented a plausible-sounding mechanism to explain state changes it couldn't account for, which is how the bug surfaced.
Related
[Bug][Desktop] Session history rolled back on interrupted_during_api_call — 31 messages permanently lost, timestamps batch-written #78010 — same family: desktop serve history snapshot diverges from real state, but that variant is same-process (interrupted_during_api_call → rollback → rows lost from DB). This report is the cross-process variant: gateway writes continue while the desktop live record is stale, so rows survive in DB but never reach the model. Both share the root cause: session["history"] in the serve backend is not kept consistent with the authoritative store.
A session resumed by the desktop backend must present the same conversation to the model as the user sees — i.e. conversation_history should be (re)loaded from state.db (or the live record refreshed on gateway writes / on resume), never a stale early snapshot. At minimum, a mismatch between the live record and the DB (e.g. DB row count > live history length) should force a reload or emit a visible warning instead of silently truncating the provider request.
Summary
When a session is created and processed by the gateway (messaging platform) and the user then continues the same session from the desktop app, the model receives only the first N messages of the conversation plus the new user message — the middle of the transcript is silently missing from the API request, with no summary and no compression involved. The desktop UI shows the full transcript (it reads from state.db), but the request body sent to the model provider is truncated.
Data is not lost (all rows are intact in state.db) — this is a stale in-memory
session["history"]snapshot in thehermes servebackend that never caught up with the gateway's concurrent writes.Environment
hermes serve)Timeline (from agent.log / gateway.log / state.db / provider request log)
in=66890tokens (full history). Turn ends normally.platform=desktop history=9; log: "Stored system prompt ... has stale runtime identity; rebuilding"). The desktop serve process takes over the gateway-created session.in=41547— ~25K tokens (~35 messages) less than the gateway's last request.Hard evidence
1. The actual request body captured at the provider gateway (15 messages total; contents elided):
2. agent.log token accounting
3. state.db is complete
active=1, zerocompacted=1rows, no summary message, no session rotation (compression locks empty, no child session).get_messages_as_conversation(<session>)returns the full transcript.sessions.compression_ineffective_count=39— compression never actually produced a summary.Root cause hypothesis
The desktop
servebackend registers a live session record (session["history"]in_sessions) while the session is still young (the desktop app watchessessions.changedevents and picks up the gateway-created session early — when only the first 9 messages existed in DB). The gateway then writes 35 more messages + updates memory + rebuilds the system prompt, but the desktop live record'shistorysnapshot is never refreshed.get_resume_conversations/get_messages_as_conversation) → full transcript shown to the user.session["history"](run_kwargs["conversation_history"] = list(history)in tui_gateway/server.py) → stale 9-message snapshot sent to the provider.Result: the user sees the complete conversation in the desktop UI while the model is blind to the middle 35 messages — and since nothing signals the truncation (no summary, no compaction notice), the model confidently answers from incomplete context. In our case it even invented a plausible-sounding mechanism to explain state changes it couldn't account for, which is how the bug surfaced.
Related
servehistory snapshot diverges from real state, but that variant is same-process (interrupted_during_api_call → rollback → rows lost from DB). This report is the cross-process variant: gateway writes continue while the desktop live record is stale, so rows survive in DB but never reach the model. Both share the root cause:session["history"]in the serve backend is not kept consistent with the authoritative store.Expected behavior
A session resumed by the desktop backend must present the same conversation to the model as the user sees — i.e.
conversation_historyshould be (re)loaded from state.db (or the live record refreshed on gateway writes / on resume), never a stale early snapshot. At minimum, a mismatch between the live record and the DB (e.g. DB row count > live history length) should force a reload or emit a visible warning instead of silently truncating the provider request.