From 7c1ef877560e482774ce1cba53f24bb66fcaeb59 Mon Sep 17 00:00:00 2001 From: haoqixu Date: Tue, 23 Jun 2026 19:14:02 +0800 Subject: [PATCH 1/2] 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"]); + }); +}); From e8ede14f5d51bae1ef1290cbd051351760936c65 Mon Sep 17 00:00:00 2001 From: haoqixu Date: Tue, 23 Jun 2026 19:32:55 +0800 Subject: [PATCH 2/2] fix(agent): normalize session names --- packages/agent/src/harness/session/session.ts | 3 ++- packages/agent/test/harness/session.test.ts | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/agent/src/harness/session/session.ts b/packages/agent/src/harness/session/session.ts index 6f136208..ce369dff 100644 --- a/packages/agent/src/harness/session/session.ts +++ b/packages/agent/src/harness/session/session.ts @@ -234,12 +234,13 @@ export class Session { } async appendSessionName(name: string): Promise { + const sanitizedName = name.replace(/[\r\n]+/g, " ").trim(); return this.appendTypedEntry({ type: "session_info", id: await this.storage.createEntryId(), parentId: await this.storage.getLeafId(), timestamp: new Date().toISOString(), - name: name.trim(), + name: sanitizedName, } satisfies SessionInfoEntry); } diff --git a/packages/agent/test/harness/session.test.ts b/packages/agent/test/harness/session.test.ts index c9598da8..39e285b5 100644 --- a/packages/agent/test/harness/session.test.ts +++ b/packages/agent/test/harness/session.test.ts @@ -86,6 +86,12 @@ async function runSessionSuite( expect(context.messages[1]?.role).toBe("custom"); }); + it("normalizes session names", async () => { + const session = new Session(await createStorage()); + await session.appendSessionName(" hello\nworld\r\nagain "); + expect(await session.getSessionName()).toBe("hello world again"); + }); + it("supports labels and session info entries without affecting context", async () => { const session = new Session(await createStorage()); const user1 = await session.appendMessage(createUserMessage("one"));