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
This commit is contained in:
Aliou Diallo
2026-03-17 16:22:04 +01:00
committed by GitHub
parent 2d05e87281
commit 18d90b5c48
5 changed files with 25 additions and 8 deletions

View File

@@ -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)) - 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 ## [0.58.4] - 2026-03-16
### Fixed ### Fixed

View File

@@ -162,6 +162,7 @@ function getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {
case "model_change": case "model_change":
case "custom": case "custom":
case "label": case "label":
case "session_info":
return undefined; return undefined;
} }
} }

View File

@@ -317,7 +317,10 @@ function findValidCutPoints(entries: SessionEntry[], startIndex: number, endInde
case "custom": case "custom":
case "custom_message": case "custom_message":
case "label": case "label":
case "session_info":
break;
} }
// branch_summary and custom_message are user-role messages, valid cut points // branch_summary and custom_message are user-role messages, valid cut points
if (entry.type === "branch_summary" || entry.type === "custom_message") { if (entry.type === "branch_summary" || entry.type === "custom_message") {
cutPoints.push(i); cutPoints.push(i);

View File

@@ -565,12 +565,10 @@ async function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {
let name: string | undefined; let name: string | undefined;
for (const entry of entries) { for (const entry of entries) {
// Extract session name (use latest) // Extract session name (use latest, including explicit clears)
if (entry.type === "session_info") { if (entry.type === "session_info") {
const infoEntry = entry as SessionInfoEntry; const infoEntry = entry as SessionInfoEntry;
if (infoEntry.name) { name = infoEntry.name?.trim() || undefined;
name = infoEntry.name.trim();
}
} }
if (entry.type !== "message") continue; 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. */ /** Get the current session name from the latest session_info entry, if any. */
getSessionName(): string | undefined { 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(); const entries = this.getEntries();
for (let i = entries.length - 1; i >= 0; i--) { for (let i = entries.length - 1; i >= 0; i--) {
const entry = entries[i]; const entry = entries[i];
if (entry.type === "session_info" && entry.name) { if (entry.type === "session_info") {
return entry.name; return entry.name?.trim() || undefined;
} }
} }
return undefined; return undefined;

View File

@@ -302,7 +302,8 @@ class TreeList implements Component {
entry.type === "label" || entry.type === "label" ||
entry.type === "custom" || entry.type === "custom" ||
entry.type === "model_change" || entry.type === "model_change" ||
entry.type === "thinking_level_change"; entry.type === "thinking_level_change" ||
entry.type === "session_info";
switch (this.filterMode) { switch (this.filterMode) {
case "user-only": case "user-only":
@@ -535,6 +536,10 @@ class TreeList implements Component {
case "branch_summary": case "branch_summary":
parts.push("branch summary", entry.summary); parts.push("branch summary", entry.summary);
break; break;
case "session_info":
parts.push("title");
if (entry.name) parts.push(entry.name);
break;
case "model_change": case "model_change":
parts.push("model", entry.modelId); parts.push("model", entry.modelId);
break; break;
@@ -755,6 +760,11 @@ class TreeList implements Component {
case "label": case "label":
result = theme.fg("dim", `[label: ${entry.label ?? "(cleared)"}]`); result = theme.fg("dim", `[label: ${entry.label ?? "(cleared)"}]`);
break; 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: default:
result = ""; result = "";
} }