Skip to content

fix(tui): refresh resumed history before prompt submit - #91523

Open
Biotrioo wants to merge 3 commits into
NousResearch:mainfrom
Biotrioo:fix/91508-refresh-resumed-history-on-submit
Open

Biotrioo wants to merge 3 commits into
NousResearch:mainfrom
Biotrioo:fix/91508-refresh-resumed-history-on-submit

Conversation

@Biotrioo

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a stale-model-context race for resumed Desktop/TUI sessions whose durable transcript is advanced by another process, especially cron runs.

A cold resume hydrates session["history"] once. If cron continues appending to the same state.db session afterwards, Desktop can show the newer transcript through its persisted-display refresh while the next prompt.submit still feeds the model the old open-time snapshot.

Fix

  • Add a pre-dispatch guard for prompt.submit on resumed sessions (resume_session_id).
  • Wait for the initial deferred resume hydration to settle before comparing state.
  • Read the model projection from the session's profile-aware owning DB.
  • Adopt durable history only when it has grown beyond the in-memory snapshot.
  • Never overwrite an active turn, a shorter/equal durable projection, or a local history mutation that races the DB read.
  • Sanitize the refreshed replay history through the same path used by resume hydration.

This keeps the change monotonic and avoids letting an external refresh clobber locally-owned turn state.

Related Issue

Fixes #91508

Regression coverage

tests/tui_gateway/test_prompt_submit_durable_refresh.py covers:

  • the reported 12-message open-time snapshot vs 37-message completed cron transcript, asserting the prompt handler sees all 37 rows;
  • new/non-resumed sessions remain untouched;
  • shorter durable projections never replace memory;
  • a local history mutation racing the DB read wins;
  • busy sessions remain owned by the active turn and are not refreshed.

Type of Change

  • Bug fix (non-breaking change)

Checklist

@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 21, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

  1. tui_gateway/prompt_history_sync.py:60-77 — ready.wait(timeout=30.0) blocks the calling thread for up to 30 seconds inside prompt.submit dispatch. Why it matters: if handlers run on a shared dispatch loop/thread, one stuck hydration worker makes every prompt across all sessions wait behind the same 30s window, turning a per-session hiccup into a gateway-wide stall; the warning log fires only after the full wait. Suggestion: confirm the dispatch threading model; if shared, lower the bound (a resumed session that hasn't hydrated within ~2-5s is unlikely to finish promptly) or make the timeout configurable and emit the log immediately on entry when ready isn't yet set.

  2. tui_gateway/prompt_history_sync.py:146-147 — The adoption guard is purely length-monotonic (len(durable_history) <= len(start_history)). Why it matters: an external writer that compacts history (summarization, tool-noise pruning) produces a newer durable projection with fewer rows, which will now never be adopted — the stale open-time prefix persists silently, the exact class of staleness this PR targets; notably the fallback read already requests include_row_ids=True, so newer-but-shorter is detectable. Suggestion: adopt when the durable tail's max row id exceeds the snapshot's (or last timestamp is newer) even if shorter, keeping the length rule as the conservative fallback.

  3. tui_gateway/method_ctx.py:51-59 — Wrapping is unconditional on each install(), so a re-install/reload of the method table double-wraps prompt.submit, doubling the durable read (and the lock churn) on every subsequent submit. Why it matters: silent per-submit cost growth that's hard to notice in profiling. Suggestion: tag the wrapper (wrapped._hermes_history_refresh = True) and skip wrapping when the incoming real already carries the marker.

  4. tui_gateway/prompt_history_sync.py:118-141 — Positive: the snapshot-under-lock → read-outside-lock → revalidate(version AND value AND running)-under-lock pattern is textbook and the "legacy in-place mutation forgets to bump version" catch via value comparison is a thoughtful touch. Test gap worth closing: no coverage for the two early-return paths — hydration-timeout (30s) and resume_history_error — which currently guard real behavior only by inspection.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Desktop — replying to a cron session opened mid-run feeds the agent a frozen open-time transcript snapshot

3 participants