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
+2 -2
View File
@@ -39,10 +39,10 @@ describe('GeminiCliHooksInstaller - event mapping', () => {
expect(src).toContain("'SessionStart': 'context'");
});
it('should map SessionEnd to session-complete (unchanged)', async () => {
it('should not map SessionEnd (worker self-completes; /clear must not drain queue)', async () => {
const { readFileSync } = await import('fs');
const src = readFileSync('src/services/integrations/GeminiCliHooksInstaller.ts', 'utf-8');
expect(src).toContain("'SessionEnd': 'session-complete'");
expect(src).not.toContain("'SessionEnd':");
});
});
+1 -10
View File
@@ -18,7 +18,7 @@ describe('Hook Lifecycle - Event Handlers', () => {
const { getEventHandler } = await import('../src/cli/handlers/index.js');
const recognizedTypes = [
'context', 'session-init', 'observation',
'summarize', 'session-complete', 'user-message', 'file-edit'
'summarize', 'user-message', 'file-edit'
];
for (const type of recognizedTypes) {
const handler = getEventHandler(type);
@@ -42,15 +42,6 @@ describe('Hook Lifecycle - Event Handlers', () => {
expect(result.exitCode).toBe(0);
});
it('should include session-complete as a recognized event type (#984)', async () => {
const { getEventHandler } = await import('../src/cli/handlers/index.js');
const handler = getEventHandler('session-complete');
// session-complete should NOT be the no-op handler
// We can verify this by checking it's not the same as an unknown type handler
expect(handler).toBeDefined();
// The real handler has different behavior than the no-op
// (it tries to call the worker, while no-op just returns immediately)
});
});
});