Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions Sources/CodexBarCore/AgentSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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) + "…"
}
}

Expand Down
36 changes: 36 additions & 0 deletions Tests/CodexBarTests/CodexSessionRolloutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Loading