diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fbcc32b1..f9b91d01b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ - iMessage: ignore disconnect errors during shutdown (avoid unhandled promise rejections). Thanks @antons for PR #359. - Messages: stop defaulting ack reactions to 👀 when identity emoji is missing. - Auto-reply: require slash for control commands to avoid false triggers in normal text. +- Auto-reply: add `/reasoning on|off` to expose model reasoning blocks (italic). +- Auto-reply: place reasoning blocks before the final reply text when appended. - Auto-reply: flag error payloads and improve Bun socket error messaging. Thanks @emanuelst for PR #331. - Commands: add `/stop` to the registry and route native aborts to the active chat session. Thanks @nachoiacovino for PR #295. - Commands: unify native + text chat commands behind `commands.*` config (Discord/Slack/Telegram). Thanks @thewilloftheshadow for PR #275. diff --git a/src/agents/pi-embedded-runner.ts b/src/agents/pi-embedded-runner.ts index 8620b2c33..7913664c4 100644 --- a/src/agents/pi-embedded-runner.ts +++ b/src/agents/pi-embedded-runner.ts @@ -1102,7 +1102,7 @@ export async function runEmbeddedPiAgent(params: { ? formatReasoningMarkdown(thinking) : ""; if (!formatted) return base; - return base ? `${base}\n\n${formatted}` : formatted; + return base ? `${formatted}\n\n${base}` : formatted; })() : ""; for (const text of assistantTexts.length diff --git a/src/agents/pi-embedded-subscribe.test.ts b/src/agents/pi-embedded-subscribe.test.ts index c22316357..72e2541c5 100644 --- a/src/agents/pi-embedded-subscribe.test.ts +++ b/src/agents/pi-embedded-subscribe.test.ts @@ -129,6 +129,42 @@ describe("subscribeEmbeddedPiSession", () => { expect(payload.text).toBe("Hello block"); }); + it("prepends reasoning before text when enabled", () => { + let handler: ((evt: unknown) => void) | undefined; + const session: StubSession = { + subscribe: (fn) => { + handler = fn; + return () => {}; + }, + }; + + const onBlockReply = vi.fn(); + + subscribeEmbeddedPiSession({ + session: session as unknown as Parameters< + typeof subscribeEmbeddedPiSession + >[0]["session"], + runId: "run", + onBlockReply, + blockReplyBreak: "message_end", + includeReasoning: true, + }); + + const assistantMessage = { + role: "assistant", + content: [ + { type: "thinking", thinking: "Because it helps" }, + { type: "text", text: "Final answer" }, + ], + } as AssistantMessage; + + handler?.({ type: "message_end", message: assistantMessage }); + + expect(onBlockReply).toHaveBeenCalledTimes(1); + const payload = onBlockReply.mock.calls[0][0]; + expect(payload.text).toBe("_Reasoning:_\n_Because it helps_\n\nFinal answer"); + }); + it("emits block replies on text_end and does not duplicate on message_end", () => { let handler: ((evt: unknown) => void) | undefined; const session: StubSession = { diff --git a/src/agents/pi-embedded-subscribe.ts b/src/agents/pi-embedded-subscribe.ts index 9b9169a69..3aa9a6d5a 100644 --- a/src/agents/pi-embedded-subscribe.ts +++ b/src/agents/pi-embedded-subscribe.ts @@ -511,8 +511,8 @@ export function subscribeEmbeddedPiSession(params: { : ""; const text = baseText && formattedReasoning - ? `${baseText}\n\n${formattedReasoning}` - : baseText || formattedReasoning; + ? `${formattedReasoning}\n\n${baseText}` + : formattedReasoning || baseText; const addedDuringMessage = assistantTexts.length > assistantTextBaseline;