From 8c55d224c5c62288b75bd9108b975f61b371e9e3 Mon Sep 17 00:00:00 2001 From: qiyi71w <1411348064@qq.com> Date: Wed, 19 Aug 2026 02:32:27 +0800 Subject: [PATCH] fix(session): scope in-flight title latch to session id A boolean latch survived /new and unnamed session switches, so the replacement session skipped titling and could inherit the previous skill's title. Bind the latch and the apply check to the originating session id. --- .../coding-agent/src/session/agent-session.ts | 21 ++++--- ...t-session-title-generation-dispose.test.ts | 61 +++++++++++++++++++ 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/packages/coding-agent/src/session/agent-session.ts b/packages/coding-agent/src/session/agent-session.ts index 788d3f41b..1883c8e2e 100644 --- a/packages/coding-agent/src/session/agent-session.ts +++ b/packages/coding-agent/src/session/agent-session.ts @@ -535,7 +535,7 @@ export class AgentSession { * the session cwd changes. */ #titleSystemPrompt: string | undefined; #titleGenerationStart: (() => void) | undefined; - #titleGenerationInFlight = false; + #titleGenerationInFlightFor: string | undefined; #titleGenerationAbortController = new AbortController(); #toolChoiceQueue = new ToolChoiceQueue(); @@ -6550,26 +6550,31 @@ export class AgentSession { this.#extensionRunner?.getCommand( extensionCommandSpace === -1 ? firstMessage.slice(1) : firstMessage.slice(1, extensionCommandSpace), ) !== undefined; + const sessionId = this.sessionManager.getSessionId(); if ( isLocalExtensionCommand || this.sessionName || - this.#titleGenerationInFlight || + this.#titleGenerationInFlightFor === sessionId || $env.PI_NO_TITLE || isLowSignalTitleInput(firstMessage) ) { return; } - this.#titleGenerationInFlight = true; + this.#titleGenerationInFlightFor = sessionId; try { (onStart ?? this.#titleGenerationStart)?.(); } catch (error) { - this.#titleGenerationInFlight = false; + if (this.#titleGenerationInFlightFor === sessionId) { + this.#titleGenerationInFlightFor = undefined; + } throw error; } this.generateTitle(firstMessage) .then(async title => { - // Re-check after generation so concurrent attempts cannot replace - // the first title that completed. + // Re-check after generation so a later completion cannot replace + // the first title, and a request from a replaced session cannot + // name the current one. + if (this.sessionManager.getSessionId() !== sessionId) return; if (title && !this.sessionName) { await this.sessionManager.setSessionName(title, "auto"); } @@ -6582,7 +6587,9 @@ export class AgentSession { }); }) .finally(() => { - this.#titleGenerationInFlight = false; + if (this.#titleGenerationInFlightFor === sessionId) { + this.#titleGenerationInFlightFor = undefined; + } }); } diff --git a/packages/coding-agent/test/agent-session-title-generation-dispose.test.ts b/packages/coding-agent/test/agent-session-title-generation-dispose.test.ts index eb9b87275..f425c62fc 100644 --- a/packages/coding-agent/test/agent-session-title-generation-dispose.test.ts +++ b/packages/coding-agent/test/agent-session-title-generation-dispose.test.ts @@ -106,4 +106,65 @@ describe("AgentSession title generation disposal", () => { response.resolve(createAssistantMessage("