fix: stabilize memory dreaming QA

This commit is contained in:
Peter Steinberger
2026-04-21 03:11:31 +01:00
parent a9bef83a0c
commit 8150c363b5
5 changed files with 155 additions and 12 deletions

View File

@@ -5,6 +5,10 @@ const hoisted = vi.hoisted(() => {
const startGmailWatcherWithLogs = vi.fn(async () => undefined);
const loadInternalHooks = vi.fn(async () => 0);
const setInternalHooksEnabled = vi.fn();
const hasInternalHookListeners = vi.fn(() => false);
const startupHookEvent = { type: "gateway", action: "startup", sessionKey: "gateway:startup" };
const createInternalHookEvent = vi.fn(() => startupHookEvent);
const triggerInternalHook = vi.fn(async () => undefined);
const startGatewayMemoryBackend = vi.fn(async () => undefined);
const scheduleGatewayUpdateCheck = vi.fn(() => () => {});
const startGatewayTailscaleExposure = vi.fn(async () => null);
@@ -22,6 +26,10 @@ const hoisted = vi.hoisted(() => {
startGmailWatcherWithLogs,
loadInternalHooks,
setInternalHooksEnabled,
hasInternalHookListeners,
startupHookEvent,
createInternalHookEvent,
triggerInternalHook,
startGatewayMemoryBackend,
scheduleGatewayUpdateCheck,
startGatewayTailscaleExposure,
@@ -61,9 +69,10 @@ vi.mock("../hooks/gmail-watcher-lifecycle.js", () => ({
}));
vi.mock("../hooks/internal-hooks.js", () => ({
createInternalHookEvent: vi.fn(() => ({})),
createInternalHookEvent: hoisted.createInternalHookEvent,
hasInternalHookListeners: hoisted.hasInternalHookListeners,
setInternalHooksEnabled: hoisted.setInternalHooksEnabled,
triggerInternalHook: vi.fn(async () => undefined),
triggerInternalHook: hoisted.triggerInternalHook,
}));
vi.mock("../hooks/loader.js", () => ({
@@ -105,7 +114,8 @@ vi.mock("./server-tailscale.js", () => ({
startGatewayTailscaleExposure: hoisted.startGatewayTailscaleExposure,
}));
const { startGatewayPostAttachRuntime } = await import("./server-startup-post-attach.js");
const { startGatewayPostAttachRuntime, startGatewaySidecars } =
await import("./server-startup-post-attach.js");
const { STARTUP_UNAVAILABLE_GATEWAY_METHODS } =
await import("./server-startup-unavailable-methods.js");
@@ -118,6 +128,10 @@ describe("startGatewayPostAttachRuntime", () => {
hoisted.startGmailWatcherWithLogs.mockClear();
hoisted.loadInternalHooks.mockClear();
hoisted.setInternalHooksEnabled.mockClear();
hoisted.hasInternalHookListeners.mockReset();
hoisted.hasInternalHookListeners.mockReturnValue(false);
hoisted.createInternalHookEvent.mockClear();
hoisted.triggerInternalHook.mockClear();
hoisted.startGatewayMemoryBackend.mockClear();
hoisted.scheduleGatewayUpdateCheck.mockClear();
hoisted.startGatewayTailscaleExposure.mockClear();
@@ -200,6 +214,52 @@ describe("startGatewayPostAttachRuntime", () => {
expect([...unavailableGatewayMethods]).toEqual([]);
expect(startGatewaySidecars).toHaveBeenCalledTimes(1);
});
it("dispatches registered gateway startup internal hooks without configured hook packs", async () => {
vi.useFakeTimers();
hoisted.hasInternalHookListeners.mockReturnValue(true);
const cfg = {} as never;
const deps = {} as never;
try {
await startGatewaySidecars({
cfg,
pluginRegistry: createPostAttachParams().pluginRegistry,
defaultWorkspaceDir: "/tmp/openclaw-workspace",
deps,
startChannels: vi.fn(async () => undefined),
log: { warn: vi.fn() },
logHooks: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
},
logChannels: {
info: vi.fn(),
error: vi.fn(),
},
});
expect(hoisted.loadInternalHooks).not.toHaveBeenCalled();
expect(hoisted.hasInternalHookListeners).toHaveBeenCalledWith("gateway", "startup");
await vi.advanceTimersByTimeAsync(250);
expect(hoisted.createInternalHookEvent).toHaveBeenCalledWith(
"gateway",
"startup",
"gateway:startup",
{
cfg,
deps,
workspaceDir: "/tmp/openclaw-workspace",
},
);
expect(hoisted.triggerInternalHook).toHaveBeenCalledWith(hoisted.startupHookEvent);
} finally {
vi.useRealTimers();
}
});
});
function createPostAttachRuntimeDeps(

View File

@@ -40,6 +40,11 @@ function shouldStartGatewayMemoryBackend(cfg: OpenClawConfig): boolean {
return cfg.memory?.backend === "qmd";
}
async function hasGatewayStartupInternalHookListeners(): Promise<boolean> {
const { hasInternalHookListeners } = await import("../hooks/internal-hooks.js");
return hasInternalHookListeners("gateway", "startup");
}
async function prewarmConfiguredPrimaryModel(params: {
cfg: OpenClawConfig;
log: { warn: (msg: string) => void };
@@ -232,7 +237,9 @@ export async function startGatewaySidecars(params: {
}
});
if (internalHooksConfigured) {
const shouldDispatchGatewayStartupInternalHook =
internalHooksConfigured || (await hasGatewayStartupInternalHookListeners());
if (shouldDispatchGatewayStartupInternalHook) {
setTimeout(() => {
void import("../hooks/internal-hooks.js").then(
({ createInternalHookEvent, triggerInternalHook }) => {