fix(ui): break app chat settings cycle
This commit is contained in:
parent
d518260bb8
commit
cd8fbf16b1
@ -2,6 +2,40 @@
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { handleSendChat, refreshChatAvatar, type ChatHost } from "./app-chat.ts";
|
||||
import { sendChatMessage } from "./controllers/chat.ts";
|
||||
import { saveSettings, type UiSettings } from "./storage.ts";
|
||||
|
||||
vi.mock("./controllers/chat.ts", () => ({
|
||||
abortChatRun: vi.fn(),
|
||||
loadChatHistory: vi.fn(),
|
||||
sendChatMessage: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./storage.ts", async () => {
|
||||
const actual = await vi.importActual<typeof import("./storage.ts")>("./storage.ts");
|
||||
return {
|
||||
...actual,
|
||||
saveSettings: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./app-scroll.ts", async () => {
|
||||
const actual = await vi.importActual<typeof import("./app-scroll.ts")>("./app-scroll.ts");
|
||||
return {
|
||||
...actual,
|
||||
resetChatScroll: vi.fn(),
|
||||
scheduleChatScroll: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./app-tool-stream.ts", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("./app-tool-stream.ts")>("./app-tool-stream.ts");
|
||||
return {
|
||||
...actual,
|
||||
resetToolStream: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
function makeHost(overrides?: Partial<ChatHost>): ChatHost {
|
||||
return {
|
||||
@ -71,6 +105,7 @@ describe("refreshChatAvatar", () => {
|
||||
describe("handleSendChat", () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("keeps slash-command model changes in sync with the chat header cache", async () => {
|
||||
@ -128,4 +163,48 @@ describe("handleSendChat", () => {
|
||||
value: "openai/gpt-5-mini",
|
||||
});
|
||||
});
|
||||
|
||||
it("persists the last active session key without depending on app-settings", async () => {
|
||||
vi.mocked(sendChatMessage).mockResolvedValueOnce("run-1");
|
||||
|
||||
const settings: UiSettings = {
|
||||
gatewayUrl: "ws://localhost:18789",
|
||||
token: "",
|
||||
sessionKey: "main",
|
||||
lastActiveSessionKey: "main",
|
||||
theme: "claw",
|
||||
themeMode: "system",
|
||||
chatFocusMode: false,
|
||||
chatShowThinking: true,
|
||||
chatShowToolCalls: true,
|
||||
splitRatio: 0.6,
|
||||
navCollapsed: false,
|
||||
navWidth: 220,
|
||||
navGroupsCollapsed: {},
|
||||
borderRadius: 50,
|
||||
};
|
||||
const host = Object.assign(
|
||||
makeHost({
|
||||
chatMessage: "hello",
|
||||
sessionKey: "agent:ops:main",
|
||||
}),
|
||||
{
|
||||
applySessionKey: "main",
|
||||
settings,
|
||||
},
|
||||
) as ChatHost & { applySessionKey: string; settings: UiSettings };
|
||||
|
||||
await handleSendChat(host);
|
||||
|
||||
expect(vi.mocked(sendChatMessage)).toHaveBeenCalledWith(
|
||||
host as unknown as Parameters<typeof sendChatMessage>[0],
|
||||
"hello",
|
||||
undefined,
|
||||
);
|
||||
expect(host.settings.lastActiveSessionKey).toBe("agent:ops:main");
|
||||
expect(host.applySessionKey).toBe("agent:ops:main");
|
||||
expect(vi.mocked(saveSettings)).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ lastActiveSessionKey: "agent:ops:main" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { parseAgentSessionKey } from "../../../src/sessions/session-key-utils.js";
|
||||
import { scheduleChatScroll, resetChatScroll } from "./app-scroll.ts";
|
||||
import { setLastActiveSessionKey } from "./app-settings.ts";
|
||||
import { resetToolStream } from "./app-tool-stream.ts";
|
||||
import type { OpenClawApp } from "./app.ts";
|
||||
import { executeSlashCommand } from "./chat/slash-command-executor.ts";
|
||||
@ -10,6 +9,7 @@ import { loadModels } from "./controllers/models.ts";
|
||||
import { loadSessions } from "./controllers/sessions.ts";
|
||||
import type { GatewayBrowserClient, GatewayHelloOk } from "./gateway.ts";
|
||||
import { normalizeBasePath } from "./navigation.ts";
|
||||
import { saveSettings, type UiSettings } from "./storage.ts";
|
||||
import type { ChatModelOverride, ModelCatalogEntry } from "./types.ts";
|
||||
import type { ChatAttachment, ChatQueueItem } from "./ui-types.ts";
|
||||
import { generateUUID } from "./uuid.ts";
|
||||
@ -38,6 +38,11 @@ export type ChatHost = {
|
||||
onSlashAction?: (action: string) => void;
|
||||
};
|
||||
|
||||
type ChatSessionSettingsHost = {
|
||||
settings: UiSettings;
|
||||
applySessionKey: string;
|
||||
};
|
||||
|
||||
export const CHAT_SESSIONS_ACTIVE_MINUTES = 120;
|
||||
|
||||
export function isChatBusy(host: ChatHost) {
|
||||
@ -82,6 +87,33 @@ export async function handleAbortChat(host: ChatHost) {
|
||||
await abortChatRun(host as unknown as OpenClawApp);
|
||||
}
|
||||
|
||||
function isChatSessionSettingsHost(host: ChatHost): host is ChatHost & ChatSessionSettingsHost {
|
||||
return (
|
||||
"settings" in host &&
|
||||
typeof host.settings === "object" &&
|
||||
host.settings !== null &&
|
||||
"applySessionKey" in host &&
|
||||
typeof host.applySessionKey === "string"
|
||||
);
|
||||
}
|
||||
|
||||
function persistLastActiveSessionKey(host: ChatHost, next: string) {
|
||||
if (!isChatSessionSettingsHost(host)) {
|
||||
return;
|
||||
}
|
||||
const trimmed = next.trim();
|
||||
if (!trimmed || host.settings.lastActiveSessionKey === trimmed) {
|
||||
return;
|
||||
}
|
||||
const settings = {
|
||||
...host.settings,
|
||||
lastActiveSessionKey: trimmed,
|
||||
};
|
||||
host.settings = settings;
|
||||
host.applySessionKey = settings.lastActiveSessionKey;
|
||||
saveSettings(settings);
|
||||
}
|
||||
|
||||
function enqueueChatMessage(
|
||||
host: ChatHost,
|
||||
text: string,
|
||||
@ -132,10 +164,7 @@ async function sendChatMessageNow(
|
||||
host.chatAttachments = opts.previousAttachments;
|
||||
}
|
||||
if (ok) {
|
||||
setLastActiveSessionKey(
|
||||
host as unknown as Parameters<typeof setLastActiveSessionKey>[0],
|
||||
host.sessionKey,
|
||||
);
|
||||
persistLastActiveSessionKey(host, host.sessionKey);
|
||||
}
|
||||
if (ok && opts?.restoreDraft && opts.previousDraft?.trim()) {
|
||||
host.chatMessage = opts.previousDraft;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user