From 8df350030dd03fc726a2fc3afaa877071f482cb3 Mon Sep 17 00:00:00 2001 From: Alex Knight Date: Fri, 22 May 2026 15:14:55 +1000 Subject: [PATCH] fix(ui): show all configured chat picker sessions Remove the chat picker recency/current-agent filters while preserving the bounded configured-agent refresh, and add the changelog credit for @amknight. --- CHANGELOG.md | 1 + ui/src/ui/app-chat.test.ts | 8 ++++--- ui/src/ui/app-chat.ts | 4 ++-- ui/src/ui/app-gateway.sessions.node.test.ts | 4 +--- ui/src/ui/app-gateway.ts | 23 --------------------- ui/src/ui/app-render.helpers.node.test.ts | 2 -- ui/src/ui/app-render.helpers.ts | 2 -- ui/src/ui/chat/session-controls.ts | 7 ------- 8 files changed, 9 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03b97934e8a..349277afb5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai - Agents/Copilot: drop unsafe GitHub Copilot Responses reasoning replay items before send so Telegram direct sessions no longer fail on overlong replay IDs. Fixes #85197. (#85198) Thanks @galiniliev. - fix: constrain Windows task script names [AI]. (#85064) Thanks @pgondhi987. +- Control UI: keep the chat session picker from hiding older or cross-agent configured conversations while preserving the bounded configured-agent refresh. (#85211) Thanks @amknight. - Agents/Anthropic: preserve unsafe integer tool-call input values in streamed Anthropic tool-use JSON, preventing Discord-style IDs from being rounded before dispatch. Fixes #47229. (#83063) Thanks @leno23. - Agents/hooks: wait for local one-shot CLI and Codex `agent_end` plugin hooks before process cleanup so terminal observability flushes reliably. (#85007) - CLI/agents: allow `openclaw agent --session-key` to target explicit session keys, including agent-scoped legacy keys. (#85121) Thanks @Kaspre. diff --git a/ui/src/ui/app-chat.test.ts b/ui/src/ui/app-chat.test.ts index a7521f06665..5263650d851 100644 --- a/ui/src/ui/app-chat.test.ts +++ b/ui/src/ui/app-chat.test.ts @@ -229,9 +229,11 @@ describe("refreshChat", () => { "sessions.list", "sessions list payload", ); - expect(sessionsListPayload.agentId).toBe("main"); + expect(sessionsListPayload).not.toHaveProperty("activeMinutes"); + expect(sessionsListPayload).not.toHaveProperty("agentId"); expect(sessionsListPayload.includeGlobal).toBe(true); expect(sessionsListPayload.includeUnknown).toBe(true); + expect(sessionsListPayload.limit).toBe(100); expect(request).toHaveBeenCalledWith("commands.list", { agentId: "main", includeArgs: true, @@ -578,8 +580,8 @@ describe("refreshChat", () => { "sessions.list", "sessions list payload", ); - expect(sessionsListPayload.activeMinutes).toBe(120); - expect(sessionsListPayload.agentId).toBe("main"); + expect(sessionsListPayload).not.toHaveProperty("activeMinutes"); + expect(sessionsListPayload).not.toHaveProperty("agentId"); expect(sessionsListPayload.includeGlobal).toBe(true); expect(sessionsListPayload.includeUnknown).toBe(true); expect(sessionsListPayload.limit).toBe(100); diff --git a/ui/src/ui/app-chat.ts b/ui/src/ui/app-chat.ts index 18db6727c88..23b1ccb079d 100644 --- a/ui/src/ui/app-chat.ts +++ b/ui/src/ui/app-chat.ts @@ -85,7 +85,8 @@ export type ChatAbortOptions = { preserveDraft?: boolean; }; -export const CHAT_SESSIONS_ACTIVE_MINUTES = 120; +// Chat pickers need recency-free session rows so older channel chats remain selectable. +export const CHAT_SESSIONS_ACTIVE_MINUTES = 0; export const CHAT_SESSIONS_REFRESH_LIMIT = 100; export { handleChatDraftChange, @@ -784,7 +785,6 @@ export async function refreshChat( limit: CHAT_SESSIONS_REFRESH_LIMIT, includeGlobal: true, includeUnknown: true, - agentId: resolveAgentIdForSession(host) ?? undefined, }), refreshChatAvatar(host), refreshChatModels(host), diff --git a/ui/src/ui/app-gateway.sessions.node.test.ts b/ui/src/ui/app-gateway.sessions.node.test.ts index 1733aaaa11c..7059d0ff270 100644 --- a/ui/src/ui/app-gateway.sessions.node.test.ts +++ b/ui/src/ui/app-gateway.sessions.node.test.ts @@ -141,7 +141,7 @@ describe("handleGatewayEvent sessions.changed", () => { vi.useRealTimers(); }); - it("scopes post-chat final session refreshes to the run's agent", () => { + it("refreshes the full chat session list after a completed chat run", () => { loadSessionsMock.mockReset(); handleChatEventMock.mockReset().mockReturnValue("final"); const host = createHost(); @@ -157,7 +157,6 @@ describe("handleGatewayEvent sessions.changed", () => { expect(loadSessionsMock).toHaveBeenCalledWith(host, { activeMinutes: 10, - agentId: "ops", limit: 25, }); }); @@ -557,7 +556,6 @@ describe("handleGatewayEvent session.message", () => { expect(loadChatHistoryMock).not.toHaveBeenCalled(); expect(loadSessionsMock).toHaveBeenCalledWith(host, { activeMinutes: 10, - agentId: "qa", limit: 25, }); await Promise.resolve(); diff --git a/ui/src/ui/app-gateway.ts b/ui/src/ui/app-gateway.ts index 644cfab2bd9..b324b92ba21 100644 --- a/ui/src/ui/app-gateway.ts +++ b/ui/src/ui/app-gateway.ts @@ -690,7 +690,6 @@ function handleTerminalChatEvent( if (state === "final") { void loadSessions(host as unknown as SessionsState, { activeMinutes: CHAT_SESSIONS_ACTIVE_MINUTES, - agentId: resolveChatEventSessionListAgentId(host, payload), limit: CHAT_SESSIONS_REFRESH_LIMIT, }); } @@ -720,27 +719,6 @@ function isEventForDifferentActiveRun( return Boolean(activeRunId && payload && payload.runId !== activeRunId); } -function resolveChatEventSessionListAgentId( - host: GatewayHost, - payload: ChatEventPayload | undefined, -): string { - return resolveSessionListAgentIdForSessionKey( - host, - payload?.sessionKey?.trim() || host.sessionKey, - ); -} - -function resolveSessionListAgentIdForSessionKey(host: GatewayHost, sessionKey: string): string { - const parsed = parseAgentSessionKey(sessionKey); - if (parsed?.agentId) { - return parsed.agentId; - } - const snapshot = host.hello?.snapshot as - | { sessionDefaults?: SessionDefaultsSnapshot } - | undefined; - return normalizeAgentId(snapshot?.sessionDefaults?.defaultAgentId); -} - function handleChatGatewayEvent(host: GatewayHost, payload: ChatEventPayload | undefined) { if (payload?.sessionKey) { setLastActiveSessionKey( @@ -861,7 +839,6 @@ function handleSessionMessageGatewayEvent( const runIdBeforeRefresh = host.chatRunId; void loadSessions(host as unknown as SessionsState, { activeMinutes: CHAT_SESSIONS_ACTIVE_MINUTES, - agentId: resolveSessionListAgentIdForSessionKey(host, sessionKey), limit: CHAT_SESSIONS_REFRESH_LIMIT, }).finally(() => replayDeferredSessionMessageReloadAfterSessionsRefresh( diff --git a/ui/src/ui/app-render.helpers.node.test.ts b/ui/src/ui/app-render.helpers.node.test.ts index b0c4897dc76..5fa576f8329 100644 --- a/ui/src/ui/app-render.helpers.node.test.ts +++ b/ui/src/ui/app-render.helpers.node.test.ts @@ -775,7 +775,6 @@ describe("createChatSession", () => { includeGlobal: true, includeUnknown: true, showArchived: false, - agentId: "ops", }, ); expect(state.sessionKey).toBe("agent:ops:dashboard:new-chat"); @@ -977,7 +976,6 @@ describe("switchChatSession", () => { includeGlobal: true, includeUnknown: true, showArchived: false, - agentId: "main", }); expect( (state as unknown as { announceSessionSwitch: ReturnType }) diff --git a/ui/src/ui/app-render.helpers.ts b/ui/src/ui/app-render.helpers.ts index 4927e5d23a2..6d9050f60d3 100644 --- a/ui/src/ui/app-render.helpers.ts +++ b/ui/src/ui/app-render.helpers.ts @@ -705,7 +705,6 @@ export async function createChatSession(state: AppViewState): Promise { includeGlobal: true, includeUnknown: true, showArchived: state.sessionsShowArchived, - agentId: resolveAgentIdFromSessionKey(previousSessionKey), }, ); if ( @@ -738,7 +737,6 @@ async function refreshSessionOptions(state: AppViewState) { includeGlobal: true, includeUnknown: true, showArchived: state.sessionsShowArchived, - agentId: parseAgentSessionKey(state.sessionKey)?.agentId, }); } diff --git a/ui/src/ui/chat/session-controls.ts b/ui/src/ui/chat/session-controls.ts index dc50a3a9dac..711112248a1 100644 --- a/ui/src/ui/chat/session-controls.ts +++ b/ui/src/ui/chat/session-controls.ts @@ -202,16 +202,9 @@ async function refreshSessionOptions(state: AppViewState) { includeGlobal: true, includeUnknown: true, showArchived: state.sessionsShowArchived, - agentId: resolveSessionOptionsAgentId(state), }); } -function resolveSessionOptionsAgentId(state: AppViewState): string { - return ( - parseAgentSessionKey(state.sessionKey)?.agentId ?? normalizeAgentId(state.agentsList?.defaultId) - ); -} - async function refreshVisibleToolsEffectiveForCurrentSessionLazy(state: AppViewState) { return refreshVisibleToolsEffectiveForCurrentSession(state); }