From 43ffe41a4b087deccfa4cb291b4c6f820f1dc47c Mon Sep 17 00:00:00 2001 From: ZYV5ge <39863830+ZYV5ge@users.noreply.github.com> Date: Tue, 25 Aug 2026 14:15:52 -0700 Subject: [PATCH] fix(sessions): search visible categories across clients (#118912) Co-authored-by: Peter Steinberger --- .../java/ai/openclaw/app/chat/ChatModels.kt | 2 +- .../chat/ChatControllerSessionSearchTest.kt | 5 ++-- .../Sources/OpenClawChatUI/ChatSessions.swift | 8 ++---- .../ChatSessionSidebarModelTests.swift | 5 ++-- src/gateway/session-utils-list.ts | 1 + src/gateway/session-utils.test.ts | 27 +++++++++++++++++++ 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/apps/android/app/src/main/java/ai/openclaw/app/chat/ChatModels.kt b/apps/android/app/src/main/java/ai/openclaw/app/chat/ChatModels.kt index 6b6f3d3c4542..04f0440f4371 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/chat/ChatModels.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/chat/ChatModels.kt @@ -366,7 +366,7 @@ fun filterSessionEntries( val query = search.trim().lowercase() if (query.isEmpty()) return sessions return sessions.filter { session -> - listOfNotNull(session.displayName, session.label, session.key) + listOfNotNull(session.displayName, session.label, session.category, session.key) .any { it.lowercase().contains(query) } } } diff --git a/apps/android/app/src/test/java/ai/openclaw/app/chat/ChatControllerSessionSearchTest.kt b/apps/android/app/src/test/java/ai/openclaw/app/chat/ChatControllerSessionSearchTest.kt index 90a20b61adcb..a4faf126c046 100644 --- a/apps/android/app/src/test/java/ai/openclaw/app/chat/ChatControllerSessionSearchTest.kt +++ b/apps/android/app/src/test/java/ai/openclaw/app/chat/ChatControllerSessionSearchTest.kt @@ -49,17 +49,18 @@ class ChatControllerSessionSearchTest { ?.content @Test - fun filterSessionEntriesMatchesDisplayNameLabelAndKey() { + fun filterSessionEntriesMatchesDisplayNameLabelCategoryAndKey() { val sessions = listOf( ChatSessionEntry(key = "agent:main:topic-a", updatedAtMs = 2, displayName = "Trip planning"), - ChatSessionEntry(key = "agent:main:topic-b", updatedAtMs = 1, displayName = "Groceries"), + ChatSessionEntry(key = "agent:main:topic-b", updatedAtMs = 1, displayName = "Groceries", category = "Team Planning"), ChatSessionEntry(key = "agent:main:trip-notes", updatedAtMs = 3, displayName = "Notes"), ) assertEquals( listOf("agent:main:topic-a", "agent:main:trip-notes"), filterSessionEntries(sessions, "TRIP").map { it.key }, ) + assertEquals(listOf("agent:main:topic-b"), filterSessionEntries(sessions, "TEAM PLANNING").map { it.key }) assertEquals(sessions, filterSessionEntries(sessions, " ")) } diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSessions.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSessions.swift index 1c6350b2e4e4..f4db5b2edd8e 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSessions.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSessions.swift @@ -587,12 +587,8 @@ public enum OpenClawChatSessionListOrganizer { let query = search.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() guard !query.isEmpty else { return sessions } return sessions.filter { session in - for field in [session.displayName, session.label, session.subject, session.sessionId, session.key] { - if let field, field.lowercased().contains(query) { - return true - } - } - return false + [session.displayName, session.label, session.subject, session.sessionId, session.category, session.key] + .contains { $0?.lowercased().contains(query) == true } } } } diff --git a/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatSessionSidebarModelTests.swift b/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatSessionSidebarModelTests.swift index 52631d9a02be..fced60672df8 100644 --- a/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatSessionSidebarModelTests.swift +++ b/apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatSessionSidebarModelTests.swift @@ -307,7 +307,7 @@ struct ChatSessionSidebarModelTests { #expect(sections.flatMap(\.nodes).map(\.session.key) == ["agent:main:research"]) } - @Test(arguments: ["holiday", "KYOTO", "session-123", " HoLiDaY "]) + @Test(arguments: ["holiday", "KYOTO", "session-123", "team planning", " HoLiDaY "]) func `sidebar search matches every canonical gateway session field`(_ query: String) { let matching = self.entry( key: "agent:main:roadmap", @@ -315,7 +315,8 @@ struct ChatSessionSidebarModelTests { label: "Summer holiday", subject: "Kyoto itinerary", sessionId: "session-123", - updatedAt: 200) + updatedAt: 200, + category: "Team Planning") let other = self.entry( key: "agent:main:other", displayName: "Unrelated", diff --git a/src/gateway/session-utils-list.ts b/src/gateway/session-utils-list.ts index 13ac234250e2..b46ac317b57d 100644 --- a/src/gateway/session-utils-list.ts +++ b/src/gateway/session-utils-list.ts @@ -280,6 +280,7 @@ function filterSessionEntries(params: { entry.label, entry.subject, entry.sessionId, + entry.category, key, ]; appendStoredSessionModelSearchFields(cheapFields, entry); diff --git a/src/gateway/session-utils.test.ts b/src/gateway/session-utils.test.ts index cebc2d305f72..26051af727c1 100644 --- a/src/gateway/session-utils.test.ts +++ b/src/gateway/session-utils.test.ts @@ -629,6 +629,33 @@ describe("gateway session utils", () => { expect(listed.hasMore).toBe(true); }); + test("session list search includes the session group name", () => { + const cfg = { agents: { list: [{ id: "main", default: true }] } } as OpenClawConfig; + const store: Record = { + "agent:main:roadmap": { + sessionId: "roadmap", + displayName: "Quarterly roadmap", + category: "Team Planning", + updatedAt: 2, + }, + "agent:main:other": { + sessionId: "other", + displayName: "Unrelated", + category: "Personal", + updatedAt: 1, + }, + }; + + const listed = listSessionsFromStore({ + cfg, + storePath: "", + store, + opts: { search: "team planning" }, + }); + + expect(listed.sessions.map((session) => session.key)).toEqual(["agent:main:roadmap"]); + }); + test("session list search includes direct-session origin display labels", () => { const cfg = { agents: { list: [{ id: "main", default: true }] } } as OpenClawConfig; const store: Record = {