From 7c1ef877560e482774ce1cba53f24bb66fcaeb59 Mon Sep 17 00:00:00 2001 From: haoqixu Date: Tue, 23 Jun 2026 19:14:02 +0800 Subject: [PATCH] fix(coding-agent): normalize session names --- .../coding-agent/src/core/session-manager.ts | 3 +- .../src/modes/interactive/interactive-mode.ts | 6 ++- .../5996-session-name-newlines.test.ts | 40 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 packages/coding-agent/test/suite/regressions/5996-session-name-newlines.test.ts diff --git a/packages/coding-agent/src/core/session-manager.ts b/packages/coding-agent/src/core/session-manager.ts index c2bae164..3d31aff5 100644 --- a/packages/coding-agent/src/core/session-manager.ts +++ b/packages/coding-agent/src/core/session-manager.ts @@ -1025,12 +1025,13 @@ export class SessionManager { /** Append a session info entry (e.g., display name). Returns entry id. */ appendSessionInfo(name: string): string { + const sanitizedName = name.replace(/[\r\n]+/g, " ").trim(); const entry: SessionInfoEntry = { type: "session_info", id: generateId(this.byId), parentId: this.leafId, timestamp: new Date().toISOString(), - name: name.trim(), + name: sanitizedName, }; this._appendEntry(entry); return entry.id; diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index d50611af..80e49d78 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -5340,8 +5340,12 @@ export class InteractiveMode { } this.session.setSessionName(name); + const sessionName = this.sessionManager.getSessionName(); + if (sessionName !== name) { + this.showWarning(`Session name was normalized from ${JSON.stringify(name)} to ${JSON.stringify(sessionName)}`); + } this.chatContainer.addChild(new Spacer(1)); - this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${name}`), 1, 0)); + this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${sessionName ?? name}`), 1, 0)); this.ui.requestRender(); } diff --git a/packages/coding-agent/test/suite/regressions/5996-session-name-newlines.test.ts b/packages/coding-agent/test/suite/regressions/5996-session-name-newlines.test.ts new file mode 100644 index 00000000..c33f7760 --- /dev/null +++ b/packages/coding-agent/test/suite/regressions/5996-session-name-newlines.test.ts @@ -0,0 +1,40 @@ +import { afterEach, describe, expect, it } from "vitest"; +import type { ExtensionAPI } from "../../../src/index.ts"; +import { createHarness, type Harness } from "../harness.ts"; + +describe("regression #5996: session names do not contain newlines", () => { + const harnesses: Harness[] = []; + + afterEach(() => { + while (harnesses.length > 0) { + harnesses.pop()?.cleanup(); + } + }); + + it("filters newlines when AgentSession.setSessionName is called", async () => { + const harness = await createHarness(); + harnesses.push(harness); + + harness.session.setSessionName("hello\nworld\r\nagain"); + + expect(harness.sessionManager.getSessionName()).toBe("hello world again"); + expect(harness.eventsOfType("session_info_changed").map((event) => event.name)).toEqual(["hello world again"]); + }); + + it("filters newlines when an extension calls pi.setSessionName", async () => { + let api: ExtensionAPI | undefined; + const harness = await createHarness({ + extensionFactories: [ + (pi) => { + api = pi; + }, + ], + }); + harnesses.push(harness); + + api?.setSessionName("from\nextension"); + + expect(harness.sessionManager.getSessionName()).toBe("from extension"); + expect(harness.eventsOfType("session_info_changed").map((event) => event.name)).toEqual(["from extension"]); + }); +});