Skip to content
This repository was archived by the owner on Feb 14, 2026. It is now read-only.

Commit 5c0a56f

Browse files
authored
Merge pull request #109 from leeroybrun/pr/codex-abort-vs-kill
codex: make abort graceful; force-close MCP on kill/exit
2 parents a9385ca + 2b5e6bd commit 5c0a56f

2 files changed

Lines changed: 30 additions & 15 deletions

File tree

src/codex/codexMcpClient.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,21 @@ export class CodexMcpClient {
312312
return this.sessionId;
313313
}
314314

315+
/**
316+
* Force close the Codex MCP transport and clear all session identifiers.
317+
* Use this for permanent shutdown (e.g. kill/exit). Prefer `disconnect()` for
318+
* transient connection resets where you may want to keep the session id.
319+
*/
320+
async forceCloseSession(): Promise<void> {
321+
logger.debug('[CodexMCP] Force closing session');
322+
try {
323+
await this.disconnect();
324+
} finally {
325+
this.clearSession();
326+
}
327+
logger.debug('[CodexMCP] Session force-closed');
328+
}
329+
315330
async disconnect(): Promise<void> {
316331
if (!this.connected) return;
317332

@@ -344,9 +359,7 @@ export class CodexMcpClient {
344359

345360
this.transport = null;
346361
this.connected = false;
347-
this.sessionId = null;
348-
this.conversationId = null;
349-
350-
logger.debug('[CodexMCP] Disconnected');
362+
// Preserve session/conversation identifiers for potential reconnection / recovery flows.
363+
logger.debug(`[CodexMCP] Disconnected; session ${this.sessionId ?? 'none'} preserved`);
351364
}
352365
}

src/codex/runCodex.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,7 @@ export async function runCodex(opts: {
252252
}
253253

254254
abortController.abort();
255-
messageQueue.reset();
256-
permissionHandler.reset();
257255
reasoningProcessor.abort();
258-
diffProcessor.reset();
259256
logger.debug('[Codex] Abort completed - session remains active');
260257
} catch (error) {
261258
logger.debug('[Codex] Error during abort:', error);
@@ -292,6 +289,13 @@ export async function runCodex(opts: {
292289
await session.close();
293290
}
294291

292+
// Force close Codex transport (best-effort) so we don't leave stray processes
293+
try {
294+
await client.forceCloseSession();
295+
} catch (e) {
296+
logger.debug('[Codex] Error while force closing Codex session during termination', e);
297+
}
298+
295299
// Stop caffeinate
296300
stopCaffeinate();
297301

@@ -713,11 +717,9 @@ export async function runCodex(opts: {
713717
if (isAbortError) {
714718
messageBuffer.addMessage('Aborted by user', 'status');
715719
session.sendSessionEvent({ type: 'message', message: 'Aborted by user' });
716-
// Session was already stored in handleAbort(), no need to store again
717-
// Mark session as not created to force proper resume on next message
718-
wasCreated = false;
719-
currentModeHash = null;
720-
logger.debug('[Codex] Marked session as not created after abort for proper resume');
720+
// Abort cancels the current task/inference but keeps the Codex session alive.
721+
// Do not clear session state here; the next user message should continue on the
722+
// existing session if possible.
721723
} else {
722724
messageBuffer.addMessage('Process exited unexpectedly', 'status');
723725
session.sendSessionEvent({ type: 'message', message: 'Process exited unexpectedly' });
@@ -767,9 +769,9 @@ export async function runCodex(opts: {
767769
} catch (e) {
768770
logger.debug('[codex]: Error while closing session', e);
769771
}
770-
logger.debug('[codex]: client.disconnect begin');
771-
await client.disconnect();
772-
logger.debug('[codex]: client.disconnect done');
772+
logger.debug('[codex]: client.forceCloseSession begin');
773+
await client.forceCloseSession();
774+
logger.debug('[codex]: client.forceCloseSession done');
773775
// Stop Happy MCP server
774776
logger.debug('[codex]: happyServer.stop');
775777
happyServer.stop();

0 commit comments

Comments
 (0)