























@@ -1286,6 +1286,15 @@ describe("runHeartbeatOnce", () => {
12861286replies: [{ text: "Because it helps", isReasoning: true }, { text: "HEARTBEAT_OK" }],
12871287expectedTexts: ["Thinking\n\n_Because it helps_"],
12881288},
1289+{
1290+// Reasoning-only result: the selector returns no main reply, but the
1291+// documented includeReasoning opt-in must still deliver the Thinking
1292+// message instead of going silent (#92242 follow-up / review finding).
1293+name: "raw flagged reasoning only (no main reply)",
1294+caseDir: "hb-reasoning-only",
1295+replies: [{ text: "Because it helps", isReasoning: true }],
1296+expectedTexts: ["Thinking\n\n_Because it helps_"],
1297+},
12891298{
12901299name: "visible final that starts with thinking prose",
12911300caseDir: "hb-thinking-visible-final",
@@ -1371,6 +1380,67 @@ describe("runHeartbeatOnce", () => {
13711380},
13721381);
137313821383+it("does not surface a trailing legacy reasoning payload as the reply when includeReasoning is unset", async () => {
1384+// With includeReasoning unset, a legacy "Reasoning:"-prefixed payload after
1385+// the final answer must not become the visible heartbeat reply, and no
1386+// separate Thinking message is sent. (#92242 review follow-up)
1387+const replySpy = vi.fn();
1388+try {
1389+const tmpDir = await createCaseDir("hb-legacy-reasoning-unset");
1390+const storePath = path.join(tmpDir, "sessions.json");
1391+const cfg: OpenClawConfig = {
1392+agents: {
1393+defaults: {
1394+workspace: tmpDir,
1395+heartbeat: { every: "5m", target: "whatsapp" },
1396+},
1397+},
1398+channels: { whatsapp: { allowFrom: ["*"] } },
1399+session: { store: storePath },
1400+};
1401+const sessionKey = resolveMainSessionKey(cfg);
1402+await fs.writeFile(
1403+storePath,
1404+JSON.stringify({
1405+[sessionKey]: {
1406+sessionId: "sid",
1407+updatedAt: Date.now(),
1408+lastChannel: "whatsapp",
1409+lastProvider: "whatsapp",
1410+lastTo: "120363401234567890@g.us",
1411+},
1412+}),
1413+);
1414+1415+replySpy.mockResolvedValue([
1416+{ text: "All clear" },
1417+{ text: "Reasoning: because nothing changed" },
1418+]);
1419+const sendWhatsApp = vi
1420+.fn<
1421+(
1422+to: string,
1423+text: string,
1424+opts?: unknown,
1425+) => Promise<{ messageId: string; toJid: string }>
1426+>()
1427+.mockResolvedValue({ messageId: "m1", toJid: "jid" });
1428+1429+await runHeartbeatOnce({
1430+ cfg,
1431+deps: createHeartbeatDeps(sendWhatsApp, { getReplyFromConfig: replySpy }),
1432+});
1433+1434+expect(sendWhatsApp).toHaveBeenCalledTimes(1);
1435+expectWhatsAppSendCall(sendWhatsApp, 0, {
1436+to: "120363401234567890@g.us",
1437+text: "All clear",
1438+});
1439+} finally {
1440+replySpy.mockReset();
1441+}
1442+});
1443+13741444it("loads the default agent session from templated stores", async () => {
13751445const tmpDir = await createCaseDir("openclaw-hb");
13761446const storeTemplate = path.join(tmpDir, "agents", "{agentId}", "sessions.json");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。