From 92f35fab19470e9e76fdda7f6b5a6fc0f04ebc55 Mon Sep 17 00:00:00 2001 From: alt-glitch Date: Tue, 9 Jun 2026 05:08:57 +0000 Subject: [PATCH] opentui(v5b/item5): track active session for the post-quit resume epilogue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The launcher (hermes_cli/main.py _print_tui_exit_summary) reads HERMES_TUI_ACTIVE_SESSION_FILE to print 'Resume this session with…' on exit. The Ink TUI writes the current session id there on every session change (useSessionLifecycle.writeActiveSessionFile); the native engine never did, so after a /session switch the launcher fell back to the INITIAL launch session and showed resume info for the wrong session (the reported leak). Now writeActiveSession() writes {session_id} on session.create AND inside resumeInto (every /session switch), mirroring Ink. Verified live: file shows the created session, then updates to the switched-to session. 85 pass. --- ui-tui-opentui-v2/src/entry/main.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ui-tui-opentui-v2/src/entry/main.tsx b/ui-tui-opentui-v2/src/entry/main.tsx index 62510590ef6..ca39f9461d9 100644 --- a/ui-tui-opentui-v2/src/entry/main.tsx +++ b/ui-tui-opentui-v2/src/entry/main.tsx @@ -21,6 +21,7 @@ */ import { render } from '@opentui/solid' import { Deferred, Duration, Effect } from 'effect' +import { writeFileSync } from 'node:fs' import { readClipboardImage, writeClipboard } from '../boundary/clipboard.ts' import { GatewayService, type GatewayServiceShape } from '../boundary/gateway/GatewayService.ts' @@ -60,8 +61,27 @@ const QUIT_WINDOW_MS = 3_000 * mapResumeHistory). Shared by the launch bootstrap and the session switcher. * Timed (rpc_ms / hydrate_ms) for the resume profile. */ +/** + * Record the CURRENT session id in `HERMES_TUI_ACTIVE_SESSION_FILE` (item #5). + * The launcher reads this on exit to print the right "Resume this session with…" + * epilogue (hermes_cli/main.py `_print_tui_exit_summary`). The Ink TUI writes it on + * every session change (useSessionLifecycle.writeActiveSessionFile); the native + * engine must too, or the launcher falls back to the INITIAL launch session and + * shows resume info for the wrong session after a `/session` switch. + */ +const writeActiveSession = (sid: string | undefined) => { + const file = process.env.HERMES_TUI_ACTIVE_SESSION_FILE + if (!file || !sid) return + try { + writeFileSync(file, JSON.stringify({ session_id: sid }), { mode: 0o600 }) + } catch (cause) { + getLog().warn('bootstrap', 'active-session-file write failed', { cause: String(cause) }) + } +} + const resumeInto = (gateway: GatewayServiceShape, store: SessionStore, sid: string, cols: number) => Effect.gen(function* () { + writeActiveSession(sid) // the session we're switching to is now the active one (#5) store.beginBuffer() const t0 = Date.now() const resumed = yield* gateway.request<{ messages?: unknown; info?: Record }>('session.resume', { @@ -127,6 +147,7 @@ const bootstrapSession = (gateway: GatewayServiceShape, store: SessionStore, inp return } if (created?.info) store.applyInfo(created.info) + writeActiveSession(sid) // record the new session for the launcher's exit epilogue (#5) log.info('bootstrap', 'session created', { sid }) }