fix: avoid duplicate status replies
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026.1.11-5
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Auto-reply: prevent duplicate /status replies (including /usage alias) and add tests for inline + standalone cases.
|
||||||
|
|
||||||
## 2026.1.11-4
|
## 2026.1.11-4
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|||||||
@@ -131,6 +131,92 @@ describe("trigger handling", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("emits /status once (no duplicate inline + final)", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const blockReplies: Array<{ text?: string }> = [];
|
||||||
|
const res = await getReplyFromConfig(
|
||||||
|
{
|
||||||
|
Body: "/status",
|
||||||
|
From: "+1000",
|
||||||
|
To: "+2000",
|
||||||
|
Provider: "whatsapp",
|
||||||
|
SenderE164: "+1000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onBlockReply: async (payload) => {
|
||||||
|
blockReplies.push(payload);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
makeCfg(home),
|
||||||
|
);
|
||||||
|
const replies = res ? (Array.isArray(res) ? res : [res]) : [];
|
||||||
|
expect(blockReplies.length).toBe(0);
|
||||||
|
expect(replies.length).toBe(1);
|
||||||
|
expect(String(replies[0]?.text ?? "")).toContain("Model:");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emits /usage once (alias of /status)", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const blockReplies: Array<{ text?: string }> = [];
|
||||||
|
const res = await getReplyFromConfig(
|
||||||
|
{
|
||||||
|
Body: "/usage",
|
||||||
|
From: "+1000",
|
||||||
|
To: "+2000",
|
||||||
|
Provider: "whatsapp",
|
||||||
|
SenderE164: "+1000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onBlockReply: async (payload) => {
|
||||||
|
blockReplies.push(payload);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
makeCfg(home),
|
||||||
|
);
|
||||||
|
const replies = res ? (Array.isArray(res) ? res : [res]) : [];
|
||||||
|
expect(blockReplies.length).toBe(0);
|
||||||
|
expect(replies.length).toBe(1);
|
||||||
|
expect(String(replies[0]?.text ?? "")).toContain("Model:");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sends one inline status and still returns agent reply for mixed text", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
|
||||||
|
payloads: [{ text: "agent says hi" }],
|
||||||
|
meta: {
|
||||||
|
durationMs: 1,
|
||||||
|
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const blockReplies: Array<{ text?: string }> = [];
|
||||||
|
const res = await getReplyFromConfig(
|
||||||
|
{
|
||||||
|
Body: "here we go /status now",
|
||||||
|
From: "+1002",
|
||||||
|
To: "+2000",
|
||||||
|
Provider: "whatsapp",
|
||||||
|
SenderE164: "+1002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onBlockReply: async (payload) => {
|
||||||
|
blockReplies.push(payload);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
makeCfg(home),
|
||||||
|
);
|
||||||
|
const replies = res ? (Array.isArray(res) ? res : [res]) : [];
|
||||||
|
expect(blockReplies.length).toBe(1);
|
||||||
|
expect(String(blockReplies[0]?.text ?? "")).toContain("Model:");
|
||||||
|
expect(replies.length).toBe(1);
|
||||||
|
expect(replies[0]?.text).toBe("agent says hi");
|
||||||
|
const prompt =
|
||||||
|
vi.mocked(runEmbeddedPiAgent).mock.calls[0]?.[0]?.prompt ?? "";
|
||||||
|
expect(prompt).not.toContain("/status");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("aborts even with timestamp prefix", async () => {
|
it("aborts even with timestamp prefix", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const res = await getReplyFromConfig(
|
const res = await getReplyFromConfig(
|
||||||
|
|||||||
@@ -866,7 +866,8 @@ export async function getReplyFromConfig(
|
|||||||
}) &&
|
}) &&
|
||||||
directives.hasStatusDirective &&
|
directives.hasStatusDirective &&
|
||||||
allowTextCommands &&
|
allowTextCommands &&
|
||||||
command.isAuthorizedSender;
|
command.isAuthorizedSender &&
|
||||||
|
command.commandBodyNormalized !== "/status";
|
||||||
if (handleInlineStatus) {
|
if (handleInlineStatus) {
|
||||||
const inlineStatusReply = await buildStatusReply({
|
const inlineStatusReply = await buildStatusReply({
|
||||||
cfg,
|
cfg,
|
||||||
|
|||||||
Reference in New Issue
Block a user