fix: stabilize internal hooks singleton registry (#32292) (thanks @Drickon)

This commit is contained in:
Peter Steinberger
2026-03-03 00:26:58 +00:00
parent d0a3743abd
commit 68e982ec80
2 changed files with 20 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
- Gateway/Control UI basePath webhook passthrough: let non-read methods under configured `controlUiBasePath` fall through to plugin routes (instead of returning Control UI 405), restoring webhook handlers behind basePath mounts. (#32311) Thanks @ademczuk.
- Models/config env propagation: apply `config.env.vars` before implicit provider discovery in models bootstrap so config-scoped credentials are visible to implicit provider resolution paths. (#32295) Thanks @hsiaoa.
- Hooks/runtime stability: keep the internal hook handler registry on a `globalThis` singleton so hook registration/dispatch remains consistent when bundling emits duplicate module copies. (#32292) Thanks @Drickon.
- Voice-call/Twilio signature verification: retry signature validation across deterministic URL port variants (with/without port) to handle mixed Twilio signing behavior behind reverse proxies and non-standard ports. (#25140) Thanks @drvoss.
- Hooks/webhook ACK compatibility: return `200` (instead of `202`) for successful `/hooks/agent` requests so providers that require `200` (for example Forward Email) accept dispatched agent hook deliveries. (#28204) Thanks @Glucksberg.
- Voice-call/Twilio external outbound: auto-register webhook-first `outbound-api` calls (initiated outside OpenClaw) so media streams are accepted and call direction metadata stays accurate. (#31181) Thanks @scoootscooob.

View File

@@ -142,6 +142,25 @@ describe("hooks", () => {
const event = createInternalHookEvent("command", "new", "test-session");
await expect(triggerInternalHook(event)).resolves.not.toThrow();
});
it("stores handlers in the global singleton registry", async () => {
const globalHooks = globalThis as typeof globalThis & {
__openclaw_internal_hook_handlers__?: Map<string, Array<(event: unknown) => unknown>>;
};
const handler = vi.fn();
registerInternalHook("command:new", handler);
const event = createInternalHookEvent("command", "new", "test-session");
await triggerInternalHook(event);
expect(handler).toHaveBeenCalledWith(event);
expect(globalHooks.__openclaw_internal_hook_handlers__?.has("command:new")).toBe(true);
const injectedHandler = vi.fn();
globalHooks.__openclaw_internal_hook_handlers__?.set("command:new", [injectedHandler]);
await triggerInternalHook(event);
expect(injectedHandler).toHaveBeenCalledWith(event);
});
});
describe("createInternalHookEvent", () => {