diff --git a/src/chat/sessionBridge.ts b/src/chat/sessionBridge.ts index 3527764..ef76879 100644 --- a/src/chat/sessionBridge.ts +++ b/src/chat/sessionBridge.ts @@ -63,6 +63,16 @@ export class ChatSessionBridge { try { // 只有 opencode 侧 session 仍存在时,才复用本地映射。 await this.runtime.getSession(current.sessionId); + await this.runtime.waitForSessionIdle(current.sessionId).catch((error) => { + logger.warn( + { + clientSessionId: requestContext.clientSessionId, + sessionId: current.sessionId, + err: error, + }, + "failed while waiting for reused opencode session to become idle", + ); + }); return { binding: current, requestContext, created: false }; } catch (error) { logger.warn( @@ -161,6 +171,12 @@ export class ChatSessionBridge { traceId: requestContext.traceId, }); await this.runtime.abortSession(binding.sessionId); + await this.runtime.waitForSessionIdle(binding.sessionId).catch((error) => { + logger.warn( + { clientSessionId, sessionId: binding.sessionId, err: error }, + "failed while waiting for aborted opencode session to become idle", + ); + }); return binding; } diff --git a/src/routes/chatStream.ts b/src/routes/chatStream.ts index 7d5f315..e31c6c5 100644 --- a/src/routes/chatStream.ts +++ b/src/routes/chatStream.ts @@ -730,6 +730,12 @@ export const streamPromptResponse = async ({ await runtime.abortSession(opencodeSessionId).catch((error) => { logger.warn({ sessionId: opencodeSessionId, err: error }, "failed to abort opencode session"); }); + await runtime.waitForSessionIdle(opencodeSessionId).catch((error) => { + logger.warn( + { sessionId: opencodeSessionId, err: error }, + "failed while waiting for aborted opencode session to become idle", + ); + }); return { aborted: true, failed: false, toolCallCount }; } diff --git a/src/runtime/opencode.ts b/src/runtime/opencode.ts index 11b77b0..087fcc2 100644 --- a/src/runtime/opencode.ts +++ b/src/runtime/opencode.ts @@ -92,6 +92,26 @@ export class OpencodeRuntimeAdapter { return requireData(response.data, "session.abort"); } + async waitForSessionIdle(sessionId: string, timeoutMs = config.OPENCODE_TIMEOUT_MS) { + const client = await this.ensureClient(); + const startedAt = Date.now(); + + while (Date.now() - startedAt < timeoutMs) { + const response = await client.session.status({}); + const statuses = requireData(response.data, "session.status"); + const status = statuses[sessionId]; + if (!status || status.type === "idle") { + return; + } + await delay(100); + } + + logger.warn( + { sessionId, timeoutMs }, + "timed out waiting for opencode session to become idle", + ); + } + async subscribeEvents() { const client = await this.ensureClient(); const response = await client.event.subscribe(); @@ -180,3 +200,9 @@ function requireData(data: T | undefined, operation: string): T { } return data; } + +function delay(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}