mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-19 01:01:37 +00:00
fix(ui): send idle stop aliases as normal messages (#105742)
* fix(ui): send idle stop aliases as messages * chore: leave release notes to release automation
This commit is contained in:
committed by
GitHub
parent
f2e46f1d23
commit
a6e87f7caf
@@ -427,6 +427,30 @@ describeControlUiE2e("Control UI mocked Gateway E2E", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("sends idle stop aliases as ordinary chat messages", async () => {
|
||||
const context = await newBrowserContext({
|
||||
locale: "en-US",
|
||||
serviceWorkers: "block",
|
||||
viewport: { height: 900, width: 1280 },
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const gateway = await installMockGateway(page);
|
||||
|
||||
try {
|
||||
await page.goto(`${server.baseUrl}chat`);
|
||||
const composer = page.locator(".agent-chat__composer-combobox textarea");
|
||||
await composer.waitFor({ state: "visible", timeout: 10_000 });
|
||||
await composer.fill("wait");
|
||||
await page.getByRole("button", { name: "Send message" }).click();
|
||||
|
||||
const sendRequest = await gateway.waitForRequest("chat.send");
|
||||
expect(requireRecord(sendRequest.params).message).toBe("wait");
|
||||
expect(await gateway.getRequests("chat.abort")).toHaveLength(0);
|
||||
} finally {
|
||||
await closeBrowserContext(context);
|
||||
}
|
||||
});
|
||||
|
||||
it("persists the chat send shortcut and keeps multiline and IME input safe", async () => {
|
||||
const context = await newBrowserContext({
|
||||
locale: "en-US",
|
||||
|
||||
@@ -1265,6 +1265,32 @@ describe("handleSendChat", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.each(["stop", "esc", "abort", "wait", "exit"])(
|
||||
"sends the idle conversational word %s as a normal message",
|
||||
async (message) => {
|
||||
const request = vi.fn(async (method: string) => {
|
||||
if (method === "chat.send") {
|
||||
return { runId: `idle-${message}`, status: "started" };
|
||||
}
|
||||
throw new Error(`Unexpected request: ${method}`);
|
||||
});
|
||||
const host = makeHost({
|
||||
client: { request } as unknown as ChatHost["client"],
|
||||
chatMessage: message,
|
||||
sessionKey: "agent:main",
|
||||
});
|
||||
|
||||
await handleSendChat(host);
|
||||
|
||||
expect(request).toHaveBeenCalledWith(
|
||||
"chat.send",
|
||||
expect.objectContaining({ message, sessionKey: "agent:main" }),
|
||||
);
|
||||
expect(request).not.toHaveBeenCalledWith("chat.abort", expect.anything());
|
||||
expect(host.chatMessage).toBe("");
|
||||
},
|
||||
);
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
@@ -7543,23 +7569,26 @@ describe("handleAbortChat", () => {
|
||||
expect(host.chatRunId).toBe("run-main");
|
||||
});
|
||||
|
||||
it("clears typed stop commands after aborting the active run", async () => {
|
||||
const request = vi.fn(async () => ({ aborted: true }));
|
||||
const host = makeHost({
|
||||
client: { request } as unknown as ChatHost["client"],
|
||||
chatRunId: "run-main",
|
||||
chatMessage: "/stop",
|
||||
sessionKey: "agent:main",
|
||||
});
|
||||
it.each(["/stop", "stop", "esc", "abort", "wait", "exit"])(
|
||||
"clears the typed stop command %s after aborting the active run",
|
||||
async (message) => {
|
||||
const request = vi.fn(async () => ({ aborted: true }));
|
||||
const host = makeHost({
|
||||
client: { request } as unknown as ChatHost["client"],
|
||||
chatRunId: "run-main",
|
||||
chatMessage: message,
|
||||
sessionKey: "agent:main",
|
||||
});
|
||||
|
||||
await handleSendChat(host);
|
||||
await handleSendChat(host);
|
||||
|
||||
expect(request).toHaveBeenCalledWith("chat.abort", {
|
||||
runId: "run-main",
|
||||
sessionKey: "agent:main",
|
||||
});
|
||||
expect(host.chatMessage).toBe("");
|
||||
});
|
||||
expect(request).toHaveBeenCalledWith("chat.abort", {
|
||||
runId: "run-main",
|
||||
sessionKey: "agent:main",
|
||||
});
|
||||
expect(host.chatMessage).toBe("");
|
||||
},
|
||||
);
|
||||
|
||||
it("queues the active run abort while disconnected", async () => {
|
||||
const host = makeHost({
|
||||
|
||||
@@ -96,6 +96,7 @@ import { controlUiNowMs, roundedControlUiDurationMs } from "./performance.ts";
|
||||
import type { RenderLifecycle } from "./render-lifecycle.ts";
|
||||
import {
|
||||
handleAbortChat,
|
||||
hasAbortableSessionRun,
|
||||
isChatBusy,
|
||||
isChatStopCommand,
|
||||
reconcileChatRunLifecycle,
|
||||
@@ -2167,7 +2168,12 @@ export async function handleSendChat(
|
||||
}
|
||||
|
||||
if (shouldInterpretChatCommands) {
|
||||
if (isChatStopCommand(message)) {
|
||||
// Natural words such as "wait" and "exit" are stop aliases only while a
|
||||
// run exists. Keep the explicit /stop command available at any time.
|
||||
const shouldAbort =
|
||||
isChatStopCommand(message) &&
|
||||
(message.trim().startsWith("/") || hasAbortableSessionRun(host));
|
||||
if (shouldAbort) {
|
||||
if (messageOverride == null) {
|
||||
recordNonTranscriptInputHistory(host, message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user