fix(whatsapp): emit message received hooks (#71217)

* fix(whatsapp): emit message received hooks

* fix(whatsapp): harden message received hooks
This commit is contained in:
Vincent Koc
2026-04-24 13:05:10 -07:00
committed by GitHub
parent 7a168150e6
commit 8154337cb6
12 changed files with 498 additions and 18 deletions

View File

@@ -107,7 +107,7 @@ describe("before_agent_reply hook runner (claiming pattern)", () => {
expect(result).toEqual({ handled: true, reply: { text: "ok" } });
expect(logger.error).toHaveBeenCalledWith(
expect.stringContaining("before_agent_reply handler from test-plugin failed: Error: boom"),
expect.stringContaining("before_agent_reply handler from test-plugin failed: boom"),
);
});

View File

@@ -186,9 +186,38 @@ describe("before_tool_call terminal block semantics", () => {
});
await expect(runner.runBeforeToolCall(toolEvent, toolCtx)).rejects.toThrow(
"before_tool_call handler from failing failed: Error: boom",
"before_tool_call handler from failing failed: boom",
);
});
it("sanitizes caught hook error logs", async () => {
const logger = {
error: vi.fn(),
warn: vi.fn(),
};
addStaticTestHooks(registry, {
hookName: "message_received",
hooks: [
{
pluginId: "failing",
result: undefined,
handler: () => {
throw new Error("boom\nforged\tsecret sk-test1234567890");
},
},
],
});
const runner = createHookRunner(registry, { catchErrors: true, logger });
await runner.runMessageReceived({ from: "user-1", content: "hi" }, { channelId: "whatsapp" });
const message = String(logger.error.mock.calls[0]?.[0] ?? "");
expect(message).toMatch(
/^\[hooks\] message_received handler from failing failed: boom forged secret/,
);
expect(message).not.toContain("\n");
expect(message).not.toContain("sk-test1234567890");
});
});
describe("message_sending terminal cancel semantics", () => {

View File

@@ -5,6 +5,7 @@
* error handling, priority ordering, and async support.
*/
import { formatHookErrorForLog } from "../hooks/fire-and-forget.js";
import { formatErrorMessage } from "../infra/errors.js";
import { concatOptionalTextSegments } from "../shared/text/join-segments.js";
import type { GlobalHookRunnerRegistry, HookRunnerRegistry } from "./hook-registry.types.js";
@@ -281,9 +282,7 @@ export function createHookRunner(
pluginId: string;
error: unknown;
}): never | void => {
const msg = `[hooks] ${params.hookName} handler from ${params.pluginId} failed: ${String(
params.error,
)}`;
const msg = `[hooks] ${params.hookName} handler from ${params.pluginId} failed: ${formatHookErrorForLog(params.error)}`;
if (shouldCatchHookErrors(params.hookName)) {
logger?.error(msg);
return;

View File

@@ -82,7 +82,7 @@ describe("inbound_claim hook runner", () => {
expect(result).toEqual({ handled: true });
expect(logger.error).toHaveBeenCalledWith(
expect.stringContaining("inbound_claim handler from test-plugin failed: Error: boom"),
expect.stringContaining("inbound_claim handler from test-plugin failed: boom"),
);
expect(succeeding).toHaveBeenCalledTimes(1);
});

View File

@@ -81,7 +81,7 @@ describe("reply_dispatch hook runner", () => {
counts: { tool: 1, block: 0, final: 0 },
});
expect(logger.error).toHaveBeenCalledWith(
expect.stringContaining("reply_dispatch handler from test-plugin failed: Error: boom"),
expect.stringContaining("reply_dispatch handler from test-plugin failed: boom"),
);
expect(succeeding).toHaveBeenCalledTimes(1);
});