From 18d90b5c4867a6ca170f18431c762f814f3fe8a1 Mon Sep 17 00:00:00 2001 From: Aliou Diallo Date: Tue, 17 Mar 2026 16:22:04 +0100 Subject: [PATCH] fix(coding-agent): handle empty session titles and session info metadata (#2304) * fix(coding-agent): handle empty session titles in tree * fix(coding-agent): ignore session info in summaries * docs: update changelog for PR #2304 --- packages/coding-agent/CHANGELOG.md | 4 ++++ .../src/core/compaction/branch-summarization.ts | 1 + .../coding-agent/src/core/compaction/compaction.ts | 3 +++ packages/coding-agent/src/core/session-manager.ts | 13 ++++++------- .../modes/interactive/components/tree-selector.ts | 12 +++++++++++- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 3f9d2c8f..6046d316 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -6,6 +6,10 @@ - Changed custom tool system prompt behavior so extension and SDK tools are included in the default `Available tools` section only when they provide `promptSnippet`. Omitting `promptSnippet` now leaves the tool out of that section instead of falling back to `description` ([#2285](https://github.com/badlogic/pi-mono/issues/2285)) +### Fixed + +- Fixed session title handling in `/tree`, compaction, and branch summarization so empty title clears render correctly and `session_info` entries stay out of summaries ([#2304](https://github.com/badlogic/pi-mono/pull/2304) by [@aliou](https://github.com/aliou)) + ## [0.58.4] - 2026-03-16 ### Fixed diff --git a/packages/coding-agent/src/core/compaction/branch-summarization.ts b/packages/coding-agent/src/core/compaction/branch-summarization.ts index 5e3b1896..d52b2d92 100644 --- a/packages/coding-agent/src/core/compaction/branch-summarization.ts +++ b/packages/coding-agent/src/core/compaction/branch-summarization.ts @@ -162,6 +162,7 @@ function getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined { case "model_change": case "custom": case "label": + case "session_info": return undefined; } } diff --git a/packages/coding-agent/src/core/compaction/compaction.ts b/packages/coding-agent/src/core/compaction/compaction.ts index 4703de6b..213ab6bd 100644 --- a/packages/coding-agent/src/core/compaction/compaction.ts +++ b/packages/coding-agent/src/core/compaction/compaction.ts @@ -317,7 +317,10 @@ function findValidCutPoints(entries: SessionEntry[], startIndex: number, endInde case "custom": case "custom_message": case "label": + case "session_info": + break; } + // branch_summary and custom_message are user-role messages, valid cut points if (entry.type === "branch_summary" || entry.type === "custom_message") { cutPoints.push(i); diff --git a/packages/coding-agent/src/core/session-manager.ts b/packages/coding-agent/src/core/session-manager.ts index 49b0f422..e22a2e0a 100644 --- a/packages/coding-agent/src/core/session-manager.ts +++ b/packages/coding-agent/src/core/session-manager.ts @@ -565,12 +565,10 @@ async function buildSessionInfo(filePath: string): Promise { let name: string | undefined; for (const entry of entries) { - // Extract session name (use latest) + // Extract session name (use latest, including explicit clears) if (entry.type === "session_info") { const infoEntry = entry as SessionInfoEntry; - if (infoEntry.name) { - name = infoEntry.name.trim(); - } + name = infoEntry.name?.trim() || undefined; } if (entry.type !== "message") continue; @@ -913,12 +911,13 @@ export class SessionManager { /** Get the current session name from the latest session_info entry, if any. */ getSessionName(): string | undefined { - // Walk entries in reverse to find the latest session_info with a name + // Walk entries in reverse to find the latest session_info entry. + // Empty names explicitly clear the session title. const entries = this.getEntries(); for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i]; - if (entry.type === "session_info" && entry.name) { - return entry.name; + if (entry.type === "session_info") { + return entry.name?.trim() || undefined; } } return undefined; diff --git a/packages/coding-agent/src/modes/interactive/components/tree-selector.ts b/packages/coding-agent/src/modes/interactive/components/tree-selector.ts index e6d53af5..58eb7afc 100644 --- a/packages/coding-agent/src/modes/interactive/components/tree-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/tree-selector.ts @@ -302,7 +302,8 @@ class TreeList implements Component { entry.type === "label" || entry.type === "custom" || entry.type === "model_change" || - entry.type === "thinking_level_change"; + entry.type === "thinking_level_change" || + entry.type === "session_info"; switch (this.filterMode) { case "user-only": @@ -535,6 +536,10 @@ class TreeList implements Component { case "branch_summary": parts.push("branch summary", entry.summary); break; + case "session_info": + parts.push("title"); + if (entry.name) parts.push(entry.name); + break; case "model_change": parts.push("model", entry.modelId); break; @@ -755,6 +760,11 @@ class TreeList implements Component { case "label": result = theme.fg("dim", `[label: ${entry.label ?? "(cleared)"}]`); break; + case "session_info": + result = entry.name + ? [theme.fg("dim", "[title: "), theme.fg("dim", entry.name), theme.fg("dim", "]")].join("") + : [theme.fg("dim", "[title: "), theme.italic(theme.fg("dim", "empty")), theme.fg("dim", "]")].join(""); + break; default: result = ""; }