diff --git a/src/auto-reply/reply.directive.directive-behavior.defaults-think-low-reasoning-capable-models-no.test.ts b/src/auto-reply/reply.directive.directive-behavior.defaults-think-low-reasoning-capable-models-no.test.ts index ab86435c79a..4b77d68a8d6 100644 --- a/src/auto-reply/reply.directive.directive-behavior.defaults-think-low-reasoning-capable-models-no.test.ts +++ b/src/auto-reply/reply.directive.directive-behavior.defaults-think-low-reasoning-capable-models-no.test.ts @@ -1,5 +1,6 @@ import "./reply.directive.directive-behavior.e2e-mocks.js"; import { describe, expect, it, vi } from "vitest"; +import { loadSessionStore } from "../config/sessions.js"; import { installDirectiveBehaviorE2EHooks, loadModelCatalog, @@ -9,6 +10,7 @@ import { replyText, replyTexts, runEmbeddedPiAgent, + sessionStorePath, withTempHome, } from "./reply.directive.directive-behavior.e2e-harness.js"; import { getReplyFromConfig } from "./reply.js"; @@ -79,6 +81,70 @@ describe("directive behavior", () => { expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); }); }); + it("persists /reasoning off on discord even when model defaults reasoning on", async () => { + await withTempHome(async (home) => { + const storePath = sessionStorePath(home); + mockEmbeddedTextResult("done"); + vi.mocked(loadModelCatalog).mockResolvedValue([ + { + id: "x-ai/grok-4.1-fast", + name: "Grok 4.1 Fast", + provider: "openrouter", + reasoning: true, + }, + ]); + + const config = makeWhatsAppDirectiveConfig( + home, + { + model: "openrouter/x-ai/grok-4.1-fast", + }, + { + channels: { + discord: { allowFrom: ["*"] }, + }, + session: { store: storePath }, + }, + ); + + const offRes = await getReplyFromConfig( + { + Body: "/reasoning off", + From: "discord:user:1004", + To: "channel:general", + Provider: "discord", + Surface: "discord", + CommandSource: "text", + CommandAuthorized: true, + }, + {}, + config, + ); + expect(replyText(offRes)).toContain("Reasoning visibility disabled."); + + const store = loadSessionStore(storePath); + const entry = Object.values(store)[0]; + expect(entry?.reasoningLevel).toBe("off"); + + await getReplyFromConfig( + { + Body: "hello", + From: "discord:user:1004", + To: "channel:general", + Provider: "discord", + Surface: "discord", + CommandSource: "text", + CommandAuthorized: true, + }, + {}, + config, + ); + + expect(runEmbeddedPiAgent).toHaveBeenCalledOnce(); + const call = vi.mocked(runEmbeddedPiAgent).mock.calls[0]?.[0]; + expect(call?.reasoningLevel).toBe("off"); + }); + }); 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) => { diff --git a/src/auto-reply/reply/directive-handling.impl.ts b/src/auto-reply/reply/directive-handling.impl.ts index 156109b1c05..979304dfb1b 100644 --- a/src/auto-reply/reply/directive-handling.impl.ts +++ b/src/auto-reply/reply/directive-handling.impl.ts @@ -292,7 +292,8 @@ export async function handleDirectiveOnly( } if (directives.hasReasoningDirective && directives.reasoningLevel) { if (directives.reasoningLevel === "off") { - delete sessionEntry.reasoningLevel; + // Persist explicit off so it overrides model-capability defaults. + sessionEntry.reasoningLevel = "off"; } else { sessionEntry.reasoningLevel = directives.reasoningLevel; } diff --git a/src/auto-reply/reply/directive-handling.persist.ts b/src/auto-reply/reply/directive-handling.persist.ts index c781f496802..f4087055801 100644 --- a/src/auto-reply/reply/directive-handling.persist.ts +++ b/src/auto-reply/reply/directive-handling.persist.ts @@ -91,7 +91,8 @@ export async function persistInlineDirectives(params: { } if (directives.hasReasoningDirective && directives.reasoningLevel) { if (directives.reasoningLevel === "off") { - delete sessionEntry.reasoningLevel; + // Persist explicit off so it overrides model-capability defaults. + sessionEntry.reasoningLevel = "off"; } else { sessionEntry.reasoningLevel = directives.reasoningLevel; }