Skip to content

fix(tui): drop cross-session events during null-sid switch window (#51058) - #53928

Closed
yingliang-zhang wants to merge 4 commits into
NousResearch:mainfrom
yingliang-zhang:main
Closed

fix(tui): drop cross-session events during null-sid switch window (#51058)#53928
yingliang-zhang wants to merge 4 commits into
NousResearch:mainfrom
yingliang-zhang:main

Conversation

@yingliang-zhang

Copy link
Copy Markdown
Contributor

Problem

Closes #51058 (TUI/Desktop session mix-up after context compression / reconnect resumes the wrong chat).

When two TUI sessions are active simultaneously and one undergoes context compression (or any session switch occurs), the other session's messages can bleed into the active view. A full state.db audit confirms the data layer is correctly isolated — the bug is purely in the TUI frontend event filtering layer.

Root cause

The event filter in createGatewayEventHandler.ts used sid && as a guard:

if (ev.session_id && sid && ev.session_id !== sid && !ev.type.startsWith('gateway.')) {
  return
}

When sid is null (during resetSession() in activateLiveSession / resumeById), sid && evaluates to false, bypassing the entire filter. ALL events from ALL live sessions pass through, causing cross-session content bleed.

Fix

Change sid && to (!sid || so that when sid is null, ALL non-gateway session events are dropped instead of let through:

if (ev.session_id && (!sid || ev.session_id !== sid) && !ev.type.startsWith('gateway.')) {
  return
}

This closes the race window: during session switch, no session-specific events are processed until the new sid is set.

Safety

  • gateway.* events always pass (they carry no session-scoped data)
  • Events without session_id always pass (session-agnostic)
  • At startup (before any session is active), only gateway events are processed — the auto-resume flow gets session info from the RPC response, not from events
  • The sid: null window in resetSession() is synchronous (nanostores), so no events are actually processed during it in normal execution — this fix is belt-and-suspenders defense for edge cases (React flushSync, microtask boundaries)

Verification

  • tsc --noEmit passes
  • node scripts/build.mjs passes
  • DB audit confirmed: resolve_resume_session_id, get_compression_tip, get_messages_as_conversation, and list_sessions_rich all return correctly isolated data — the bug was exclusively in the frontend filter

no_agent cron jobs short-circuit run_job() before any SessionDB work,
so they never produce the cron_{job_id}_{timestamp} session row that the
run-history endpoint (GET /api/cron/jobs/{id}/runs, backed by
SessionDB.list_cron_job_runs) is built from. Manually triggering such a
job from the Desktop GUI gives zero feedback: no running indicator, no
run record, no output — the script runs fine, the GUI just can't see it.

Fix: in the no_agent branch of run_job(), create the run session
(cron_{job_id}_{ts}, source='cron') BEFORE executing the script so the
runs endpoint's is_active computation yields a running indicator for
in-flight manual triggers. After execution, persist the outcome doc as
an assistant message, title the session, and end_session. Covers all
four exit paths: success, script failure, empty-stdout silent run, and
wakeAgent=false silent run.

Best-effort: a missing/broken state store degrades to the old no-record
behaviour and never blocks the script run. The no_agent cost contract
is preserved — run_agent/AIAgent are still never imported on this path.

Supersedes NousResearch#44087 (same approach, rebased on current main).
Fixes NousResearch#44080, NousResearch#42433.
… consecutive user/user turns

Local fix (not yet PR'd). After compression, the preserved todo list was
appended as a standalone user message; when the compressed transcript
already ended with a user message this produced consecutive user/user
turns — a content-ordering violation some providers reject. Now fold the
snapshot into the trailing user message (blank-line separated) instead of
appending a second standalone user turn. Falls back to append when the tail
is empty, non-user, or structured (list) content so image/tool parts are
not corrupted.
…usResearch#51058)

The event filter in createGatewayEventHandler.ts used `sid &&` as a
guard, which evaluated to false when sid was null (during resetSession()
in activateLiveSession/resumeById). This bypassed the ENTIRE filter,
allowing events from other live sessions to bleed into the active view.

Change `sid &&` to `(!sid ||` so that when sid is null, ALL non-gateway
session events are dropped instead of let through. This closes the race
window where two concurrent live sessions can cross-contaminate.

Repro: two TUI sessions active simultaneously, one undergoes context
compression (rotation mode). After compression, the other session's
content appears in the first session's view. DB audit confirms data is
correctly isolated — the bug is purely in the TUI event filtering layer.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cron Cron scheduler and job management comp/tui Terminal UI (ui-tui/ + tui_gateway/) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state P2 Medium — degraded but workaround exists labels Jun 28, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Heads up for reviewers: the title/body describe a 1-line TUI event-filter fix (ui-tui/src/app/createGatewayEventHandler.ts, closes #51058), but the diff also bundles three undisclosed core changes:

Recommend splitting into focused PRs (or at least correcting the title/scope) so each change can be reviewed on its own. Related: #48525, #48524, #51058.

@yingliang-zhang

Copy link
Copy Markdown
Contributor Author

Closing — this PR was opened from a main branch that bundled 3 other fixes (compression todo merge, no_agent cron sessions, session delete lineage). Reopening as a focused PR with only the TUI event-filter change: the new PR will be linked shortly. The other fixes have their own dedicated PRs (#53890 for compression todo merge; the cron and session-lineage fixes were duplicates of #44087 and #48525 respectively, now closed).

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

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cron Cron scheduler and job management 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.

TUI/Desktop session mix-up after context compression / reconnect resumes the wrong chat

2 participants