Summary
When /compress triggers a session fork (compression ends the current SessionDB session and creates a new continuation session), the TUI frontend does not update its local ctx.sid to match the new session ID. The gateway correctly syncs session_key via _sync_session_key_after_compress() and emits session.info with the updated session_id, but the TUI handler only patches info and usage without updating the active session ID.
Impact
After compression, the TUI display goes blank because:
- Gateway session key → new session ID
- TUI
ctx.sid → stale (pointing to ended parent session)
Subsequent RPC calls (e.g., message submission, /image, /model) use the stale ctx.sid, targeting the closed parent session.
Reproduction
- Start a TUI session with enough messages to trigger auto-compression (or manually run
/compress)
- Compression completes, session ID rotates in SessionDB
- TUI display shows blank / new session state instead of compressed history
agent.log confirms compression succeeded correctly
Root Cause
File: ui-tui/src/app/createGatewayEventHandler.ts (L389-401)
The session.info handler updates info, status, usage, and intro messages — but does not update sid in the UI state. When info.session_id differs from the current state.sid, the TUI should sync to the new session.
Suggested Fix
In the session.info handler, check if info.session_id has changed and update the UI state accordingly:
case 'session.info': {
const info = ev.payload
patchUiState(state => {
const sidChanged = info.session_id && info.session_id !== state.sid
return {
...state,
info,
sid: sidChanged ? info.session_id : state.sid,
status: state.status === 'starting agent…' ? 'ready' : state.status,
usage: info.usage ? { ...state.usage, ...info.usage } : state.usage
}
})
setHistoryItems(prev => prev.map(m => (m.kind === 'intro' ? { ...m, info } : m)))
return
}
Workaround
Use /resume <new_session_id> or /new to re-sync the TUI to the correct session after compression.
Environment
- Hermes Agent v0.15.1 (2026.5.29)
- macOS (26.5)
- Main model: Qwen/Qwen3.6-27B-FP8 @ custom (SGLang)
- Compression:
auxiliary.compression.provider: auto
Regression Source
Introduced in v2026.4.23 by commit a8e0a1148 (perf(tui): async session.create).
This commit made session.create async: instead of returning sid synchronously, it returns immediately and pushes the full session info via session.info event. The TS handler was written to only patch info, status, usage, and intro messages — not sid.
In the initial session creation flow this works fine (sid is set from session.create response). However, when /compress forks the session (added in v2026.4.30 via fc7f55f49), the session.info event carries the new session_id but the handler ignores it, leaving ctx.sid stale.
Timeline:
v2026.4.23: a8e0a1148 — async session.create (latent bug)
v2026.4.30: fc7f55f49 — /compress + _sync_session_key_after_compress (triggers the bug)
v2026.5.7: 3b750715a — fixes lazy session regressions (bug remains)
Summary
When
/compresstriggers a session fork (compression ends the current SessionDB session and creates a new continuation session), the TUI frontend does not update its localctx.sidto match the new session ID. The gateway correctly syncssession_keyvia_sync_session_key_after_compress()and emitssession.infowith the updatedsession_id, but the TUI handler only patchesinfoandusagewithout updating the active session ID.Impact
After compression, the TUI display goes blank because:
ctx.sid→ stale (pointing to ended parent session)Subsequent RPC calls (e.g., message submission,
/image,/model) use the stalectx.sid, targeting the closed parent session.Reproduction
/compress)agent.logconfirms compression succeeded correctlyRoot Cause
File:
ui-tui/src/app/createGatewayEventHandler.ts(L389-401)The
session.infohandler updatesinfo,status,usage, and intro messages — but does not updatesidin the UI state. Wheninfo.session_iddiffers from the currentstate.sid, the TUI should sync to the new session.Suggested Fix
In the
session.infohandler, check ifinfo.session_idhas changed and update the UI state accordingly:Workaround
Use
/resume <new_session_id>or/newto re-sync the TUI to the correct session after compression.Environment
auxiliary.compression.provider: autoRegression Source
Introduced in v2026.4.23 by commit
a8e0a1148(perf(tui): async session.create).This commit made
session.createasync: instead of returningsidsynchronously, it returns immediately and pushes the full session info viasession.infoevent. The TS handler was written to only patchinfo,status,usage, and intro messages — notsid.In the initial session creation flow this works fine (
sidis set fromsession.createresponse). However, when/compressforks the session (added in v2026.4.30 viafc7f55f49), thesession.infoevent carries the newsession_idbut the handler ignores it, leavingctx.sidstale.Timeline:
v2026.4.23:a8e0a1148— async session.create (latent bug)v2026.4.30:fc7f55f49—/compress+_sync_session_key_after_compress(triggers the bug)v2026.5.7:3b750715a— fixes lazy session regressions (bug remains)