diff --git a/src/chat/sessionBridge.ts b/src/chat/sessionBridge.ts index b59d32b..855789b 100644 --- a/src/chat/sessionBridge.ts +++ b/src/chat/sessionBridge.ts @@ -31,6 +31,7 @@ export class ChatSessionBridge { // runtime session 仅在单次请求生命周期内有效;线程连续性由 clientSessionId 对应的持久状态承担。 private readonly activeRuntimeSessions = new Map(); private readonly activeSensitiveContexts = new Map(); + private readonly abortControllers = new Map(); private readonly toolContextStore = new ToolSessionContextStore(); constructor(private readonly runtime: OpencodeRuntimeAdapter) {} @@ -89,6 +90,14 @@ export class ChatSessionBridge { return this.activeSensitiveContexts.get(sessionScopeKey) ?? null; } + registerAbortController(clientSessionId: string, controller: AbortController) { + this.abortControllers.set(clientSessionId, controller); + } + + deleteAbortController(clientSessionId: string) { + this.abortControllers.delete(clientSessionId); + } + async abort(context: { clientSessionId?: string; }): Promise { @@ -144,11 +153,18 @@ export class ChatSessionBridge { private async abortActiveRuntime(clientSessionId: string) { const activeSessionId = this.activeRuntimeSessions.get(clientSessionId); + this.activeRuntimeSessions.delete(clientSessionId); + this.activeSensitiveContexts.delete(findScopeKey(this.activeSensitiveContexts, clientSessionId)); + + const controller = this.abortControllers.get(clientSessionId); + if (controller) { + this.abortControllers.delete(clientSessionId); + controller.abort(); + } + if (!activeSessionId) { return; } - this.activeRuntimeSessions.delete(clientSessionId); - this.activeSensitiveContexts.delete(findScopeKey(this.activeSensitiveContexts, clientSessionId)); await this.toolContextStore.remove(activeSessionId).catch(() => undefined); await this.runtime.abortSession(activeSessionId).catch((error) => { logger.warn( diff --git a/src/routes/chat.ts b/src/routes/chat.ts index 25e153a..9bd18ae 100644 --- a/src/routes/chat.ts +++ b/src/routes/chat.ts @@ -505,6 +505,7 @@ export const buildChatRouter = ( const clientSessionId = requestContext.clientSessionId; let streamClosed = false; const abortController = new AbortController(); + sessionBridge.registerAbortController(clientSessionId, abortController); const handleClientClose = () => { if (streamClosed || abortController.signal.aborted) { return; @@ -606,6 +607,7 @@ export const buildChatRouter = ( } } finally { await sessionBridge.releaseRuntimeSession(clientSessionId, binding.sessionId); + sessionBridge.deleteAbortController(clientSessionId); streamClosed = true; req.off("close", handleClientClose); res.off("close", handleClientClose);