From 920b3880c1465e5978041dcdd2a39018b9f620db Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 10 Jan 2026 05:31:48 +0100 Subject: [PATCH] test: add elevated mode regressions --- src/auto-reply/reply.directive.test.ts | 230 +++++++++++++++++++++++++ src/auto-reply/reply.triggers.test.ts | 5 + src/auto-reply/status.test.ts | 18 ++ src/gateway/sessions-patch.test.ts | 60 +++++++ 4 files changed, 313 insertions(+) create mode 100644 src/gateway/sessions-patch.test.ts diff --git a/src/auto-reply/reply.directive.test.ts b/src/auto-reply/reply.directive.test.ts index 4cbcd2b32..1f86df32d 100644 --- a/src/auto-reply/reply.directive.test.ts +++ b/src/auto-reply/reply.directive.test.ts @@ -558,6 +558,236 @@ describe("directive behavior", () => { }); }); + it("persists elevated off and reflects it in /status (even when default is on)", async () => { + await withTempHome(async (home) => { + vi.mocked(runEmbeddedPiAgent).mockReset(); + const storePath = path.join(home, "sessions.json"); + + const res = await getReplyFromConfig( + { + Body: "/elevated off\n/status", + From: "+1222", + To: "+1222", + Provider: "whatsapp", + SenderE164: "+1222", + }, + {}, + { + agents: { + defaults: { + model: "anthropic/claude-opus-4-5", + workspace: path.join(home, "clawd"), + elevatedDefault: "on", + }, + }, + tools: { + elevated: { + allowFrom: { whatsapp: ["+1222"] }, + }, + }, + whatsapp: { allowFrom: ["+1222"] }, + session: { store: storePath }, + }, + ); + + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("Elevated mode disabled."); + const optionsLine = text + ?.split("\n") + .find((line) => line.trim().startsWith("⚙️")); + expect(optionsLine).toBeTruthy(); + expect(optionsLine).not.toContain("elevated"); + + const store = loadSessionStore(storePath); + expect(store["agent:main:main"]?.elevatedLevel).toBe("off"); + expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); + }); + }); + + it("strips inline elevated directives from the user text (does not persist session override)", async () => { + await withTempHome(async (home) => { + vi.mocked(runEmbeddedPiAgent).mockResolvedValue({ + payloads: [{ text: "ok" }], + meta: { + durationMs: 1, + agentMeta: { sessionId: "s", provider: "p", model: "m" }, + }, + }); + const storePath = path.join(home, "sessions.json"); + + await getReplyFromConfig( + { + Body: "hello there /elevated off", + From: "+1222", + To: "+1222", + Provider: "whatsapp", + SenderE164: "+1222", + }, + {}, + { + agents: { + defaults: { + model: "anthropic/claude-opus-4-5", + workspace: path.join(home, "clawd"), + elevatedDefault: "on", + }, + }, + tools: { + elevated: { + allowFrom: { whatsapp: ["+1222"] }, + }, + }, + whatsapp: { allowFrom: ["+1222"] }, + session: { store: storePath }, + }, + ); + + const store = loadSessionStore(storePath); + expect(store["agent:main:main"]?.elevatedLevel).toBeUndefined(); + + const calls = vi.mocked(runEmbeddedPiAgent).mock.calls; + expect(calls.length).toBeGreaterThan(0); + const call = calls[0]?.[0]; + expect(call?.prompt).toContain("hello there"); + expect(call?.prompt).not.toContain("/elevated"); + }); + }); + + it("shows current elevated level as off after toggling it off", async () => { + await withTempHome(async (home) => { + vi.mocked(runEmbeddedPiAgent).mockReset(); + const storePath = path.join(home, "sessions.json"); + + await getReplyFromConfig( + { + Body: "/elevated off", + From: "+1222", + To: "+1222", + Provider: "whatsapp", + SenderE164: "+1222", + }, + {}, + { + agents: { + defaults: { + model: "anthropic/claude-opus-4-5", + workspace: path.join(home, "clawd"), + elevatedDefault: "on", + }, + }, + tools: { + elevated: { + allowFrom: { whatsapp: ["+1222"] }, + }, + }, + whatsapp: { allowFrom: ["+1222"] }, + session: { store: storePath }, + }, + ); + + const res = await getReplyFromConfig( + { + Body: "/elevated", + From: "+1222", + To: "+1222", + Provider: "whatsapp", + SenderE164: "+1222", + }, + {}, + { + agents: { + defaults: { + model: "anthropic/claude-opus-4-5", + workspace: path.join(home, "clawd"), + elevatedDefault: "on", + }, + }, + tools: { + elevated: { + allowFrom: { whatsapp: ["+1222"] }, + }, + }, + whatsapp: { allowFrom: ["+1222"] }, + session: { store: storePath }, + }, + ); + + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("Current elevated level: off"); + expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); + }); + }); + + it("can toggle elevated off then back on (status reflects on)", async () => { + await withTempHome(async (home) => { + vi.mocked(runEmbeddedPiAgent).mockReset(); + const storePath = path.join(home, "sessions.json"); + + const cfg = { + agents: { + defaults: { + model: "anthropic/claude-opus-4-5", + workspace: path.join(home, "clawd"), + elevatedDefault: "on", + }, + }, + tools: { + elevated: { + allowFrom: { whatsapp: ["+1222"] }, + }, + }, + whatsapp: { allowFrom: ["+1222"] }, + session: { store: storePath }, + } as const; + + await getReplyFromConfig( + { + Body: "/elevated off", + From: "+1222", + To: "+1222", + Provider: "whatsapp", + SenderE164: "+1222", + }, + {}, + cfg, + ); + await getReplyFromConfig( + { + Body: "/elevated on", + From: "+1222", + To: "+1222", + Provider: "whatsapp", + SenderE164: "+1222", + }, + {}, + cfg, + ); + + const res = await getReplyFromConfig( + { + Body: "/status", + From: "+1222", + To: "+1222", + Provider: "whatsapp", + SenderE164: "+1222", + }, + {}, + cfg, + ); + + const text = Array.isArray(res) ? res[0]?.text : res?.text; + const optionsLine = text + ?.split("\n") + .find((line) => line.trim().startsWith("⚙️")); + expect(optionsLine).toBeTruthy(); + expect(optionsLine).toContain("elevated"); + + const store = loadSessionStore(storePath); + expect(store["agent:main:main"]?.elevatedLevel).toBe("on"); + expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); + }); + }); + it("rejects per-agent elevated when disabled", async () => { await withTempHome(async (home) => { vi.mocked(runEmbeddedPiAgent).mockReset(); diff --git a/src/auto-reply/reply.triggers.test.ts b/src/auto-reply/reply.triggers.test.ts index 4d0880218..0d410f703 100644 --- a/src/auto-reply/reply.triggers.test.ts +++ b/src/auto-reply/reply.triggers.test.ts @@ -580,6 +580,11 @@ describe("trigger handling", () => { ); const text = Array.isArray(res) ? res[0]?.text : res?.text; expect(text).toContain("Elevated mode disabled."); + + const store = loadSessionStore(cfg.session.store); + expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe( + "off", + ); }); }); diff --git a/src/auto-reply/status.test.ts b/src/auto-reply/status.test.ts index 81dc1f4a7..98ebd6d2b 100644 --- a/src/auto-reply/status.test.ts +++ b/src/auto-reply/status.test.ts @@ -90,6 +90,24 @@ describe("buildStatusMessage", () => { expect(text).toContain("elevated"); }); + it("does not show elevated label when session explicitly disables it", () => { + const text = buildStatusMessage({ + agent: { model: "anthropic/claude-opus-4-5", elevatedDefault: "on" }, + sessionEntry: { sessionId: "v1", updatedAt: 0, elevatedLevel: "off" }, + sessionKey: "agent:main:main", + sessionScope: "per-sender", + resolvedThink: "low", + resolvedVerbose: "off", + queue: { mode: "collect", depth: 0 }, + }); + + const optionsLine = text + .split("\n") + .find((line) => line.trim().startsWith("⚙️")); + expect(optionsLine).toBeTruthy(); + expect(optionsLine).not.toContain("elevated"); + }); + it("prefers model overrides over last-run model", () => { const text = buildStatusMessage({ agent: { diff --git a/src/gateway/sessions-patch.test.ts b/src/gateway/sessions-patch.test.ts new file mode 100644 index 000000000..cd330fa0a --- /dev/null +++ b/src/gateway/sessions-patch.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, test } from "vitest"; +import type { ClawdbotConfig } from "../config/config.js"; +import type { SessionEntry } from "../config/sessions.js"; +import { applySessionsPatchToStore } from "./sessions-patch.js"; + +describe("gateway sessions patch", () => { + test("persists elevatedLevel=off (does not clear)", async () => { + const store: Record = {}; + const res = await applySessionsPatchToStore({ + cfg: {} as ClawdbotConfig, + store, + storeKey: "agent:main:main", + patch: { elevatedLevel: "off" }, + }); + expect(res.ok).toBe(true); + if (!res.ok) return; + expect(res.entry.elevatedLevel).toBe("off"); + }); + + test("persists elevatedLevel=on", async () => { + const store: Record = {}; + const res = await applySessionsPatchToStore({ + cfg: {} as ClawdbotConfig, + store, + storeKey: "agent:main:main", + patch: { elevatedLevel: "on" }, + }); + expect(res.ok).toBe(true); + if (!res.ok) return; + expect(res.entry.elevatedLevel).toBe("on"); + }); + + test("clears elevatedLevel when patch sets null", async () => { + const store: Record = { + "agent:main:main": { elevatedLevel: "off" } as SessionEntry, + }; + const res = await applySessionsPatchToStore({ + cfg: {} as ClawdbotConfig, + store, + storeKey: "agent:main:main", + patch: { elevatedLevel: null }, + }); + expect(res.ok).toBe(true); + if (!res.ok) return; + expect(res.entry.elevatedLevel).toBeUndefined(); + }); + + test("rejects invalid elevatedLevel values", async () => { + const store: Record = {}; + const res = await applySessionsPatchToStore({ + cfg: {} as ClawdbotConfig, + store, + storeKey: "agent:main:main", + patch: { elevatedLevel: "maybe" }, + }); + expect(res.ok).toBe(false); + if (res.ok) return; + expect(res.error.message).toContain("invalid elevatedLevel"); + }); +});