From 0b7aa8cf1d2a318795ee23dc1ef10fc9873a365f Mon Sep 17 00:00:00 2001 From: Tyler Yust Date: Fri, 30 Jan 2026 14:29:04 -0800 Subject: [PATCH] feat(ui): refresh session list after chat commands in Web UI --- CHANGELOG.md | 1 + ui/src/ui/app-chat.ts | 24 +++++++++++++++++------- ui/src/ui/app-gateway.ts | 7 ++++--- ui/src/ui/app.ts | 2 +- ui/src/ui/controllers/chat.ts | 10 +++++----- ui/src/ui/ui-types.ts | 1 + 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73fb59bec..73f7d3947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,7 @@ Status: stable. - TTS: read OPENAI_TTS_BASE_URL at runtime instead of module load to honor config.env. (#3341) Thanks @hclsys. - macOS: auto-scroll to bottom when sending a new message while scrolled up. (#2471) Thanks @kennyklee. - Web UI: auto-expand the chat compose textarea while typing (with sensible max height). (#2950) Thanks @shivamraut101. +- Web UI: refresh sessions after queued /new or /reset commands once the run completes. - Gateway: prevent crashes on transient network errors (fetch failures, timeouts, DNS). Added fatal error detection to only exit on truly critical errors. Fixes #2895, #2879, #2873. (#2980) Thanks @elliotsecops. - Agents: guard channel tool listActions to avoid plugin crashes. (#2859) Thanks @mbelinky. - Discord: stop resolveDiscordTarget from passing directory params into messaging target parsers. Fixes #3167. Thanks @thewilloftheshadow. diff --git a/ui/src/ui/app-chat.ts b/ui/src/ui/app-chat.ts index d7a5165c5..8d3262c5c 100644 --- a/ui/src/ui/app-chat.ts +++ b/ui/src/ui/app-chat.ts @@ -21,7 +21,7 @@ type ChatHost = { basePath: string; hello: GatewayHelloOk | null; chatAvatarUrl: string | null; - refreshSessionsAfterChat: boolean; + refreshSessionsAfterChat: Set; }; export function isChatBusy(host: ChatHost) { @@ -56,7 +56,12 @@ export async function handleAbortChat(host: ChatHost) { await abortChatRun(host as unknown as OpenClawApp); } -function enqueueChatMessage(host: ChatHost, text: string, attachments?: ChatAttachment[]) { +function enqueueChatMessage( + host: ChatHost, + text: string, + attachments?: ChatAttachment[], + refreshSessions?: boolean, +) { const trimmed = text.trim(); const hasAttachments = Boolean(attachments && attachments.length > 0); if (!trimmed && !hasAttachments) return; @@ -67,6 +72,7 @@ function enqueueChatMessage(host: ChatHost, text: string, attachments?: ChatAtta text: trimmed, createdAt: Date.now(), attachments: hasAttachments ? attachments?.map((att) => ({ ...att })) : undefined, + refreshSessions, }, ]; } @@ -84,7 +90,8 @@ async function sendChatMessageNow( }, ) { resetToolStream(host as unknown as Parameters[0]); - const ok = await sendChatMessage(host as unknown as OpenClawApp, message, opts?.attachments); + const runId = await sendChatMessage(host as unknown as OpenClawApp, message, opts?.attachments); + const ok = Boolean(runId); if (!ok && opts?.previousDraft != null) { host.chatMessage = opts.previousDraft; } @@ -104,8 +111,8 @@ async function sendChatMessageNow( if (ok && !host.chatRunId) { void flushChatQueue(host); } - if (ok && opts?.refreshSessions) { - host.refreshSessionsAfterChat = true; + if (ok && opts?.refreshSessions && runId) { + host.refreshSessionsAfterChat.add(runId); } return ok; } @@ -115,7 +122,10 @@ async function flushChatQueue(host: ChatHost) { const [next, ...rest] = host.chatQueue; if (!next) return; host.chatQueue = rest; - const ok = await sendChatMessageNow(host, next.text, { attachments: next.attachments }); + const ok = await sendChatMessageNow(host, next.text, { + attachments: next.attachments, + refreshSessions: next.refreshSessions, + }); if (!ok) { host.chatQueue = [next, ...host.chatQueue]; } @@ -153,7 +163,7 @@ export async function handleSendChat( } if (isChatBusy(host)) { - enqueueChatMessage(host, message, attachmentsToSend); + enqueueChatMessage(host, message, attachmentsToSend, refreshSessions); return; } diff --git a/ui/src/ui/app-gateway.ts b/ui/src/ui/app-gateway.ts index d4c8339c9..fe156470a 100644 --- a/ui/src/ui/app-gateway.ts +++ b/ui/src/ui/app-gateway.ts @@ -51,7 +51,7 @@ type GatewayHost = { assistantAgentId: string | null; sessionKey: string; chatRunId: string | null; - refreshSessionsAfterChat: boolean; + refreshSessionsAfterChat: Set; execApprovalQueue: ExecApprovalRequest[]; execApprovalError: string | null; }; @@ -196,8 +196,9 @@ function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) { void flushChatQueueForEvent( host as unknown as Parameters[0], ); - if (host.refreshSessionsAfterChat) { - host.refreshSessionsAfterChat = false; + const runId = payload?.runId; + if (runId && host.refreshSessionsAfterChat.has(runId)) { + host.refreshSessionsAfterChat.delete(runId); if (state === "final") { void loadSessions(host as unknown as OpenClawApp, { activeMinutes: 0 }); } diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index 627bf4f07..48f77d344 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -258,7 +258,7 @@ export class OpenClawApp extends LitElement { private logsScrollFrame: number | null = null; private toolStreamById = new Map(); private toolStreamOrder: string[] = []; - refreshSessionsAfterChat = false; + refreshSessionsAfterChat = new Set(); basePath = ""; private popStateHandler = () => onPopStateInternal( diff --git a/ui/src/ui/controllers/chat.ts b/ui/src/ui/controllers/chat.ts index be9c6bcdc..c2d73bd83 100644 --- a/ui/src/ui/controllers/chat.ts +++ b/ui/src/ui/controllers/chat.ts @@ -55,11 +55,11 @@ export async function sendChatMessage( state: ChatState, message: string, attachments?: ChatAttachment[], -): Promise { - if (!state.client || !state.connected) return false; +): Promise { + if (!state.client || !state.connected) return null; const msg = message.trim(); const hasAttachments = attachments && attachments.length > 0; - if (!msg && !hasAttachments) return false; + if (!msg && !hasAttachments) return null; const now = Date.now(); @@ -117,7 +117,7 @@ export async function sendChatMessage( idempotencyKey: runId, attachments: apiAttachments, }); - return true; + return runId; } catch (err) { const error = String(err); state.chatRunId = null; @@ -132,7 +132,7 @@ export async function sendChatMessage( timestamp: Date.now(), }, ]; - return false; + return null; } finally { state.chatSending = false; } diff --git a/ui/src/ui/ui-types.ts b/ui/src/ui/ui-types.ts index 196d6d114..afb80c179 100644 --- a/ui/src/ui/ui-types.ts +++ b/ui/src/ui/ui-types.ts @@ -9,6 +9,7 @@ export type ChatQueueItem = { text: string; createdAt: number; attachments?: ChatAttachment[]; + refreshSessions?: boolean; }; export const CRON_CHANNEL_LAST = "last";