feat(ui): refresh session list after chat commands in Web UI
This commit is contained in:
parent
34bdbdb405
commit
0b7aa8cf1d
6 changed files with 29 additions and 16 deletions
|
|
@ -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.
|
- 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.
|
- 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: 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.
|
- 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.
|
- 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.
|
- Discord: stop resolveDiscordTarget from passing directory params into messaging target parsers. Fixes #3167. Thanks @thewilloftheshadow.
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ type ChatHost = {
|
||||||
basePath: string;
|
basePath: string;
|
||||||
hello: GatewayHelloOk | null;
|
hello: GatewayHelloOk | null;
|
||||||
chatAvatarUrl: string | null;
|
chatAvatarUrl: string | null;
|
||||||
refreshSessionsAfterChat: boolean;
|
refreshSessionsAfterChat: Set<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isChatBusy(host: ChatHost) {
|
export function isChatBusy(host: ChatHost) {
|
||||||
|
|
@ -56,7 +56,12 @@ export async function handleAbortChat(host: ChatHost) {
|
||||||
await abortChatRun(host as unknown as OpenClawApp);
|
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 trimmed = text.trim();
|
||||||
const hasAttachments = Boolean(attachments && attachments.length > 0);
|
const hasAttachments = Boolean(attachments && attachments.length > 0);
|
||||||
if (!trimmed && !hasAttachments) return;
|
if (!trimmed && !hasAttachments) return;
|
||||||
|
|
@ -67,6 +72,7 @@ function enqueueChatMessage(host: ChatHost, text: string, attachments?: ChatAtta
|
||||||
text: trimmed,
|
text: trimmed,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
attachments: hasAttachments ? attachments?.map((att) => ({ ...att })) : undefined,
|
attachments: hasAttachments ? attachments?.map((att) => ({ ...att })) : undefined,
|
||||||
|
refreshSessions,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +90,8 @@ async function sendChatMessageNow(
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
resetToolStream(host as unknown as Parameters<typeof resetToolStream>[0]);
|
resetToolStream(host as unknown as Parameters<typeof resetToolStream>[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) {
|
if (!ok && opts?.previousDraft != null) {
|
||||||
host.chatMessage = opts.previousDraft;
|
host.chatMessage = opts.previousDraft;
|
||||||
}
|
}
|
||||||
|
|
@ -104,8 +111,8 @@ async function sendChatMessageNow(
|
||||||
if (ok && !host.chatRunId) {
|
if (ok && !host.chatRunId) {
|
||||||
void flushChatQueue(host);
|
void flushChatQueue(host);
|
||||||
}
|
}
|
||||||
if (ok && opts?.refreshSessions) {
|
if (ok && opts?.refreshSessions && runId) {
|
||||||
host.refreshSessionsAfterChat = true;
|
host.refreshSessionsAfterChat.add(runId);
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +122,10 @@ async function flushChatQueue(host: ChatHost) {
|
||||||
const [next, ...rest] = host.chatQueue;
|
const [next, ...rest] = host.chatQueue;
|
||||||
if (!next) return;
|
if (!next) return;
|
||||||
host.chatQueue = rest;
|
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) {
|
if (!ok) {
|
||||||
host.chatQueue = [next, ...host.chatQueue];
|
host.chatQueue = [next, ...host.chatQueue];
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +163,7 @@ export async function handleSendChat(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChatBusy(host)) {
|
if (isChatBusy(host)) {
|
||||||
enqueueChatMessage(host, message, attachmentsToSend);
|
enqueueChatMessage(host, message, attachmentsToSend, refreshSessions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ type GatewayHost = {
|
||||||
assistantAgentId: string | null;
|
assistantAgentId: string | null;
|
||||||
sessionKey: string;
|
sessionKey: string;
|
||||||
chatRunId: string | null;
|
chatRunId: string | null;
|
||||||
refreshSessionsAfterChat: boolean;
|
refreshSessionsAfterChat: Set<string>;
|
||||||
execApprovalQueue: ExecApprovalRequest[];
|
execApprovalQueue: ExecApprovalRequest[];
|
||||||
execApprovalError: string | null;
|
execApprovalError: string | null;
|
||||||
};
|
};
|
||||||
|
|
@ -196,8 +196,9 @@ function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) {
|
||||||
void flushChatQueueForEvent(
|
void flushChatQueueForEvent(
|
||||||
host as unknown as Parameters<typeof flushChatQueueForEvent>[0],
|
host as unknown as Parameters<typeof flushChatQueueForEvent>[0],
|
||||||
);
|
);
|
||||||
if (host.refreshSessionsAfterChat) {
|
const runId = payload?.runId;
|
||||||
host.refreshSessionsAfterChat = false;
|
if (runId && host.refreshSessionsAfterChat.has(runId)) {
|
||||||
|
host.refreshSessionsAfterChat.delete(runId);
|
||||||
if (state === "final") {
|
if (state === "final") {
|
||||||
void loadSessions(host as unknown as OpenClawApp, { activeMinutes: 0 });
|
void loadSessions(host as unknown as OpenClawApp, { activeMinutes: 0 });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ export class OpenClawApp extends LitElement {
|
||||||
private logsScrollFrame: number | null = null;
|
private logsScrollFrame: number | null = null;
|
||||||
private toolStreamById = new Map<string, ToolStreamEntry>();
|
private toolStreamById = new Map<string, ToolStreamEntry>();
|
||||||
private toolStreamOrder: string[] = [];
|
private toolStreamOrder: string[] = [];
|
||||||
refreshSessionsAfterChat = false;
|
refreshSessionsAfterChat = new Set<string>();
|
||||||
basePath = "";
|
basePath = "";
|
||||||
private popStateHandler = () =>
|
private popStateHandler = () =>
|
||||||
onPopStateInternal(
|
onPopStateInternal(
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,11 @@ export async function sendChatMessage(
|
||||||
state: ChatState,
|
state: ChatState,
|
||||||
message: string,
|
message: string,
|
||||||
attachments?: ChatAttachment[],
|
attachments?: ChatAttachment[],
|
||||||
): Promise<boolean> {
|
): Promise<string | null> {
|
||||||
if (!state.client || !state.connected) return false;
|
if (!state.client || !state.connected) return null;
|
||||||
const msg = message.trim();
|
const msg = message.trim();
|
||||||
const hasAttachments = attachments && attachments.length > 0;
|
const hasAttachments = attachments && attachments.length > 0;
|
||||||
if (!msg && !hasAttachments) return false;
|
if (!msg && !hasAttachments) return null;
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ export async function sendChatMessage(
|
||||||
idempotencyKey: runId,
|
idempotencyKey: runId,
|
||||||
attachments: apiAttachments,
|
attachments: apiAttachments,
|
||||||
});
|
});
|
||||||
return true;
|
return runId;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = String(err);
|
const error = String(err);
|
||||||
state.chatRunId = null;
|
state.chatRunId = null;
|
||||||
|
|
@ -132,7 +132,7 @@ export async function sendChatMessage(
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return false;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
state.chatSending = false;
|
state.chatSending = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export type ChatQueueItem = {
|
||||||
text: string;
|
text: string;
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
attachments?: ChatAttachment[];
|
attachments?: ChatAttachment[];
|
||||||
|
refreshSessions?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CRON_CHANNEL_LAST = "last";
|
export const CRON_CHANNEL_LAST = "last";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue