fix: stop draining queue on /clear (remove SessionEnd shim) (#2136)

* fix: stop draining queue on /clear (and on every other SessionEnd)

The SessionEnd hook was wired to session-complete on Claude Code, Gemini
CLI, the transcripts processor, the OpenCode plugin, and OpenClaw. All of
those paths called POST /api/sessions/complete, which marked the session
completed and abandoned every still-pending observation in the queue.

So typing /clear (or logging out, or quitting) wiped in-flight work that
the worker was perfectly happy to keep processing on its own.

Removed the entire shim:
- Deleted SessionEnd hook block in plugin/hooks/hooks.json
- Deleted src/cli/handlers/session-complete.ts and its registry entry
- Deleted POST /api/sessions/complete route + Zod schema in SessionRoutes
- Removed call from transcripts processor handleSessionEnd
- Removed call from opencode-plugin session.deleted handler
- Removed Gemini SessionEnd → session-complete mapping
- Removed openclaw scheduleSessionComplete + completionDelayMs + timer state
- Updated tests + comments accordingly

Explicit user-initiated deletion (DELETE /api/sessions/:id and
POST /api/sessions/:sessionDbId/complete from the viewer UI) still works
via SessionCompletionHandler.completeByDbId — that's the only path that
should drain the queue.

The worker self-completes via its SDK-agent generator's finally-block, so
no external completion call is needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: clarify opencode-plugin session.deleted is in-memory cleanup only

Greptile P2: file-level header still implied session.deleted called the
worker. Now it only cleans up the local contentSessionIdsByOpenCodeSessionId
map; worker self-completes via the SDK-agent generator finally-block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-04-25 17:08:35 -07:00
committed by GitHub
parent 298f5463d9
commit 8e0e3ca109
17 changed files with 540 additions and 695 deletions
+1 -8
View File
@@ -266,12 +266,6 @@ describe("Observation I/O event handlers", () => {
return;
}
if (req.url === "/api/sessions/complete") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "completed" }));
return;
}
if (req.url?.startsWith("/api/context/inject")) {
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
res.end("# Claude-Mem Context\n\n## Timeline\n- Session 1: Did some work");
@@ -446,8 +440,7 @@ describe("Observation I/O event handlers", () => {
assert.ok(summarizeRequest!.body.contentSessionId.startsWith("openclaw-summarize-test-"));
const completeRequest = receivedRequests.find((r) => r.url === "/api/sessions/complete");
assert.ok(completeRequest, "should send complete to worker");
assert.ok(completeRequest!.body.contentSessionId.startsWith("openclaw-summarize-test-"));
assert.ok(!completeRequest, "should not send complete (worker self-completes)");
});
it("agent_end extracts text from array content", async () => {
+3 -28
View File
@@ -644,12 +644,7 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
const sessionIds = new Map<string, string>();
const canonicalSessionKeys = new Map<string, string>();
const sessionAliasesByCanonicalKey = new Map<string, Set<string>>();
const pendingCompletionTimers = new Map<string, ReturnType<typeof setTimeout>>();
const recentPromptInits = new Map<string, number>();
const completionDelayMs = (() => {
const val = Number((userConfig as Record<string, unknown>).completionDelayMs);
return Number.isFinite(val) ? Math.max(0, val) : 5000;
})();
const syncMemoryFile = userConfig.syncMemoryFile !== false; // default true
const syncMemoryFileExclude = new Set(userConfig.syncMemoryFileExclude || []);
@@ -733,18 +728,6 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
sessionIds.delete(canonicalKey);
}
function scheduleSessionComplete(contentSessionId: string): void {
const existingTimer = pendingCompletionTimers.get(contentSessionId);
if (existingTimer) clearTimeout(existingTimer);
const timer = setTimeout(() => {
pendingCompletionTimers.delete(contentSessionId);
workerPostFireAndForget(workerPort, "/api/sessions/complete", {
contentSessionId,
}, api.logger);
}, completionDelayMs);
pendingCompletionTimers.set(contentSessionId, timer);
}
// TTL cache for context injection to avoid re-fetching on every LLM turn.
// before_prompt_build fires on every turn; caching for 60s keeps the worker
// load manageable while still picking up new observations reasonably quickly.
@@ -898,7 +881,7 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
});
// ------------------------------------------------------------------
// Event: agent_end — summarize and complete session
// Event: agent_end — summarize session (worker self-completes)
// ------------------------------------------------------------------
api.on("agent_end", async (event, ctx) => {
const { contentSessionId } = rememberSessionContext(ctx);
@@ -922,16 +905,12 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
}
}
// Await summarize so the worker receives it before complete.
// This also gives in-flight tool_result_persist observations time to arrive
// (they use fire-and-forget and may still be in transit).
// Send summarize. The worker self-completes the session when its SDK-agent
// generator drains; no explicit complete call needed.
await workerPost(workerPort, "/api/sessions/summarize", {
contentSessionId,
last_assistant_message: lastAssistantMessage,
}, api.logger);
api.logger.info(`[claude-mem] Scheduling session complete in ${completionDelayMs}ms: ${contentSessionId}`);
scheduleSessionComplete(contentSessionId);
});
// ------------------------------------------------------------------
@@ -952,10 +931,6 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
recentPromptInits.clear();
canonicalSessionKeys.clear();
sessionAliasesByCanonicalKey.clear();
for (const timer of pendingCompletionTimers.values()) {
clearTimeout(timer);
}
pendingCompletionTimers.clear();
api.logger.info("[claude-mem] Gateway started — session tracking reset");
});