From b15c860b35694eeba782846d7eb8614fb3a8a6cb Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:15:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=AD=89=E5=BE=85=20codex=20terminal=20?= =?UTF-8?q?=E5=90=8E=E7=BB=AD=E6=B6=88=E6=81=AF=20(#234)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/codex-stdio.ts | 22 +++++++++++++++++++++- src/selftest/fake-codex-app-server.ts | 8 +++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/backend/codex-stdio.ts b/src/backend/codex-stdio.ts index 370db5b..1aefa66 100644 --- a/src/backend/codex-stdio.ts +++ b/src/backend/codex-stdio.ts @@ -470,6 +470,20 @@ async function runCodexStdioTurnWithSession(options: CodexStdioTurnOptions, sess let terminal: { status: TerminalStatus; failureKind: FailureKind | null; message: string | null } | null = null; let terminalResolve!: () => void; const terminalPromise = new Promise((resolve) => { terminalResolve = resolve; }); + const terminalNotificationDrainMs = codexTerminalNotificationDrainMs(env); + let terminalNotificationDrainTimeout: NodeJS.Timeout | null = null; + const resolveTerminalAfterNotificationDrain = (): void => { + if (terminalNotificationDrainTimeout) clearTimeout(terminalNotificationDrainTimeout); + if (terminalNotificationDrainMs <= 0) { + terminalResolve(); + return; + } + terminalNotificationDrainTimeout = setTimeout(() => { + terminalNotificationDrainTimeout = null; + terminalResolve(); + }, terminalNotificationDrainMs); + terminalNotificationDrainTimeout.unref?.(); + }; let client: CodexStdioClient | null = null; const requestTimeoutMs = Math.min(positiveTimeout(options.timeoutMs), requestTimeoutCapMs); let stopActiveTurn: (() => void) | undefined; @@ -676,7 +690,7 @@ async function runCodexStdioTurnWithSession(options: CodexStdioTurnOptions, sess if (normalized.terminalEvents) deferredTerminalEvents.push(...normalized.terminalEvents); if (normalized.terminal && !terminal) { terminal = normalized.terminal; - terminalResolve(); + resolveTerminalAfterNotificationDrain(); } }); try { @@ -1487,6 +1501,12 @@ function codexMissingTerminalAfterToolTimeoutMs(env: NodeJS.ProcessEnv, turnTime return positiveTimeout(turnTimeoutMs); } +function codexTerminalNotificationDrainMs(env: NodeJS.ProcessEnv): number { + const configured = Number(env.AGENTRUN_CODEX_TERMINAL_NOTIFICATION_DRAIN_MS); + if (Number.isFinite(configured) && configured >= 0) return Math.floor(configured); + return 250; +} + function emitCodexNotificationOtel(options: CodexStdioTurnOptions, env: NodeJS.ProcessEnv, message: JsonRecord, state: JsonRecord): void { const attributes = { ...state, ...notificationOtelAttributes(message) }; emitCodexOtelSpan("codex_stdio.notification", options, env, attributes); diff --git a/src/selftest/fake-codex-app-server.ts b/src/selftest/fake-codex-app-server.ts index eb0cdf8..0886c12 100644 --- a/src/selftest/fake-codex-app-server.ts +++ b/src/selftest/fake-codex-app-server.ts @@ -226,12 +226,14 @@ for await (const line of rl) { if (mode === "multi-agent-message-terminal-before-final") { turnCounter += 1; const turn = { id: `turn_selftest_${turnCounter}`, status: "completed" }; + respond(message.id, { turn }); notify("turn/started", { turn }); notify("item/agentMessage/delta", { itemId: "msg_late_progress", delta: "Progress before delayed final. " }); notify("turn/completed", { turn }); - notify("item/completed", { item: { id: "msg_late_progress", type: "agentMessage", text: "Progress before delayed final." } }); - notify("item/completed", { item: { id: "msg_late_final", type: "agentMessage", text: "Delayed final answer." } }); - respond(message.id, { turn }); + setTimeout(() => { + notify("item/completed", { item: { id: "msg_late_progress", type: "agentMessage", text: "Progress before delayed final." } }); + notify("item/completed", { item: { id: "msg_late_final", type: "agentMessage", text: "Delayed final answer." } }); + }, 20).unref?.(); continue; } if (mode === "web-search-progress") {