Auto-reply: send timeout fallback and tests

This commit is contained in:
Peter Steinberger
2025-11-25 17:52:57 +01:00
parent e4076d14c0
commit 46be5eac7d
3 changed files with 70 additions and 1 deletions

View File

@@ -3,7 +3,8 @@
## [Unreleased] 0.1.4
### Pending
- (add entries here)
- Auto-replies now send a WhatsApp fallback message when a command/Claude run hits the timeout, including up to 800 chars of partial stdout so the user still sees progress.
- Added tests covering the new timeout fallback behavior and partial-output truncation.
## 0.1.3 — 2025-11-25

View File

@@ -386,6 +386,14 @@ export async function getReplyFromConfig(
console.error(
`Command auto-reply timed out after ${elapsed}ms (limit ${timeoutMs}ms)`,
);
const baseMsg = `Command timed out after ${timeoutSeconds}s. Try a shorter prompt or split the request.`;
const partial = errorObj.stdout?.trim();
const partialSnippet =
partial && partial.length > 800 ? `${partial.slice(0, 800)}...` : partial;
const text = partialSnippet
? `${baseMsg}\n\nPartial output before timeout:\n${partialSnippet}`
: baseMsg;
return { text };
} else {
logError(
`Command auto-reply failed after ${elapsed}ms: ${String(err)}`,

View File

@@ -300,6 +300,66 @@ describe("config and templating", () => {
expect(result?.mediaUrl).toBeUndefined();
});
it("returns timeout reply with partial stdout snippet", async () => {
const partial = "x".repeat(900);
const runSpy = vi.fn().mockRejectedValue({
killed: true,
signal: "SIGKILL",
stdout: partial,
stderr: "",
});
const cfg = {
inbound: {
reply: {
mode: "command" as const,
command: ["echo", "{{Body}}"],
timeoutSeconds: 42,
},
},
};
const result = await index.getReplyFromConfig(
{ Body: "hi", From: "+1", To: "+2" },
undefined,
cfg,
runSpy,
);
expect(result?.text).toContain("Command timed out after 42s");
expect(result?.text).toContain("Partial output before timeout");
expect(result?.text).toContain(`${partial.slice(0, 800)}...`);
expect(result?.text).not.toContain(partial);
});
it("returns timeout reply without partial output when none is available", async () => {
const runSpy = vi.fn().mockRejectedValue({
killed: true,
signal: "SIGKILL",
stdout: "",
stderr: "",
});
const cfg = {
inbound: {
reply: {
mode: "command" as const,
command: ["echo", "{{Body}}"],
timeoutSeconds: 5,
},
},
};
const result = await index.getReplyFromConfig(
{ Body: "hi", From: "+1", To: "+2" },
undefined,
cfg,
runSpy,
);
expect(result?.text).toBe(
"Command timed out after 5s. Try a shorter prompt or split the request.",
);
});
it("splitMediaFromOutput strips media token and preserves text", () => {
const { text, mediaUrl } = splitMediaFromOutput(
"line1\nMEDIA:https://x/y.png\nline2",