








@@ -1,4 +1,4 @@
1-import { afterEach, beforeEach, describe, expect, it } from "vitest";
1+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22import type { OpenClawConfig } from "../../config/config.js";
33import { setActivePluginRegistry } from "../../plugins/runtime.js";
44import { createTestRegistry } from "../../test-utils/channel-plugins.js";
@@ -278,3 +278,119 @@ describe("runMessageAction send validation", () => {
278278).rejects.toThrow(/use action "poll" instead of "send"/i);
279279});
280280});
281+282+describe("message body alias normalization", () => {
283+beforeEach(() => {
284+setActivePluginRegistry(
285+createTestRegistry([
286+{
287+pluginId: "workspace",
288+source: "test",
289+plugin: workspaceTestPlugin,
290+},
291+]),
292+);
293+});
294+295+afterEach(() => {
296+setActivePluginRegistry(createTestRegistry([]));
297+vi.restoreAllMocks();
298+});
299+300+it.each([
301+{ alias: "SendMessage", value: "hello from alias" },
302+{ alias: "content", value: "hello from content" },
303+{ alias: "text", value: "hello from text" },
304+])("normalizes $alias alias to message for send", async ({ alias, value }) => {
305+const result = await runDrySend({
306+cfg: workspaceConfig,
307+actionParams: {
308+channel: "workspace",
309+target: "#C12345678",
310+[alias]: value,
311+},
312+toolContext: { currentChannelId: "C12345678" },
313+});
314+315+expect(result.kind).toBe("send");
316+});
317+318+it("does not overwrite an explicit message with an alias", async () => {
319+const result = await runDrySend({
320+cfg: workspaceConfig,
321+actionParams: {
322+channel: "workspace",
323+target: "#C12345678",
324+message: "explicit",
325+SendMessage: "alias value",
326+},
327+toolContext: { currentChannelId: "C12345678" },
328+});
329+330+expect(result.kind).toBe("send");
331+});
332+333+it("emits a diagnostic warning when normalizing an alias", async () => {
334+const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
335+336+await runDrySend({
337+cfg: workspaceConfig,
338+actionParams: {
339+channel: "workspace",
340+target: "#C12345678",
341+SendMessage: "alias body",
342+},
343+toolContext: { currentChannelId: "C12345678" },
344+});
345+346+expect(warnSpy).toHaveBeenCalledWith(
347+expect.stringContaining('[message-tool] normalized alias "SendMessage" to "message"'),
348+);
349+});
350+351+it.each([
352+{
353+name: "reasoning tag",
354+SendMessage: "<think>internal reasoning</think>Visible answer",
355+},
356+{
357+name: "formatted reasoning prefix",
358+SendMessage: "Reasoning:\n_internal plan_\n\nVisible answer",
359+},
360+])("sanitizes SendMessage alias $name before delivery", async ({ SendMessage }) => {
361+const result = await runMessageAction({
362+cfg: emptyConfig,
363+action: "send",
364+params: {
365+ SendMessage,
366+},
367+toolContext: {
368+currentChannelProvider: "webchat",
369+},
370+sessionKey: "agent:main",
371+sourceReplyDeliveryMode: "message_tool_only",
372+});
373+374+expect(result).toMatchObject({
375+kind: "send",
376+payload: {
377+sourceReply: {
378+text: "Visible answer",
379+},
380+},
381+});
382+});
383+384+it("still rejects send with no message and no alias", async () => {
385+await expect(
386+runDrySend({
387+cfg: workspaceConfig,
388+actionParams: {
389+channel: "workspace",
390+target: "#C12345678",
391+},
392+toolContext: { currentChannelId: "C12345678" },
393+}),
394+).rejects.toThrow(/message required/i);
395+});
396+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。