
























@@ -32,8 +32,15 @@ type CodexNativeCompactionWaiter = {
32323333const DEFAULT_CODEX_COMPACTION_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
3434const CODEX_COMPACTION_TOKEN_USAGE_GRACE_MS = 250;
35+const MAX_CODEX_NATIVE_COMPACTION_ATTEMPTS = 2;
3536const warnedIgnoredCompactionOverrides = new Set<string>();
363738+class CodexNativeCompactionTimeoutError extends Error {
39+constructor(readonly threadId: string) {
40+super(`timed out waiting for codex app-server compaction for ${threadId}`);
41+}
42+}
43+3744export async function maybeCompactCodexAppServerSession(
3845params: CompactEmbeddedPiSessionParams,
3946options: { pluginConfig?: unknown; clientFactory?: CodexAppServerClientFactory } = {},
@@ -354,38 +361,70 @@ async function compactCodexNativeThread(
354361}
355362356363const clientFactory = options.clientFactory ?? defaultCodexAppServerClientFactory;
357-const client = await clientFactory(
358-appServer.start,
359-requestedAuthProfileId ?? binding.authProfileId,
360-params.agentDir,
361-params.config,
362-);
363-const waiter = createCodexNativeCompactionWaiter(client, binding.threadId);
364-let completion: CodexNativeCompactionCompletion;
365-try {
366-await client.request("thread/compact/start", {
367-threadId: binding.threadId,
368-});
369-embeddedAgentLog.info("started codex app-server compaction", {
370-sessionId: params.sessionId,
371-threadId: binding.threadId,
372-});
373-waiter.startTimeout();
374-completion = await waiter.promise;
375-} catch (error) {
376-waiter.cancel();
377-if (isCodexThreadNotFoundError(error)) {
378-await clearCodexAppServerBinding(params.sessionFile, { config: params.config });
379-return failedCodexThreadBindingCompactionResult(params, {
364+let completion: CodexNativeCompactionCompletion | undefined;
365+let attempt = 0;
366+for (attempt = 1; attempt <= MAX_CODEX_NATIVE_COMPACTION_ATTEMPTS; attempt += 1) {
367+const client = await clientFactory(
368+appServer.start,
369+requestedAuthProfileId ?? binding.authProfileId,
370+params.agentDir,
371+params.config,
372+);
373+const waiter = createCodexNativeCompactionWaiter(client, binding.threadId);
374+try {
375+await client.request("thread/compact/start", {
380376threadId: binding.threadId,
381-reason: formatCompactionError(error),
382-recovery: "stale_thread_binding",
383377});
378+embeddedAgentLog.info("started codex app-server compaction", {
379+sessionId: params.sessionId,
380+threadId: binding.threadId,
381+ attempt,
382+});
383+waiter.startTimeout();
384+completion = await waiter.promise;
385+break;
386+} catch (error) {
387+waiter.cancel();
388+if (isCodexThreadNotFoundError(error)) {
389+await clearCodexAppServerBinding(params.sessionFile, { config: params.config });
390+return failedCodexThreadBindingCompactionResult(params, {
391+threadId: binding.threadId,
392+reason: formatCompactionError(error),
393+recovery: "stale_thread_binding",
394+});
395+}
396+if (
397+isCodexNativeCompactionTimeoutError(error, binding.threadId) &&
398+attempt < MAX_CODEX_NATIVE_COMPACTION_ATTEMPTS
399+) {
400+restartCodexAppServerAfterNativeCompactionTimeout(
401+client,
402+params,
403+binding.threadId,
404+attempt,
405+);
406+continue;
407+}
408+if (isCodexNativeCompactionTimeoutError(error, binding.threadId)) {
409+restartCodexAppServerAfterNativeCompactionTimeout(
410+client,
411+params,
412+binding.threadId,
413+attempt,
414+);
415+}
416+return {
417+ok: false,
418+compacted: false,
419+reason: formatCompactionError(error),
420+};
384421}
422+}
423+if (!completion) {
385424return {
386425ok: false,
387426compacted: false,
388-reason: formatCompactionError(error),
427+reason: `codex app-server compaction did not complete for ${binding.threadId}`,
389428};
390429}
391430embeddedAgentLog.info("completed codex app-server compaction", {
@@ -410,6 +449,10 @@ async function compactCodexNativeThread(
410449if (completion.tokensAfter !== undefined) {
411450resultDetails.tokenUsageSource = "thread/tokenUsage/updated";
412451}
452+if (attempt > 1) {
453+resultDetails.compactionAttempts = attempt;
454+resultDetails.recoveredAfterAppServerRestart = true;
455+}
413456return {
414457ok: true,
415458compacted: true,
@@ -453,6 +496,26 @@ function isCodexThreadNotFoundError(error: unknown): boolean {
453496return formatCompactionError(error).toLowerCase().includes("thread not found");
454497}
455498499+function isCodexNativeCompactionTimeoutError(error: unknown, threadId: string): boolean {
500+return error instanceof CodexNativeCompactionTimeoutError && error.threadId === threadId;
501+}
502+503+function restartCodexAppServerAfterNativeCompactionTimeout(
504+client: CodexAppServerClient,
505+params: CompactEmbeddedPiSessionParams,
506+threadId: string,
507+attempt: number,
508+): void {
509+embeddedAgentLog.warn("codex app-server compaction timed out; restarting app-server", {
510+sessionId: params.sessionId,
511+sessionKey: params.sessionKey,
512+ threadId,
513+ attempt,
514+maxAttempts: MAX_CODEX_NATIVE_COMPACTION_ATTEMPTS,
515+});
516+client.close();
517+}
518+456519function createCodexNativeCompactionWaiter(
457520client: CodexAppServerClient,
458521threadId: string,
@@ -544,7 +607,7 @@ function createCodexNativeCompactionWaiter(
544607return;
545608}
546609timeout = setTimeout(() => {
547-failWaiter(new Error(`timed out waiting for codex app-server compaction for ${threadId}`));
610+failWaiter(new CodexNativeCompactionTimeoutError(threadId));
548611}, resolveCompactionWaitTimeoutMs());
549612timeout.unref?.();
550613},
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。