diff --git a/src/auto-reply/reply.directive.directive-behavior.accepts-thinking-xhigh-codex-models.test.ts b/src/auto-reply/reply.directive.directive-behavior.accepts-thinking-xhigh-codex-models.test.ts index 75eb23b0dd1..40a145220c1 100644 --- a/src/auto-reply/reply.directive.directive-behavior.accepts-thinking-xhigh-codex-models.test.ts +++ b/src/auto-reply/reply.directive.directive-behavior.accepts-thinking-xhigh-codex-models.test.ts @@ -187,22 +187,4 @@ describe("directive behavior", () => { expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); }); }); - it("shows current think level when /think has no argument", async () => { - await withTempHome(async (home) => { - const res = await getReplyFromConfig( - { Body: "/think", From: "+1222", To: "+1222", CommandAuthorized: true }, - {}, - makeWhatsAppDirectiveConfig( - home, - { model: "anthropic/claude-opus-4-5", thinkingDefault: "high" }, - { session: { store: sessionStorePath(home) } }, - ), - ); - - const text = replyText(res); - expect(text).toContain("Current thinking level: high"); - expect(text).toContain("Options: off, minimal, low, medium, high."); - expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); - }); - }); }); diff --git a/src/auto-reply/reply.directive.directive-behavior.applies-inline-reasoning-mixed-messages-acks-immediately.test.ts b/src/auto-reply/reply.directive.directive-behavior.applies-inline-reasoning-mixed-messages-acks-immediately.test.ts index 08c7f493f05..78a7fb2792f 100644 --- a/src/auto-reply/reply.directive.directive-behavior.applies-inline-reasoning-mixed-messages-acks-immediately.test.ts +++ b/src/auto-reply/reply.directive.directive-behavior.applies-inline-reasoning-mixed-messages-acks-immediately.test.ts @@ -1,8 +1,9 @@ import "./reply.directive.directive-behavior.e2e-mocks.js"; import { describe, expect, it, vi } from "vitest"; -import { loadSessionStore } from "../config/sessions.js"; +import { loadSessionStore, resolveSessionKey, saveSessionStore } from "../config/sessions.js"; import { installDirectiveBehaviorE2EHooks, + makeEmbeddedTextResult, makeWhatsAppDirectiveConfig, replyText, replyTexts, @@ -12,29 +13,20 @@ import { } from "./reply.directive.directive-behavior.e2e-harness.js"; import { getReplyFromConfig } from "./reply.js"; -async function runThinkDirectiveAndGetText( - home: string, - options: { thinkingDefault?: "high" } = {}, -): Promise { +async function runThinkDirectiveAndGetText(home: string): Promise { const res = await getReplyFromConfig( { Body: "/think", From: "+1222", To: "+1222", CommandAuthorized: true }, {}, makeWhatsAppDirectiveConfig(home, { model: "anthropic/claude-opus-4-5", - ...(options.thinkingDefault ? { thinkingDefault: options.thinkingDefault } : {}), + thinkingDefault: "high", }), ); return replyText(res); } function mockEmbeddedResponse(text: string) { - vi.mocked(runEmbeddedPiAgent).mockResolvedValue({ - payloads: [{ text }], - meta: { - durationMs: 5, - agentMeta: { sessionId: "s", provider: "p", model: "m" }, - }, - }); + vi.mocked(runEmbeddedPiAgent).mockResolvedValue(makeEmbeddedTextResult(text)); } async function runInlineReasoningMessage(params: { @@ -67,6 +59,62 @@ async function runInlineReasoningMessage(params: { ); } +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 }; +} + describe("directive behavior", () => { installDirectiveBehaviorE2EHooks(); @@ -152,20 +200,39 @@ describe("directive behavior", () => { expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); }); }); + it("updates tool verbose during an in-flight run (toggle on)", async () => { + await withTempHome(async (home) => { + const { res } = await runInFlightVerboseToggleCase({ + home, + shouldEmitBefore: false, + toggledVerboseLevel: "on", + }); + + const texts = replyTexts(res); + expect(texts).toContain("done"); + expect(runEmbeddedPiAgent).toHaveBeenCalledOnce(); + }); + }); + it("updates tool verbose during an in-flight run (toggle off)", async () => { + await withTempHome(async (home) => { + const { res } = await runInFlightVerboseToggleCase({ + home, + shouldEmitBefore: true, + toggledVerboseLevel: "off", + seedVerboseOn: true, + }); + + const texts = replyTexts(res); + expect(texts).toContain("done"); + expect(runEmbeddedPiAgent).toHaveBeenCalledOnce(); + }); + }); it("shows current think level when /think has no argument", async () => { await withTempHome(async (home) => { - const text = await runThinkDirectiveAndGetText(home, { thinkingDefault: "high" }); + const text = await runThinkDirectiveAndGetText(home); expect(text).toContain("Current thinking level: high"); expect(text).toContain("Options: off, minimal, low, medium, high."); expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); }); }); - it("shows off when /think has no argument and no default set", async () => { - await withTempHome(async (home) => { - const text = await runThinkDirectiveAndGetText(home); - expect(text).toContain("Current thinking level: off"); - expect(text).toContain("Options: off, minimal, low, medium, high."); - expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); - }); - }); }); diff --git a/src/auto-reply/reply.directive.directive-behavior.lists-allowlisted-models-model-list.test.ts b/src/auto-reply/reply.directive.directive-behavior.lists-allowlisted-models-model-list.test.ts index 5ad163dac5d..759136fc65d 100644 --- a/src/auto-reply/reply.directive.directive-behavior.lists-allowlisted-models-model-list.test.ts +++ b/src/auto-reply/reply.directive.directive-behavior.lists-allowlisted-models-model-list.test.ts @@ -37,6 +37,18 @@ describe("directive behavior", () => { expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); }); }); + it("lists allowlisted models on /model status", async () => { + await withTempHome(async (home) => { + const text = await runModelDirectiveText(home, "/model status", { + includeSessionStore: false, + }); + expect(text).toContain("anthropic/claude-opus-4-5"); + expect(text).toContain("openai/gpt-4.1-mini"); + expect(text).not.toContain("claude-sonnet-4-1"); + expect(text).toContain("auth:"); + expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); + }); + }); it("includes catalog providers when no allowlist is set", async () => { await withTempHome(async (home) => { vi.mocked(loadModelCatalog).mockResolvedValue([ diff --git a/src/auto-reply/reply.directive.directive-behavior.updates-tool-verbose-during-flight-run-toggle.test.ts b/src/auto-reply/reply.directive.directive-behavior.updates-tool-verbose-during-flight-run-toggle.test.ts deleted file mode 100644 index 9081566adea..00000000000 --- a/src/auto-reply/reply.directive.directive-behavior.updates-tool-verbose-during-flight-run-toggle.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import "./reply.directive.directive-behavior.e2e-mocks.js"; -import { describe, expect, it, vi } from "vitest"; -import { loadSessionStore, resolveSessionKey, saveSessionStore } from "../config/sessions.js"; -import { - installDirectiveBehaviorE2EHooks, - makeEmbeddedTextResult, - makeWhatsAppDirectiveConfig, - replyTexts, - runEmbeddedPiAgent, - sessionStorePath, - withTempHome, -} from "./reply.directive.directive-behavior.e2e-harness.js"; -import { runModelDirectiveText } from "./reply.directive.directive-behavior.model-directive-test-utils.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 }; -} - -describe("directive behavior", () => { - installDirectiveBehaviorE2EHooks(); - - it("updates tool verbose during an in-flight run (toggle on)", async () => { - await withTempHome(async (home) => { - const { res } = await runInFlightVerboseToggleCase({ - home, - shouldEmitBefore: false, - toggledVerboseLevel: "on", - }); - - const texts = replyTexts(res); - expect(texts).toContain("done"); - expect(runEmbeddedPiAgent).toHaveBeenCalledOnce(); - }); - }); - it("updates tool verbose during an in-flight run (toggle off)", async () => { - await withTempHome(async (home) => { - const { res } = await runInFlightVerboseToggleCase({ - home, - shouldEmitBefore: true, - toggledVerboseLevel: "off", - seedVerboseOn: true, - }); - - const texts = replyTexts(res); - expect(texts).toContain("done"); - expect(runEmbeddedPiAgent).toHaveBeenCalledOnce(); - }); - }); - it("shows summary on /model", async () => { - await withTempHome(async (home) => { - const text = await runModelDirectiveText(home, "/model", { includeSessionStore: false }); - expect(text).toContain("Current: anthropic/claude-opus-4-5"); - expect(text).toContain("Switch: /model "); - expect(text).toContain("Browse: /models (providers) or /models (models)"); - expect(text).toContain("More: /model status"); - expect(text).not.toContain("openai/gpt-4.1-mini"); - expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); - }); - }); - it("lists allowlisted models on /model status", async () => { - await withTempHome(async (home) => { - const text = await runModelDirectiveText(home, "/model status", { - includeSessionStore: false, - }); - expect(text).toContain("anthropic/claude-opus-4-5"); - expect(text).toContain("openai/gpt-4.1-mini"); - expect(text).not.toContain("claude-sonnet-4-1"); - expect(text).toContain("auth:"); - expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); - }); - }); -});