mirror of https://github.com/openclaw/openclaw.git
fix: skip session:patch hook clone without listeners
This commit is contained in:
parent
ee0dcaa7b0
commit
765182dcc6
|
|
@ -19,6 +19,7 @@ import {
|
|||
updateSessionStore,
|
||||
} from "../../config/sessions.js";
|
||||
import {
|
||||
hasInternalHookListeners,
|
||||
triggerInternalHook,
|
||||
type SessionPatchHookContext,
|
||||
type SessionPatchHookEvent,
|
||||
|
|
@ -899,20 +900,22 @@ export const sessionsHandlers: GatewayRequestHandlers = {
|
|||
return;
|
||||
}
|
||||
|
||||
const hookContext: SessionPatchHookContext = structuredClone({
|
||||
sessionEntry: applied.entry,
|
||||
patch: p,
|
||||
cfg,
|
||||
});
|
||||
const hookEvent: SessionPatchHookEvent = {
|
||||
type: "session",
|
||||
action: "patch",
|
||||
sessionKey: target.canonicalKey ?? key,
|
||||
context: hookContext,
|
||||
timestamp: new Date(),
|
||||
messages: [],
|
||||
};
|
||||
void triggerInternalHook(hookEvent);
|
||||
if (hasInternalHookListeners("session", "patch")) {
|
||||
const hookContext: SessionPatchHookContext = structuredClone({
|
||||
sessionEntry: applied.entry,
|
||||
patch: p,
|
||||
cfg,
|
||||
});
|
||||
const hookEvent: SessionPatchHookEvent = {
|
||||
type: "session",
|
||||
action: "patch",
|
||||
sessionKey: target.canonicalKey ?? key,
|
||||
context: hookContext,
|
||||
timestamp: new Date(),
|
||||
messages: [],
|
||||
};
|
||||
void triggerInternalHook(hookEvent);
|
||||
}
|
||||
|
||||
const parsed = parseAgentSessionKey(target.canonicalKey ?? key);
|
||||
const agentId = normalizeAgentId(parsed?.agentId ?? resolveDefaultAgentId(cfg));
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ const bootstrapCacheMocks = vi.hoisted(() => ({
|
|||
}));
|
||||
|
||||
const sessionHookMocks = vi.hoisted(() => ({
|
||||
hasInternalHookListeners: vi.fn(() => true),
|
||||
triggerInternalHook: vi.fn(async (_event: unknown) => {}),
|
||||
}));
|
||||
|
||||
|
|
@ -96,6 +97,7 @@ vi.mock("../hooks/internal-hooks.js", async () => {
|
|||
);
|
||||
return {
|
||||
...actual,
|
||||
hasInternalHookListeners: sessionHookMocks.hasInternalHookListeners,
|
||||
triggerInternalHook: sessionHookMocks.triggerInternalHook,
|
||||
};
|
||||
});
|
||||
|
|
@ -260,6 +262,8 @@ describe("gateway server sessions", () => {
|
|||
sessionCleanupMocks.clearSessionQueues.mockClear();
|
||||
sessionCleanupMocks.stopSubagentsForRequester.mockClear();
|
||||
bootstrapCacheMocks.clearBootstrapSnapshot.mockReset();
|
||||
sessionHookMocks.hasInternalHookListeners.mockReset();
|
||||
sessionHookMocks.hasInternalHookListeners.mockReturnValue(true);
|
||||
sessionHookMocks.triggerInternalHook.mockClear();
|
||||
subagentLifecycleHookMocks.runSubagentEnded.mockClear();
|
||||
subagentLifecycleHookState.hasSubagentEndedHook = true;
|
||||
|
|
@ -1938,6 +1942,30 @@ describe("gateway server sessions", () => {
|
|||
ws.close();
|
||||
});
|
||||
|
||||
test("session:patch skips clone and dispatch when no hooks listen", async () => {
|
||||
const structuredCloneSpy = vi.spyOn(globalThis, "structuredClone");
|
||||
sessionHookMocks.hasInternalHookListeners.mockReturnValue(false);
|
||||
|
||||
const { ws } = await openClient();
|
||||
const patched = await rpcReq(ws, "sessions.patch", {
|
||||
key: "agent:main:main",
|
||||
label: "no-hook-listener",
|
||||
});
|
||||
|
||||
expect(patched.ok).toBe(true);
|
||||
expect(structuredCloneSpy).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: expect.any(Object),
|
||||
patch: expect.any(Object),
|
||||
sessionEntry: expect.any(Object),
|
||||
}),
|
||||
);
|
||||
expect(sessionHookMocks.triggerInternalHook).not.toHaveBeenCalled();
|
||||
|
||||
structuredCloneSpy.mockRestore();
|
||||
ws.close();
|
||||
});
|
||||
|
||||
test("session:patch hook mutations cannot change the response path", async () => {
|
||||
await createSessionStoreDir();
|
||||
await writeSessionStore({
|
||||
|
|
|
|||
|
|
@ -268,6 +268,12 @@ export function getRegisteredEventKeys(): string[] {
|
|||
return Array.from(handlers.keys());
|
||||
}
|
||||
|
||||
export function hasInternalHookListeners(type: InternalHookEventType, action: string): boolean {
|
||||
return (
|
||||
(handlers.get(type)?.length ?? 0) > 0 || (handlers.get(`${type}:${action}`)?.length ?? 0) > 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a hook event
|
||||
*
|
||||
|
|
@ -281,15 +287,14 @@ export function getRegisteredEventKeys(): string[] {
|
|||
* @param event - The event to trigger
|
||||
*/
|
||||
export async function triggerInternalHook(event: InternalHookEvent): Promise<void> {
|
||||
const typeHandlers = handlers.get(event.type) ?? [];
|
||||
const specificHandlers = handlers.get(`${event.type}:${event.action}`) ?? [];
|
||||
|
||||
const allHandlers = [...typeHandlers, ...specificHandlers];
|
||||
|
||||
if (allHandlers.length === 0) {
|
||||
if (!hasInternalHookListeners(event.type, event.action)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const typeHandlers = handlers.get(event.type) ?? [];
|
||||
const specificHandlers = handlers.get(`${event.type}:${event.action}`) ?? [];
|
||||
const allHandlers = [...typeHandlers, ...specificHandlers];
|
||||
|
||||
for (const handler of allHandlers) {
|
||||
try {
|
||||
await handler(event);
|
||||
|
|
|
|||
Loading…
Reference in New Issue