fix(ui): reset /model default through server directive (#129895)

* fix(ui): reset model through server directive

* test(ui): target visible mobile transcript message
This commit is contained in:
Peter Steinberger
2026-08-26 00:08:42 -07:00
committed by GitHub
parent 6d1c1c9f57
commit 860e8e301f
4 changed files with 46 additions and 13 deletions
+4 -4
View File
@@ -46,14 +46,14 @@ type DefineChatCommandInput = {
};
/**
* Keep simple model selections on fast client-side patch paths. Multi-token
* forms can carry runtime selectors or a prompt, so the server directive parser
* must own the full atomic transaction.
* Keep simple model selections on fast client-side patch paths. Semantic reset
* and multi-token forms require the server directive parser to own the full
* atomic transaction.
*/
export function shouldForwardModelCommandToServer(rawArgs: string): boolean {
const args = rawArgs.trim();
const normalized = args.toLowerCase();
return normalized === "list" || normalized === "status" || /\s/u.test(args);
return ["default", "list", "status"].includes(normalized) || /\s/u.test(args);
}
/** Defines one command with normalized aliases, scope, and argument parsing defaults. */
+16 -8
View File
@@ -2381,13 +2381,10 @@ describe("tui command handlers", () => {
it.each([
{ mode: "gateway", local: false, command: "/think default", field: "thinkingLevel" },
{ mode: "gateway", local: false, command: "/fast default", field: "fastMode" },
{ mode: "gateway", local: false, command: "/model default", field: "model" },
{ mode: "embedded", local: true, command: "/think default", field: "thinkingLevel" },
{ mode: "embedded", local: true, command: "/fast default", field: "fastMode" },
{ mode: "embedded", local: true, command: "/model default", field: "model" },
{ mode: "gateway", local: false, command: "/think inherit", field: "thinkingLevel" },
{ mode: "embedded", local: true, command: "/fast reset", field: "fastMode" },
{ mode: "gateway", local: false, command: "/model DEFAULT", field: "model" },
])(
"clears the $field session override for $command in $mode mode",
async ({ local, command, field }) => {
@@ -2994,13 +2991,24 @@ describe("tui command handlers", () => {
expect(closeOverlay).toHaveBeenCalledTimes(1);
});
it.each(["codex", "openclaw"])(
"forwards model/runtime transactions through the server directive path for %s",
async (runtime) => {
it.each([
{ local: false, command: "/model default" },
{ local: true, command: "/model default" },
{ local: false, command: "/model DEFAULT" },
{
local: false,
command: "/model openai/gpt-5.6-luna --runtime codex continue with this model",
},
{
local: false,
command: "/model openai/gpt-5.6-luna --runtime openclaw continue with this model",
},
])(
"forwards $command through the server directive path (local: $local)",
async ({ command, local }) => {
const sendChat = vi.fn().mockResolvedValue({ status: "ok" });
const patchSession = vi.fn();
const command = `/model openai/gpt-5.6-luna --runtime ${runtime} continue with this model`;
const { handleCommand } = createHarness({ sendChat, patchSession });
const { handleCommand } = createHarness({ sendChat, patchSession, opts: { local } });
await handleCommand(command);
@@ -173,7 +173,10 @@ suite.define(() => {
});
await page.goto(controlUiSessionUrl(suite.server.baseUrl, sessionKey));
const activePane = page.locator("openclaw-chat-pane.chat-pane-cache__pane--active");
await activePane.getByText("Mobile session menu proof.", { exact: true }).waitFor();
await activePane
.getByRole("paragraph")
.filter({ hasText: /^Mobile session menu proof\.$/ })
.waitFor();
const menuTrigger = activePane.getByRole("button", {
name: "Actions for Terminal continuation",
+22
View File
@@ -3428,6 +3428,28 @@ describe("handleSendChat", () => {
expect(refreshCurrentSessionTools).toHaveBeenCalledTimes(1);
});
it.each(["/model default", "/model DEFAULT"])(
"forwards semantic model reset commands through chat.send: %s",
async (command) => {
const host = makeChatHost({
requestHandlers: {
"chat.send": { status: "started" },
"sessions.patch": createResolvedModelPatch("default", "openai"),
},
sessionKey: "main",
chatMessage: command,
});
await handleSendChat(host);
expect(host.request).toHaveBeenCalledWith(
"chat.send",
expect.objectContaining({ message: command, sessionKey: "main" }),
);
expect(host.request).not.toHaveBeenCalledWith("sessions.patch", expect.anything());
},
);
it("queues local slash commands while the gateway client is unavailable", async () => {
const host = makeChatHost({
client: null,