


























@@ -87,6 +87,7 @@ import { filterToolsForVisionInputs } from "./vision-tools.js";
87878888const CODEX_DYNAMIC_TOOL_TIMEOUT_MS = 30_000;
8989const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 60_000;
90+const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 60_000;
9091const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
91929293type OpenClawCodingToolsOptions = NonNullable<
@@ -226,6 +227,7 @@ export async function runCodexAppServerAttempt(
226227hookTimeoutSec?: number;
227228};
228229turnCompletionIdleTimeoutMs?: number;
230+turnTerminalIdleTimeoutMs?: number;
229231} = {},
230232): Promise<EmbeddedRunAttemptResult> {
231233const attemptStartedAt = Date.now();
@@ -471,8 +473,13 @@ export async function runCodexAppServerAttempt(
471473const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(
472474options.turnCompletionIdleTimeoutMs,
473475);
476+const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(
477+options.turnTerminalIdleTimeoutMs,
478+);
474479let turnCompletionIdleTimer: ReturnType<typeof setTimeout> | undefined;
475480let turnCompletionIdleWatchArmed = false;
481+let turnTerminalIdleTimer: ReturnType<typeof setTimeout> | undefined;
482+let turnTerminalIdleWatchArmed = false;
476483let turnCompletionLastActivityAt = Date.now();
477484let turnCompletionLastActivityReason = "startup";
478485let activeAppServerTurnRequests = 0;
@@ -484,6 +491,13 @@ export async function runCodexAppServerAttempt(
484491}
485492};
486493494+const clearTurnTerminalIdleTimer = () => {
495+if (turnTerminalIdleTimer) {
496+clearTimeout(turnTerminalIdleTimer);
497+turnTerminalIdleTimer = undefined;
498+}
499+};
500+487501const fireTurnCompletionIdleTimeout = () => {
488502if (
489503completed ||
@@ -520,6 +534,42 @@ export async function runCodexAppServerAttempt(
520534runAbortController.abort("turn_completion_idle_timeout");
521535};
522536537+const fireTurnTerminalIdleTimeout = () => {
538+if (
539+completed ||
540+runAbortController.signal.aborted ||
541+!turnTerminalIdleWatchArmed ||
542+activeAppServerTurnRequests > 0
543+) {
544+return;
545+}
546+const idleMs = Math.max(0, Date.now() - turnCompletionLastActivityAt);
547+if (idleMs < turnTerminalIdleTimeoutMs) {
548+scheduleTurnTerminalIdleWatch();
549+return;
550+}
551+timedOut = true;
552+turnCompletionIdleTimedOut = true;
553+turnCompletionIdleTimeoutMessage =
554+"codex app-server turn idle timed out waiting for turn/completed";
555+projector?.markTimedOut();
556+trajectoryRecorder?.recordEvent("turn.terminal_idle_timeout", {
557+threadId: thread.threadId,
558+ turnId,
559+ idleMs,
560+timeoutMs: turnTerminalIdleTimeoutMs,
561+lastActivityReason: turnCompletionLastActivityReason,
562+});
563+embeddedAgentLog.warn("codex app-server turn idle timed out waiting for terminal event", {
564+threadId: thread.threadId,
565+ turnId,
566+ idleMs,
567+timeoutMs: turnTerminalIdleTimeoutMs,
568+lastActivityReason: turnCompletionLastActivityReason,
569+});
570+runAbortController.abort("turn_terminal_idle_timeout");
571+};
572+523573function scheduleTurnCompletionIdleWatch() {
524574clearTurnCompletionIdleTimer();
525575if (
@@ -536,13 +586,30 @@ export async function runCodexAppServerAttempt(
536586turnCompletionIdleTimer.unref?.();
537587}
538588589+function scheduleTurnTerminalIdleWatch() {
590+clearTurnTerminalIdleTimer();
591+if (
592+completed ||
593+runAbortController.signal.aborted ||
594+!turnTerminalIdleWatchArmed ||
595+activeAppServerTurnRequests > 0
596+) {
597+return;
598+}
599+const elapsedMs = Math.max(0, Date.now() - turnCompletionLastActivityAt);
600+const delayMs = Math.max(1, turnTerminalIdleTimeoutMs - elapsedMs);
601+turnTerminalIdleTimer = setTimeout(fireTurnTerminalIdleTimeout, delayMs);
602+turnTerminalIdleTimer.unref?.();
603+}
604+539605const touchTurnCompletionActivity = (reason: string, options?: { arm?: boolean }) => {
540606turnCompletionLastActivityAt = Date.now();
541607turnCompletionLastActivityReason = reason;
542608if (options?.arm) {
543609turnCompletionIdleWatchArmed = true;
544610}
545611scheduleTurnCompletionIdleWatch();
612+scheduleTurnTerminalIdleWatch();
546613};
547614548615const emitLifecycleStart = () => {
@@ -595,6 +662,7 @@ export async function runCodexAppServerAttempt(
595662}
596663completed = true;
597664clearTurnCompletionIdleTimer();
665+clearTurnTerminalIdleTimer();
598666resolveCompletion?.();
599667}
600668}
@@ -839,6 +907,7 @@ export async function runCodexAppServerAttempt(
839907abort: () => runAbortController.abort("aborted"),
840908};
841909setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
910+turnTerminalIdleWatchArmed = true;
842911touchTurnCompletionActivity("turn:start");
843912844913const timeout = setTimeout(
@@ -1005,6 +1074,7 @@ export async function runCodexAppServerAttempt(
10051074userInputBridge?.cancelPending();
10061075clearTimeout(timeout);
10071076clearTurnCompletionIdleTimer();
1077+clearTurnTerminalIdleTimer();
10081078notificationCleanup();
10091079requestCleanup();
10101080nativeHookRelay?.unregister();
@@ -1305,6 +1375,16 @@ function resolveCodexTurnCompletionIdleTimeoutMs(value: number | undefined): num
13051375return Math.max(1, Math.floor(value));
13061376}
130713771378+function resolveCodexTurnTerminalIdleTimeoutMs(value: number | undefined): number {
1379+if (value === undefined) {
1380+return CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS;
1381+}
1382+if (!Number.isFinite(value)) {
1383+return CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS;
1384+}
1385+return Math.max(1, Math.floor(value));
1386+}
1387+13081388function readDynamicToolCallParams(
13091389value: JsonValue | undefined,
13101390): CodexDynamicToolCallParams | undefined {
@@ -1417,6 +1497,7 @@ function handleApprovalRequest(params: {
14171497export const __testing = {
14181498CODEX_DYNAMIC_TOOL_TIMEOUT_MS,
14191499CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS,
1500+CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS,
14201501 buildCodexNativeHookRelayId,
14211502 filterToolsForVisionInputs,
14221503 handleDynamicToolCallWithTimeout,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。