gateway: await before_reset hooks after cleanup

This commit is contained in:
Vincent Koc 2026-03-09 10:27:57 -07:00
parent 72535d5508
commit e2419f445d
5 changed files with 41 additions and 42 deletions

View File

@ -40,7 +40,7 @@ describe("emitResetCommandHooks", () => {
workspaceDir: "/tmp/openclaw-workspace",
});
await vi.waitFor(() => expect(hookRunnerMocks.runBeforeReset).toHaveBeenCalledTimes(1));
expect(hookRunnerMocks.runBeforeReset).toHaveBeenCalledTimes(1);
const [, ctx] = hookRunnerMocks.runBeforeReset.mock.calls[0] ?? [];
return ctx;
}

View File

@ -90,7 +90,7 @@ export async function emitResetCommandHooks(params: {
}
}
emitBeforeResetPluginHook({
await emitBeforeResetPluginHook({
sessionKey: params.sessionKey,
previousSessionEntry: params.previousSessionEntry,
workspaceDir: params.workspaceDir,

View File

@ -8,12 +8,12 @@ type BeforeResetSessionEntry = {
sessionFile?: string;
} | null;
export function emitBeforeResetPluginHook(params: {
export async function emitBeforeResetPluginHook(params: {
sessionKey?: string;
previousSessionEntry?: BeforeResetSessionEntry;
workspaceDir: string;
reason: string;
}): void {
}): Promise<void> {
const hookRunner = getGlobalHookRunner();
if (!hookRunner?.hasHooks("before_reset")) {
return;
@ -22,39 +22,36 @@ export function emitBeforeResetPluginHook(params: {
const prevEntry = params.previousSessionEntry;
const sessionFile = prevEntry?.sessionFile;
// Fire-and-forget: read old session messages and run hook before reset mutates the store.
void (async () => {
try {
const messages: unknown[] = [];
if (sessionFile) {
const content = await fs.readFile(sessionFile, "utf-8");
for (const line of content.split("\n")) {
if (!line.trim()) {
continue;
}
try {
const entry = JSON.parse(line);
if (entry.type === "message" && entry.message) {
messages.push(entry.message);
}
} catch {
// Skip malformed transcript lines.
}
try {
const messages: unknown[] = [];
if (sessionFile) {
const content = await fs.readFile(sessionFile, "utf-8");
for (const line of content.split("\n")) {
if (!line.trim()) {
continue;
}
try {
const entry = JSON.parse(line);
if (entry.type === "message" && entry.message) {
messages.push(entry.message);
}
} catch {
// Skip malformed transcript lines.
}
} else {
logVerbose("before_reset: no session file available, firing hook with empty messages");
}
await hookRunner.runBeforeReset(
{ sessionFile, messages, reason: params.reason },
{
agentId: resolveAgentIdFromSessionKey(params.sessionKey),
sessionKey: params.sessionKey,
sessionId: prevEntry?.sessionId,
workspaceDir: params.workspaceDir,
},
);
} catch (err: unknown) {
logVerbose(`before_reset hook failed: ${String(err)}`);
} else {
logVerbose("before_reset: no session file available, firing hook with empty messages");
}
})();
await hookRunner.runBeforeReset(
{ sessionFile, messages, reason: params.reason },
{
agentId: resolveAgentIdFromSessionKey(params.sessionKey),
sessionKey: params.sessionKey,
sessionId: prevEntry?.sessionId,
workspaceDir: params.workspaceDir,
},
);
} catch (err: unknown) {
logVerbose(`before_reset hook failed: ${String(err)}`);
}
}

View File

@ -490,12 +490,6 @@ export const sessionsHandlers: GatewayRequestHandlers = {
},
);
await triggerInternalHook(hookEvent);
emitBeforeResetPluginHook({
sessionKey: target.canonicalKey ?? key,
previousSessionEntry: entry,
workspaceDir: resolveAgentWorkspaceDir(cfg, target.agentId),
reason: commandReason,
});
const mutationCleanupError = await cleanupSessionBeforeMutation({
cfg,
key,
@ -509,6 +503,12 @@ export const sessionsHandlers: GatewayRequestHandlers = {
respond(false, undefined, mutationCleanupError);
return;
}
await emitBeforeResetPluginHook({
sessionKey: target.canonicalKey ?? key,
previousSessionEntry: entry,
workspaceDir: resolveAgentWorkspaceDir(cfg, target.agentId),
reason: commandReason,
});
let oldSessionId: string | undefined;
let oldSessionFile: string | undefined;
const next = await updateSessionStore(storePath, (store) => {

View File

@ -1235,6 +1235,7 @@ describe("gateway server sessions", () => {
embeddedRunMock.activeIds.add("sess-main");
embeddedRunMock.waitResults.set("sess-main", false);
subagentLifecycleHookState.hasBeforeResetHook = true;
const { ws } = await openClient();
@ -1251,6 +1252,7 @@ describe("gateway server sessions", () => {
);
expect(waitCallCountAtSnapshotClear).toEqual([1]);
expect(browserSessionTabMocks.closeTrackedBrowserTabsForSessions).not.toHaveBeenCalled();
expect(beforeResetHookMocks.runBeforeReset).not.toHaveBeenCalled();
const store = JSON.parse(await fs.readFile(storePath, "utf-8")) as Record<
string,