From e63390f390dceb595430cf45896fd38b3da2ecf5 Mon Sep 17 00:00:00 2001 From: Waz Date: Sun, 19 Jul 2026 16:30:28 -0400 Subject: [PATCH] Prefer chat names in session labels --- Sources/CodexBarCore/AgentSession.swift | 52 +++++++++++++++---- .../CodexSessionRolloutTests.swift | 36 +++++++++++++ 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Sources/CodexBarCore/AgentSession.swift b/Sources/CodexBarCore/AgentSession.swift index bd2ec6e3a9..73ca905421 100644 --- a/Sources/CodexBarCore/AgentSession.swift +++ b/Sources/CodexBarCore/AgentSession.swift @@ -535,15 +535,16 @@ public struct CodexRolloutMetadata: Equatable, Sendable { } public func descriptiveName(threadMetadata: CodexThreadMetadata?) -> String? { - if self.isGuardian { - return "Approval review" + let taskName: String? = if self.isGuardian { + "Approval review" + } else if let agentPath = threadMetadata?.agentPath ?? self.agentPath { + AgentSessionNameFormatter.agentPath(agentPath) + } else { + nil } - if let agentPath = threadMetadata?.agentPath ?? self.agentPath, - let name = AgentSessionNameFormatter.agentPath(agentPath) - { - return name - } - return threadMetadata?.title.flatMap { AgentSessionNameFormatter.title($0) } + return AgentSessionNameFormatter.sessionName( + title: threadMetadata?.title, + taskName: taskName) } } @@ -597,8 +598,39 @@ private enum AgentSessionNameFormatter { } let compact = line.split(whereSeparator: \ .isWhitespace).joined(separator: " ") - guard compact.count > maximumLength else { return compact } - return compact.prefix(maximumLength - 1).trimmingCharacters(in: .whitespacesAndNewlines) + "…" + return self.truncated(compact, maximumLength: maximumLength) + } + + static func sessionName(title: String?, taskName: String?, maximumLength: Int = 64) -> String? { + let chatName = title.flatMap { self.title($0, maximumLength: .max) } + guard let chatName else { + return taskName.map { self.truncated($0, maximumLength: maximumLength) } + } + guard let taskName, + chatName.caseInsensitiveCompare(taskName) != .orderedSame + else { + return self.truncated(chatName, maximumLength: maximumLength) + } + + let separator = " · " + let combined = chatName + separator + taskName + guard combined.count > maximumLength else { return combined } + + let initialTaskLength = min(taskName.count, maximumLength / 3) + var compactTaskName = self.truncated(taskName, maximumLength: initialTaskLength) + let chatLength = max(1, maximumLength - separator.count - compactTaskName.count) + let compactChatName = self.truncated(chatName, maximumLength: chatLength) + if compactChatName.count == chatName.count { + let taskLength = max(1, maximumLength - separator.count - compactChatName.count) + compactTaskName = self.truncated(taskName, maximumLength: taskLength) + } + return compactChatName + separator + compactTaskName + } + + private static func truncated(_ value: String, maximumLength: Int) -> String { + guard value.count > maximumLength else { return value } + guard maximumLength > 1 else { return String(value.prefix(max(0, maximumLength))) } + return value.prefix(maximumLength - 1).trimmingCharacters(in: .whitespacesAndNewlines) + "…" } } diff --git a/Tests/CodexBarTests/CodexSessionRolloutTests.swift b/Tests/CodexBarTests/CodexSessionRolloutTests.swift index 87335f4ec2..b2f9531224 100644 --- a/Tests/CodexBarTests/CodexSessionRolloutTests.swift +++ b/Tests/CodexBarTests/CodexSessionRolloutTests.swift @@ -141,6 +141,42 @@ struct CodexSessionRolloutTests { #expect(guardian.descriptiveName(threadMetadata: nil) == "Approval review") } + @Test + func `chat names stay primary for subagent and guardian sessions`() throws { + let subagentLine = + "{\"type\":\"session_meta\",\"payload\":{\"id\":\"subagent\",\"cwd\":\"/repo\"," + + "\"originator\":\"codex_vscode\",\"source\":{\"subagent\":{\"thread_spawn\":{\"agent_path\":" + + "\"/root/neon_patch_review2\"}}}}}" + let guardianLine = + "{\"type\":\"session_meta\",\"payload\":{\"id\":\"guardian\",\"cwd\":\"/repo\"," + + "\"originator\":\"codex_vscode\",\"source\":{\"subagent\":{\"other\":\"guardian\"}}}}" + let subagent = try #require(CodexRolloutFirstLineParser.parse(subagentLine)) + let guardian = try #require(CodexRolloutFirstLineParser.parse(guardianLine)) + + #expect(subagent.descriptiveName(threadMetadata: CodexThreadMetadata( + title: "Website refresh", + agentPath: nil)) == "Website refresh · Neon patch review 2") + #expect(guardian.descriptiveName(threadMetadata: CodexThreadMetadata( + title: "Dependency audit", + agentPath: nil)) == "Dependency audit · Approval review") + } + + @Test + func `chat and task names stay menu sized`() throws { + let line = + "{\"type\":\"session_meta\",\"payload\":{\"id\":\"subagent\",\"cwd\":\"/repo\"," + + "\"originator\":\"codex_vscode\",\"source\":{\"subagent\":{\"thread_spawn\":{\"agent_path\":" + + "\"/root/review_the_complete_rollout_and_report_every_regression\"}}}}}" + let metadata = try #require(CodexRolloutFirstLineParser.parse(line)) + + let name = metadata.descriptiveName(threadMetadata: CodexThreadMetadata( + title: "Continue work on the Concrete Authority website and compare every source", + agentPath: nil)) + + #expect(name == "Continue work on the Concrete Authority… · Review the complete…") + #expect((name?.count ?? .max) <= 64) + } + @Test func `current rollout agent path produces a descriptive subagent name without sqlite`() throws { let line =