diff --git a/src/auto-reply/reply.triggers.trigger-handling.shows-quick-model-picker-grouped-by-model.test.ts b/src/auto-reply/reply.triggers.trigger-handling.shows-quick-model-picker-grouped-by-model.test.ts deleted file mode 100644 index 3cf248fe871..00000000000 --- a/src/auto-reply/reply.triggers.trigger-handling.shows-quick-model-picker-grouped-by-model.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { beforeAll, describe, expect, it } from "vitest"; -import { normalizeTestText } from "../../test/helpers/normalize-text.js"; -import { loadSessionStore } from "../config/sessions.js"; -import { - installTriggerHandlingE2eTestHooks, - makeCfg, - withTempHome, -} from "./reply.triggers.trigger-handling.test-harness.js"; - -let getReplyFromConfig: typeof import("./reply.js").getReplyFromConfig; -beforeAll(async () => { - ({ getReplyFromConfig } = await import("./reply.js")); -}); - -installTriggerHandlingE2eTestHooks(); - -const DEFAULT_SESSION_KEY = "telegram:slash:111"; - -function requireSessionStorePath(cfg: { session?: { store?: string } }): string { - const storePath = cfg.session?.store; - if (!storePath) { - throw new Error("expected session store path"); - } - return storePath; -} - -function makeTelegramModelCommand(body: string, sessionKey = DEFAULT_SESSION_KEY) { - return { - Body: body, - From: "telegram:111", - To: "telegram:111", - ChatType: "direct" as const, - Provider: "telegram" as const, - Surface: "telegram" as const, - SessionKey: sessionKey, - CommandAuthorized: true, - }; -} - -function firstReplyText(reply: Awaited>) { - return Array.isArray(reply) ? (reply[0]?.text ?? "") : (reply?.text ?? ""); -} - -async function runModelCommand(home: string, body: string, sessionKey = DEFAULT_SESSION_KEY) { - const cfg = makeCfg(home); - const res = await getReplyFromConfig(makeTelegramModelCommand(body, sessionKey), {}, cfg); - const text = firstReplyText(res); - return { - cfg, - sessionKey, - text, - normalized: normalizeTestText(text), - }; -} - -describe("trigger handling", () => { - it("shows a /model summary and points to /models", async () => { - await withTempHome(async (home) => { - const { normalized } = await runModelCommand(home, "/model"); - - expect(normalized).toContain("Current: anthropic/claude-opus-4-5"); - expect(normalized).toContain("/model to switch"); - expect(normalized).toContain("Tap below to browse models"); - expect(normalized).toContain("/model status for details"); - expect(normalized).not.toContain("reasoning"); - expect(normalized).not.toContain("image"); - }); - }); - - it("aliases /model list to /models", async () => { - await withTempHome(async (home) => { - const { normalized } = await runModelCommand(home, "/model list"); - - expect(normalized).toContain("Providers:"); - expect(normalized).toContain("Use: /models "); - expect(normalized).toContain("Switch: /model "); - }); - }); - - it("selects the exact provider/model pair for openrouter", async () => { - await withTempHome(async (home) => { - const { cfg, sessionKey, normalized } = await runModelCommand( - home, - "/model openrouter/anthropic/claude-opus-4-5", - ); - - expect(normalized).toContain("Model set to openrouter/anthropic/claude-opus-4-5"); - - const store = loadSessionStore(requireSessionStorePath(cfg)); - expect(store[sessionKey]?.providerOverride).toBe("openrouter"); - expect(store[sessionKey]?.modelOverride).toBe("anthropic/claude-opus-4-5"); - }); - }); - - it("rejects invalid /model <#> selections", async () => { - await withTempHome(async (home) => { - const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model 99"); - - expect(normalized).toContain("Numeric model selection is not supported in chat."); - expect(normalized).toContain("Browse: /models or /models "); - expect(normalized).toContain("Switch: /model "); - - const store = loadSessionStore(requireSessionStorePath(cfg)); - expect(store[sessionKey]?.providerOverride).toBeUndefined(); - expect(store[sessionKey]?.modelOverride).toBeUndefined(); - }); - }); - - it("resets to the default model via /model ", async () => { - await withTempHome(async (home) => { - const { cfg, sessionKey, normalized } = await runModelCommand( - home, - "/model anthropic/claude-opus-4-5", - ); - - expect(normalized).toContain("Model reset to default (anthropic/claude-opus-4-5)"); - - const store = loadSessionStore(requireSessionStorePath(cfg)); - expect(store[sessionKey]?.providerOverride).toBeUndefined(); - expect(store[sessionKey]?.modelOverride).toBeUndefined(); - }); - }); - - it("selects a model via /model ", async () => { - await withTempHome(async (home) => { - const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model openai/gpt-5.2"); - - expect(normalized).toContain("Model set to openai/gpt-5.2"); - - const store = loadSessionStore(requireSessionStorePath(cfg)); - expect(store[sessionKey]?.providerOverride).toBe("openai"); - expect(store[sessionKey]?.modelOverride).toBe("gpt-5.2"); - }); - }); -}); diff --git a/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.test.ts b/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.test.ts index 0d5c6e2db81..dff015c0057 100644 --- a/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.test.ts +++ b/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import { join } from "node:path"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { normalizeTestText } from "../../test/helpers/normalize-text.js"; import type { OpenClawConfig } from "../config/config.js"; import { loadSessionStore } from "../config/sessions.js"; import { @@ -30,7 +31,125 @@ afterAll(() => { installTriggerHandlingE2eTestHooks(); +const DEFAULT_SESSION_KEY = "telegram:slash:111"; + +function requireSessionStorePath(cfg: { session?: { store?: string } }): string { + const storePath = cfg.session?.store; + if (!storePath) { + throw new Error("expected session store path"); + } + return storePath; +} + +function makeTelegramModelCommand(body: string, sessionKey = DEFAULT_SESSION_KEY) { + return { + Body: body, + From: "telegram:111", + To: "telegram:111", + ChatType: "direct" as const, + Provider: "telegram" as const, + Surface: "telegram" as const, + SessionKey: sessionKey, + CommandAuthorized: true, + }; +} + +function firstReplyText(reply: Awaited>) { + return Array.isArray(reply) ? (reply[0]?.text ?? "") : (reply?.text ?? ""); +} + +async function runModelCommand(home: string, body: string, sessionKey = DEFAULT_SESSION_KEY) { + const cfg = makeCfg(home); + const res = await getReplyFromConfig(makeTelegramModelCommand(body, sessionKey), {}, cfg); + const text = firstReplyText(res); + return { + cfg, + sessionKey, + text, + normalized: normalizeTestText(text), + }; +} + describe("trigger handling", () => { + it("shows a /model summary and points to /models", async () => { + await withTempHome(async (home) => { + const { normalized } = await runModelCommand(home, "/model"); + + expect(normalized).toContain("Current: anthropic/claude-opus-4-5"); + expect(normalized).toContain("/model to switch"); + expect(normalized).toContain("Tap below to browse models"); + expect(normalized).toContain("/model status for details"); + expect(normalized).not.toContain("reasoning"); + expect(normalized).not.toContain("image"); + }); + }); + + it("aliases /model list to /models", async () => { + await withTempHome(async (home) => { + const { normalized } = await runModelCommand(home, "/model list"); + + expect(normalized).toContain("Providers:"); + expect(normalized).toContain("Use: /models "); + expect(normalized).toContain("Switch: /model "); + }); + }); + + it("selects the exact provider/model pair for openrouter", async () => { + await withTempHome(async (home) => { + const { cfg, sessionKey, normalized } = await runModelCommand( + home, + "/model openrouter/anthropic/claude-opus-4-5", + ); + + expect(normalized).toContain("Model set to openrouter/anthropic/claude-opus-4-5"); + + const store = loadSessionStore(requireSessionStorePath(cfg)); + expect(store[sessionKey]?.providerOverride).toBe("openrouter"); + expect(store[sessionKey]?.modelOverride).toBe("anthropic/claude-opus-4-5"); + }); + }); + + it("rejects invalid /model <#> selections", async () => { + await withTempHome(async (home) => { + const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model 99"); + + expect(normalized).toContain("Numeric model selection is not supported in chat."); + expect(normalized).toContain("Browse: /models or /models "); + expect(normalized).toContain("Switch: /model "); + + const store = loadSessionStore(requireSessionStorePath(cfg)); + expect(store[sessionKey]?.providerOverride).toBeUndefined(); + expect(store[sessionKey]?.modelOverride).toBeUndefined(); + }); + }); + + it("resets to the default model via /model ", async () => { + await withTempHome(async (home) => { + const { cfg, sessionKey, normalized } = await runModelCommand( + home, + "/model anthropic/claude-opus-4-5", + ); + + expect(normalized).toContain("Model reset to default (anthropic/claude-opus-4-5)"); + + const store = loadSessionStore(requireSessionStorePath(cfg)); + expect(store[sessionKey]?.providerOverride).toBeUndefined(); + expect(store[sessionKey]?.modelOverride).toBeUndefined(); + }); + }); + + it("selects a model via /model ", async () => { + await withTempHome(async (home) => { + const { cfg, sessionKey, normalized } = await runModelCommand(home, "/model openai/gpt-5.2"); + + expect(normalized).toContain("Model set to openai/gpt-5.2"); + + const store = loadSessionStore(requireSessionStorePath(cfg)); + expect(store[sessionKey]?.providerOverride).toBe("openai"); + expect(store[sessionKey]?.modelOverride).toBe("gpt-5.2"); + }); + }); + it("targets the active session for native /stop", async () => { await withTempHome(async (home) => { const cfg = makeCfg(home);