fix: harden codex recent project time budgets

This commit is contained in:
777genius 2026-04-14 20:36:58 +03:00
parent db7c4fe160
commit 1932ddcbe2
3 changed files with 44 additions and 4 deletions

View file

@ -15,7 +15,10 @@ import type { ServiceContext } from '@main/services';
const CODEX_THREAD_LIMIT = 40;
const CODEX_LIVE_FETCH_TIMEOUT_MS = 4_500;
const CODEX_ARCHIVED_FETCH_TIMEOUT_MS = 2_500;
const CODEX_SOURCE_TIMEOUT_MS = 5_200;
const CODEX_SESSION_OVERHEAD_TIMEOUT_MS = 1_500;
const CODEX_TOTAL_FETCH_TIMEOUT_MS =
CODEX_LIVE_FETCH_TIMEOUT_MS + CODEX_SESSION_OVERHEAD_TIMEOUT_MS;
const CODEX_SOURCE_TIMEOUT_MS = CODEX_TOTAL_FETCH_TIMEOUT_MS + 500;
function isInteractiveSource(source: unknown): boolean {
return source === 'vscode' || source === 'cli';
@ -83,7 +86,7 @@ export class CodexRecentProjectsSourceAdapter implements RecentProjectsSourcePor
limit: CODEX_THREAD_LIMIT,
liveRequestTimeoutMs: CODEX_LIVE_FETCH_TIMEOUT_MS,
archivedRequestTimeoutMs: CODEX_ARCHIVED_FETCH_TIMEOUT_MS,
totalTimeoutMs: CODEX_LIVE_FETCH_TIMEOUT_MS,
totalTimeoutMs: CODEX_TOTAL_FETCH_TIMEOUT_MS,
});
this.deps.logger.info('codex recent-projects thread lists loaded', {

View file

@ -2,6 +2,7 @@ import type { JsonRpcStdioClient } from './JsonRpcStdioClient';
const DEFAULT_REQUEST_TIMEOUT_MS = 3_000;
const DEFAULT_TOTAL_TIMEOUT_MS = 8_000;
const MIN_SESSION_OVERHEAD_TIMEOUT_MS = 1_500;
const SUPPRESSED_NOTIFICATION_METHODS = [
'thread/started',
'thread/status/changed',
@ -63,7 +64,10 @@ export class CodexAppServerClient {
const liveRequestTimeoutMs = options.liveRequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
const archivedRequestTimeoutMs = options.archivedRequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
const sessionRequestTimeoutMs = Math.max(liveRequestTimeoutMs, archivedRequestTimeoutMs);
const totalTimeoutMs = options.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS;
const totalTimeoutMs = Math.max(
options.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
sessionRequestTimeoutMs + MIN_SESSION_OVERHEAD_TIMEOUT_MS
);
return this.rpcClient.withSession(
{

View file

@ -53,7 +53,7 @@ describe('CodexAppServerClient', () => {
expect.objectContaining({
binaryPath: '/usr/local/bin/codex',
requestTimeoutMs: 4500,
totalTimeoutMs: 4500,
totalTimeoutMs: 6000,
}),
expect.any(Function)
);
@ -107,4 +107,37 @@ describe('CodexAppServerClient', () => {
error: 'JSON-RPC request timed out: thread/list',
});
});
it('raises the session timeout budget above the longest request timeout', async () => {
const session = createSession(
vi.fn().mockImplementation((method: string, params?: { archived?: boolean }) => {
if (method === 'initialize') {
return Promise.resolve({});
}
if (method === 'thread/list') {
return Promise.resolve({ data: [] });
}
return Promise.reject(new Error(`Unexpected method: ${method}`));
})
);
const withSession = vi.fn().mockImplementation((_options, handler) => handler(session));
const client = new CodexAppServerClient({ withSession } as unknown as JsonRpcStdioClient);
await client.listRecentThreads('/usr/local/bin/codex', {
limit: 40,
liveRequestTimeoutMs: 4500,
archivedRequestTimeoutMs: 2500,
totalTimeoutMs: 4500,
});
expect(withSession).toHaveBeenCalledWith(
expect.objectContaining({
totalTimeoutMs: 6000,
}),
expect.any(Function)
);
});
});