test(auto-reply): dedupe directive behavior e2e fixtures

This commit is contained in:
Peter Steinberger
2026-02-18 12:20:40 +00:00
parent 3c886ee98b
commit 2fd211b705
6 changed files with 185 additions and 348 deletions

View File

@@ -1,48 +1,20 @@
import "./reply.directive.directive-behavior.e2e-mocks.js";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
installDirectiveBehaviorE2EHooks,
loadModelCatalog,
makeEmbeddedTextResult,
makeWhatsAppDirectiveConfig,
mockEmbeddedTextResult,
replyText,
replyTexts,
runEmbeddedPiAgent,
withTempHome,
} from "./reply.directive.directive-behavior.e2e-harness.js";
import { getReplyFromConfig } from "./reply.js";
function makeThinkConfig(home: string) {
return {
agents: {
defaults: {
model: "anthropic/claude-opus-4-5",
workspace: path.join(home, "openclaw"),
},
},
session: { store: path.join(home, "sessions.json") },
} as unknown as OpenClawConfig;
}
function makeWhatsAppConfig(home: string) {
return {
agents: {
defaults: {
model: "anthropic/claude-opus-4-5",
workspace: path.join(home, "openclaw"),
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: path.join(home, "sessions.json") },
} as unknown as OpenClawConfig;
}
async function runReplyToCurrentCase(home: string, text: string) {
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
payloads: [{ text }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
});
vi.mocked(runEmbeddedPiAgent).mockResolvedValue(makeEmbeddedTextResult(text));
const res = await getReplyFromConfig(
{
@@ -52,7 +24,7 @@ async function runReplyToCurrentCase(home: string, text: string) {
MessageSid: "msg-123",
},
{},
makeWhatsAppConfig(home),
makeWhatsAppDirectiveConfig(home, { model: "anthropic/claude-opus-4-5" }),
);
return Array.isArray(res) ? res[0] : res;
@@ -75,10 +47,10 @@ describe("directive behavior", () => {
const res = await getReplyFromConfig(
{ Body: "/think", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
makeThinkConfig(home),
makeWhatsAppDirectiveConfig(home, { model: "anthropic/claude-opus-4-5" }),
);
const text = Array.isArray(res) ? res[0]?.text : res?.text;
const text = replyText(res);
expect(text).toContain("Current thinking level: low");
expect(text).toContain("Options: off, minimal, low, medium, high.");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
@@ -98,42 +70,29 @@ describe("directive behavior", () => {
const res = await getReplyFromConfig(
{ Body: "/think", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
makeThinkConfig(home),
makeWhatsAppDirectiveConfig(home, { model: "anthropic/claude-opus-4-5" }),
);
const text = Array.isArray(res) ? res[0]?.text : res?.text;
const text = replyText(res);
expect(text).toContain("Current thinking level: off");
expect(text).toContain("Options: off, minimal, low, medium, high.");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("strips reply tags and maps reply_to_current to MessageSid", async () => {
await withTempHome(async (home) => {
const payload = await runReplyToCurrentCase(home, "hello [[reply_to_current]]");
expect(payload?.text).toBe("hello");
expect(payload?.replyToId).toBe("msg-123");
for (const replyTag of ["[[reply_to_current]]", "[[ reply_to_current ]]"]) {
it(`strips ${replyTag} and maps reply_to_current to MessageSid`, async () => {
await withTempHome(async (home) => {
const payload = await runReplyToCurrentCase(home, `hello ${replyTag}`);
expect(payload?.text).toBe("hello");
expect(payload?.replyToId).toBe("msg-123");
});
});
});
it("strips reply tags with whitespace and maps reply_to_current to MessageSid", async () => {
await withTempHome(async (home) => {
const payload = await runReplyToCurrentCase(home, "hello [[ reply_to_current ]]");
expect(payload?.text).toBe("hello");
expect(payload?.replyToId).toBe("msg-123");
});
});
}
it("prefers explicit reply_to id over reply_to_current", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
payloads: [
{
text: "hi [[reply_to_current]] [[reply_to:abc-456]]",
},
],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
});
vi.mocked(runEmbeddedPiAgent).mockResolvedValue(
makeEmbeddedTextResult("hi [[reply_to_current]] [[reply_to:abc-456]]"),
);
const res = await getReplyFromConfig(
{
@@ -143,16 +102,7 @@ describe("directive behavior", () => {
MessageSid: "msg-123",
},
{},
{
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-5" },
workspace: path.join(home, "openclaw"),
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: path.join(home, "sessions.json") },
},
makeWhatsAppDirectiveConfig(home, { model: { primary: "anthropic/claude-opus-4-5" } }),
);
const payload = Array.isArray(res) ? res[0] : res;
@@ -162,13 +112,7 @@ describe("directive behavior", () => {
});
it("applies inline think and still runs agent content", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
payloads: [{ text: "done" }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
});
mockEmbeddedTextResult("done");
const res = await getReplyFromConfig(
{
@@ -177,19 +121,10 @@ describe("directive behavior", () => {
To: "+2000",
},
{},
{
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-5" },
workspace: path.join(home, "openclaw"),
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: path.join(home, "sessions.json") },
},
makeWhatsAppDirectiveConfig(home, { model: { primary: "anthropic/claude-opus-4-5" } }),
);
const texts = (Array.isArray(res) ? res : [res]).map((entry) => entry?.text).filter(Boolean);
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
});

View File

@@ -36,6 +36,20 @@ export function replyTexts(res: ReplyPayloadText | ReplyPayloadText[]): string[]
.filter((value): value is string => Boolean(value));
}
export function makeEmbeddedTextResult(text = "done") {
return {
payloads: [{ text }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
};
}
export function mockEmbeddedTextResult(text = "done") {
vi.mocked(runEmbeddedPiAgent).mockResolvedValue(makeEmbeddedTextResult(text));
}
export async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
return withTempHomeBase(
async (home) => {

View File

@@ -1,27 +1,32 @@
import "./reply.directive.directive-behavior.e2e-mocks.js";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import {
installDirectiveBehaviorE2EHooks,
loadModelCatalog,
makeWhatsAppDirectiveConfig,
mockEmbeddedTextResult,
replyTexts,
runEmbeddedPiAgent,
withTempHome,
} from "./reply.directive.directive-behavior.e2e-harness.js";
import { getReplyFromConfig } from "./reply.js";
function makeDefaultModelConfig(home: string) {
return makeWhatsAppDirectiveConfig(home, {
model: { primary: "anthropic/claude-opus-4-5" },
models: {
"anthropic/claude-opus-4-5": {},
"openai/gpt-4.1-mini": {},
},
});
}
describe("directive behavior", () => {
installDirectiveBehaviorE2EHooks();
it("ignores inline /model and uses the default model", async () => {
await withTempHome(async (home) => {
const storePath = path.join(home, "sessions.json");
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
payloads: [{ text: "done" }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
});
mockEmbeddedTextResult("done");
const res = await getReplyFromConfig(
{
@@ -30,23 +35,10 @@ describe("directive behavior", () => {
To: "+2000",
},
{},
{
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-5" },
workspace: path.join(home, "openclaw"),
models: {
"anthropic/claude-opus-4-5": {},
"openai/gpt-4.1-mini": {},
},
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: storePath },
},
makeDefaultModelConfig(home),
);
const texts = (Array.isArray(res) ? res : [res]).map((entry) => entry?.text).filter(Boolean);
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
const call = vi.mocked(runEmbeddedPiAgent).mock.calls[0]?.[0];
@@ -56,14 +48,7 @@ describe("directive behavior", () => {
});
it("defaults thinking to low for reasoning-capable models", async () => {
await withTempHome(async (home) => {
const storePath = path.join(home, "sessions.json");
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
payloads: [{ text: "done" }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
});
mockEmbeddedTextResult("done");
vi.mocked(loadModelCatalog).mockResolvedValueOnce([
{
id: "claude-opus-4-5",
@@ -80,16 +65,7 @@ describe("directive behavior", () => {
To: "+2000",
},
{},
{
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-5" },
workspace: path.join(home, "openclaw"),
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: storePath },
},
makeWhatsAppDirectiveConfig(home, { model: { primary: "anthropic/claude-opus-4-5" } }),
);
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
@@ -99,14 +75,7 @@ describe("directive behavior", () => {
});
it("passes elevated defaults when sender is approved", async () => {
await withTempHome(async (home) => {
const storePath = path.join(home, "sessions.json");
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
payloads: [{ text: "done" }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
});
mockEmbeddedTextResult("done");
await getReplyFromConfig(
{
@@ -117,21 +86,17 @@ describe("directive behavior", () => {
SenderE164: "+1004",
},
{},
{
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-5" },
workspace: path.join(home, "openclaw"),
makeWhatsAppDirectiveConfig(
home,
{ model: { primary: "anthropic/claude-opus-4-5" } },
{
tools: {
elevated: {
allowFrom: { whatsapp: ["+1004"] },
},
},
},
tools: {
elevated: {
allowFrom: { whatsapp: ["+1004"] },
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: storePath },
},
),
);
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();

View File

@@ -9,7 +9,10 @@ import {
assertModelSelection,
installDirectiveBehaviorE2EHooks,
MAIN_SESSION_KEY,
makeWhatsAppDirectiveConfig,
replyText,
runEmbeddedPiAgent,
sessionStorePath,
withTempHome,
} from "./reply.directive.directive-behavior.e2e-harness.js";
import { getReplyFromConfig } from "./reply.js";
@@ -26,12 +29,22 @@ function makeModelDefinition(id: string, name: string): ModelDefinitionConfig {
};
}
function makeModelSwitchConfig(home: string) {
return makeWhatsAppDirectiveConfig(home, {
model: { primary: "openai/gpt-4.1-mini" },
models: {
"openai/gpt-4.1-mini": {},
"anthropic/claude-opus-4-5": { alias: "Opus" },
},
});
}
describe("directive behavior", () => {
installDirectiveBehaviorE2EHooks();
it("prefers alias matches when fuzzy selection is ambiguous", async () => {
await withTempHome(async (home) => {
const storePath = path.join(home, "sessions.json");
const storePath = sessionStorePath(home);
const res = await getReplyFromConfig(
{ Body: "/model ki", From: "+1222", To: "+1222", CommandAuthorized: true },
@@ -69,7 +82,7 @@ describe("directive behavior", () => {
},
);
const text = Array.isArray(res) ? res[0]?.text : res?.text;
const text = replyText(res);
expect(text).toContain("Model set to Kimi (moonshot/kimi-k2-0905-preview).");
assertModelSelection(storePath, {
provider: "moonshot",
@@ -80,7 +93,7 @@ describe("directive behavior", () => {
});
it("stores auth profile overrides on /model directive", async () => {
await withTempHome(async (home) => {
const storePath = path.join(home, "sessions.json");
const storePath = sessionStorePath(home);
const authDir = path.join(home, ".openclaw", "agents", "main", "agent");
await fs.mkdir(authDir, { recursive: true, mode: 0o700 });
await fs.writeFile(
@@ -104,22 +117,10 @@ describe("directive behavior", () => {
const res = await getReplyFromConfig(
{ Body: "/model Opus@anthropic:work", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
{
agents: {
defaults: {
model: { primary: "openai/gpt-4.1-mini" },
workspace: path.join(home, "openclaw"),
models: {
"openai/gpt-4.1-mini": {},
"anthropic/claude-opus-4-5": { alias: "Opus" },
},
},
},
session: { store: storePath },
},
makeModelSwitchConfig(home),
);
const text = Array.isArray(res) ? res[0]?.text : res?.text;
const text = replyText(res);
expect(text).toContain("Auth profile set to anthropic:work");
const store = loadSessionStore(storePath);
const entry = store["agent:main:main"];
@@ -130,24 +131,10 @@ describe("directive behavior", () => {
it("queues a system event when switching models", async () => {
await withTempHome(async (home) => {
drainSystemEvents(MAIN_SESSION_KEY);
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
{ Body: "/model Opus", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
{
agents: {
defaults: {
model: { primary: "openai/gpt-4.1-mini" },
workspace: path.join(home, "openclaw"),
models: {
"openai/gpt-4.1-mini": {},
"anthropic/claude-opus-4-5": { alias: "Opus" },
},
},
},
session: { store: storePath },
},
makeModelSwitchConfig(home),
);
const events = drainSystemEvents(MAIN_SESSION_KEY);
@@ -158,7 +145,6 @@ describe("directive behavior", () => {
it("queues a system event when toggling elevated", async () => {
await withTempHome(async (home) => {
drainSystemEvents(MAIN_SESSION_KEY);
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
{
@@ -169,17 +155,11 @@ describe("directive behavior", () => {
CommandAuthorized: true,
},
{},
{
agents: {
defaults: {
model: { primary: "openai/gpt-4.1-mini" },
workspace: path.join(home, "openclaw"),
},
},
tools: { elevated: { allowFrom: { whatsapp: ["*"] } } },
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: storePath },
},
makeWhatsAppDirectiveConfig(
home,
{ model: { primary: "openai/gpt-4.1-mini" } },
{ tools: { elevated: { allowFrom: { whatsapp: ["*"] } } } },
),
);
const events = drainSystemEvents(MAIN_SESSION_KEY);
@@ -189,7 +169,6 @@ describe("directive behavior", () => {
it("queues a system event when toggling reasoning", async () => {
await withTempHome(async (home) => {
drainSystemEvents(MAIN_SESSION_KEY);
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
{
@@ -200,16 +179,7 @@ describe("directive behavior", () => {
CommandAuthorized: true,
},
{},
{
agents: {
defaults: {
model: { primary: "openai/gpt-4.1-mini" },
workspace: path.join(home, "openclaw"),
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: storePath },
},
makeWhatsAppDirectiveConfig(home, { model: { primary: "openai/gpt-4.1-mini" } }),
);
const events = drainSystemEvents(MAIN_SESSION_KEY);

View File

@@ -42,6 +42,29 @@ function makeWorkElevatedAllowlistConfig(home: string) {
};
}
function makeElevatedDirectiveConfig(
home: string,
defaults: Record<string, unknown> = {},
extra: Record<string, unknown> = {},
) {
return makeWhatsAppDirectiveConfig(
home,
{
model: "anthropic/claude-opus-4-5",
...defaults,
},
{
tools: {
elevated: {
allowFrom: { whatsapp: ["+1222"] },
},
},
channels: { whatsapp: { allowFrom: ["+1222"] } },
...extra,
},
);
}
function makeCommandMessage(body: string, from = "+1222") {
return {
Body: body,
@@ -98,21 +121,7 @@ describe("directive behavior", () => {
const res = await getReplyFromConfig(
makeCommandMessage("/elevated off"),
{},
makeWhatsAppDirectiveConfig(
home,
{
model: "anthropic/claude-opus-4-5",
sandbox: { mode: "off" },
},
{
tools: {
elevated: {
allowFrom: { whatsapp: ["+1222"] },
},
},
channels: { whatsapp: { allowFrom: ["+1222"] } },
},
),
makeElevatedDirectiveConfig(home, { sandbox: { mode: "off" } }),
);
const text = replyText(res);
@@ -126,18 +135,7 @@ describe("directive behavior", () => {
const res = await getReplyFromConfig(
makeCommandMessage("/elevated maybe"),
{},
makeWhatsAppDirectiveConfig(
home,
{ model: "anthropic/claude-opus-4-5" },
{
tools: {
elevated: {
allowFrom: { whatsapp: ["+1222"] },
},
},
channels: { whatsapp: { allowFrom: ["+1222"] } },
},
),
makeElevatedDirectiveConfig(home),
);
const text = replyText(res);
@@ -150,18 +148,7 @@ describe("directive behavior", () => {
const res = await getReplyFromConfig(
makeCommandMessage("/elevated off\n/verbose on"),
{},
makeWhatsAppDirectiveConfig(
home,
{ model: "anthropic/claude-opus-4-5" },
{
tools: {
elevated: {
allowFrom: { whatsapp: ["+1222"] },
},
},
channels: { whatsapp: { allowFrom: ["+1222"] } },
},
),
makeElevatedDirectiveConfig(home),
);
const text = replyText(res);

View File

@@ -3,6 +3,7 @@ import { describe, expect, it, vi } from "vitest";
import { loadSessionStore, resolveSessionKey, saveSessionStore } from "../config/sessions.js";
import {
installDirectiveBehaviorE2EHooks,
makeEmbeddedTextResult,
makeWhatsAppDirectiveConfig,
replyText,
replyTexts,
@@ -12,6 +13,62 @@ import {
} from "./reply.directive.directive-behavior.e2e-harness.js";
import { getReplyFromConfig } from "./reply.js";
function makeRunConfig(home: string, storePath: string) {
return makeWhatsAppDirectiveConfig(
home,
{ model: "anthropic/claude-opus-4-5" },
{ session: { store: storePath } },
);
}
async function runInFlightVerboseToggleCase(params: {
home: string;
shouldEmitBefore: boolean;
toggledVerboseLevel: "on" | "off";
seedVerboseOn?: boolean;
}) {
const storePath = sessionStorePath(params.home);
const ctx = {
Body: "please do the thing",
From: "+1004",
To: "+2000",
};
const sessionKey = resolveSessionKey(
"per-sender",
{ From: ctx.From, To: ctx.To, Body: ctx.Body },
"main",
);
vi.mocked(runEmbeddedPiAgent).mockImplementation(async (agentParams) => {
const shouldEmit = agentParams.shouldEmitToolResult;
expect(shouldEmit?.()).toBe(params.shouldEmitBefore);
const store = loadSessionStore(storePath);
const entry = store[sessionKey] ?? {
sessionId: "s",
updatedAt: Date.now(),
};
store[sessionKey] = {
...entry,
verboseLevel: params.toggledVerboseLevel,
updatedAt: Date.now(),
};
await saveSessionStore(storePath, store);
expect(shouldEmit?.()).toBe(!params.shouldEmitBefore);
return makeEmbeddedTextResult("done");
});
if (params.seedVerboseOn) {
await getReplyFromConfig(
{ Body: "/verbose on", From: ctx.From, To: ctx.To, CommandAuthorized: true },
{},
makeRunConfig(params.home, storePath),
);
}
const res = await getReplyFromConfig(ctx, {}, makeRunConfig(params.home, storePath));
return { res };
}
async function runModelDirectiveAndGetText(
home: string,
body: string,
@@ -35,50 +92,12 @@ describe("directive behavior", () => {
it("updates tool verbose during an in-flight run (toggle on)", async () => {
await withTempHome(async (home) => {
const storePath = sessionStorePath(home);
const ctx = { Body: "please do the thing", From: "+1004", To: "+2000" };
const sessionKey = resolveSessionKey(
"per-sender",
{ From: ctx.From, To: ctx.To, Body: ctx.Body },
"main",
);
vi.mocked(runEmbeddedPiAgent).mockImplementation(async (params) => {
const shouldEmit = params.shouldEmitToolResult;
expect(shouldEmit?.()).toBe(false);
const store = loadSessionStore(storePath);
const entry = store[sessionKey] ?? {
sessionId: "s",
updatedAt: Date.now(),
};
store[sessionKey] = {
...entry,
verboseLevel: "on",
updatedAt: Date.now(),
};
await saveSessionStore(storePath, store);
expect(shouldEmit?.()).toBe(true);
return {
payloads: [{ text: "done" }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
};
const { res } = await runInFlightVerboseToggleCase({
home,
shouldEmitBefore: false,
toggledVerboseLevel: "on",
});
const res = await getReplyFromConfig(
ctx,
{},
makeWhatsAppDirectiveConfig(
home,
{ model: "anthropic/claude-opus-4-5" },
{
session: { store: storePath },
},
),
);
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
@@ -86,66 +105,13 @@ describe("directive behavior", () => {
});
it("updates tool verbose during an in-flight run (toggle off)", async () => {
await withTempHome(async (home) => {
const storePath = sessionStorePath(home);
const ctx = {
Body: "please do the thing",
From: "+1004",
To: "+2000",
};
const sessionKey = resolveSessionKey(
"per-sender",
{ From: ctx.From, To: ctx.To, Body: ctx.Body },
"main",
);
vi.mocked(runEmbeddedPiAgent).mockImplementation(async (params) => {
const shouldEmit = params.shouldEmitToolResult;
expect(shouldEmit?.()).toBe(true);
const store = loadSessionStore(storePath);
const entry = store[sessionKey] ?? {
sessionId: "s",
updatedAt: Date.now(),
};
store[sessionKey] = {
...entry,
verboseLevel: "off",
updatedAt: Date.now(),
};
await saveSessionStore(storePath, store);
expect(shouldEmit?.()).toBe(false);
return {
payloads: [{ text: "done" }],
meta: {
durationMs: 5,
agentMeta: { sessionId: "s", provider: "p", model: "m" },
},
};
const { res } = await runInFlightVerboseToggleCase({
home,
shouldEmitBefore: true,
toggledVerboseLevel: "off",
seedVerboseOn: true,
});
await getReplyFromConfig(
{ Body: "/verbose on", From: ctx.From, To: ctx.To, CommandAuthorized: true },
{},
makeWhatsAppDirectiveConfig(
home,
{ model: "anthropic/claude-opus-4-5" },
{
session: { store: storePath },
},
),
);
const res = await getReplyFromConfig(
ctx,
{},
makeWhatsAppDirectiveConfig(
home,
{ model: "anthropic/claude-opus-4-5" },
{
session: { store: storePath },
},
),
);
const texts = replyTexts(res);
expect(texts).toContain("done");
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();