From 52bedf8c87ed0feebe40c6021076a0f4eed3017d Mon Sep 17 00:00:00 2001 From: Vincenzo Favara Date: Tue, 3 Nov 2026 22:15:23 +0100 Subject: [PATCH] Keep the Claude session Switchboard was started from out of new sessions When Switchboard is started from inside a Claude Code session (a terminal in a session, or a session restarting the app), it inherits CLAUDECODE, CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_CHILD_SESSION and the other variables Claude gives its children, and passed them to every session it started. A CLI started with them takes itself for a child of that old session and writes no transcript of its own, so a new session never got its title, never got a row from the index and could not be resumed. They are now left out of every PTY's environment; the settings a user puts in env stay. --- pty-env.js | 27 +++++++++++++++++++++++++-- test/pty-env.test.js | 16 ++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/pty-env.js b/pty-env.js index 9a7df17c7..d68a08f76 100644 --- a/pty-env.js +++ b/pty-env.js @@ -1,6 +1,6 @@ // Environment for PTY children. // -// Two jobs: +// Three jobs: // // 1. Strip Electron internals that cause nested Electron apps (or node-pty // inside them) to malfunction. @@ -19,6 +19,28 @@ const LOCALE_VARS = ['LC_ALL', 'LC_CTYPE', 'LANG']; +// 3. Forget the Claude Code session Switchboard itself may have been started +// from (a terminal inside a session, or a session restarting the app). +// Claude passes these to every child; a CLI started with them takes itself +// for a child of that old session: it writes no transcript of its own, so +// the new session gets no title, no row and nothing to resume. Settings a +// user puts in env (CLAUDE_CODE_MAX_OUTPUT_TOKENS, …) are kept. +const INHERITED_CLAUDE_SESSION_VARS = new Set([ + 'CLAUDECODE', + 'CLAUDE_PID', + 'CLAUDE_EFFORT', + 'CLAUDE_CODE_ENTRYPOINT', + 'CLAUDE_CODE_EXECPATH', + 'CLAUDE_CODE_SESSION_ID', + 'CLAUDE_CODE_CHILD_SESSION', + 'CLAUDE_CODE_SESSION_ATTENDED', + 'CLAUDE_CODE_BRIDGE_SESSION_ID', + 'CLAUDE_CODE_MESSAGING_TOKEN', + 'CLAUDE_CODE_MESSAGING_SOCKET', + // The IDE server of that session; Switchboard sets its own for --ide. + 'CLAUDE_CODE_SSE_PORT', +]); + function buildPtyEnv(sourceEnv) { const env = Object.fromEntries( Object.entries(sourceEnv).filter(([k]) => @@ -26,7 +48,8 @@ function buildPtyEnv(sourceEnv) { !k.startsWith('GOOGLE_API_KEY') && k !== 'NODE_OPTIONS' && k !== 'ORIGINAL_XDG_CURRENT_DESKTOP' && - k !== 'WT_SESSION' + k !== 'WT_SESSION' && + !INHERITED_CLAUDE_SESSION_VARS.has(k) ) ); diff --git a/test/pty-env.test.js b/test/pty-env.test.js index c4611d3db..b1835b44f 100644 --- a/test/pty-env.test.js +++ b/test/pty-env.test.js @@ -33,3 +33,19 @@ test('an empty locale value counts as absent', () => { // an empty string gives pbcopy nothing to work with either. assert.equal(buildPtyEnv({ LANG: '' }).LC_CTYPE, 'UTF-8'); }); + +test('a Claude Code session Switchboard was started from is not passed on', () => { + // Restarting the app from inside a session leaves these in its environment; + // a CLI that inherits them takes itself for a child and writes no transcript. + const env = buildPtyEnv({ + PATH: '/usr/bin', LANG: 'en_US.UTF-8', + CLAUDECODE: '1', CLAUDE_PID: '42', CLAUDE_EFFORT: 'high', + CLAUDE_CODE_ENTRYPOINT: 'cli', CLAUDE_CODE_EXECPATH: '/x/claude', + CLAUDE_CODE_SESSION_ID: 'parent', CLAUDE_CODE_CHILD_SESSION: '1', CLAUDE_CODE_SESSION_ATTENDED: '1', + CLAUDE_CODE_BRIDGE_SESSION_ID: 'b', CLAUDE_CODE_MESSAGING_TOKEN: 't', CLAUDE_CODE_MESSAGING_SOCKET: '/tmp/s', + CLAUDE_CODE_SSE_PORT: '1234', + // What the user configured stays. + CLAUDE_CODE_MAX_OUTPUT_TOKENS: '64000', CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: '1', CLAUDE_CONFIG_DIR: '/cfg', + }); + assert.deepEqual(Object.keys(env).sort(), ['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS', 'CLAUDE_CODE_MAX_OUTPUT_TOKENS', 'CLAUDE_CONFIG_DIR', 'LANG', 'PATH']); +});