fix: allow slower codex app-server initialization
This commit is contained in:
parent
af1caf90e8
commit
363fef224d
3 changed files with 53 additions and 8 deletions
|
|
@ -13,14 +13,15 @@ import type { RecentProjectIdentityResolver } from '@features/recent-projects/ma
|
||||||
import type { ServiceContext } from '@main/services';
|
import type { ServiceContext } from '@main/services';
|
||||||
|
|
||||||
const CODEX_THREAD_LIMIT = 40;
|
const CODEX_THREAD_LIMIT = 40;
|
||||||
|
const CODEX_INITIALIZE_TIMEOUT_MS = 6_000;
|
||||||
const CODEX_LIVE_FETCH_TIMEOUT_MS = 4_500;
|
const CODEX_LIVE_FETCH_TIMEOUT_MS = 4_500;
|
||||||
const CODEX_ARCHIVED_FETCH_TIMEOUT_MS = 2_500;
|
const CODEX_ARCHIVED_FETCH_TIMEOUT_MS = 2_500;
|
||||||
const CODEX_SESSION_OVERHEAD_TIMEOUT_MS = 1_500;
|
const CODEX_SESSION_OVERHEAD_TIMEOUT_MS = 1_500;
|
||||||
const CODEX_TOTAL_FETCH_TIMEOUT_MS =
|
const CODEX_TOTAL_FETCH_TIMEOUT_MS =
|
||||||
CODEX_LIVE_FETCH_TIMEOUT_MS + CODEX_ARCHIVED_FETCH_TIMEOUT_MS + CODEX_SESSION_OVERHEAD_TIMEOUT_MS;
|
CODEX_INITIALIZE_TIMEOUT_MS + CODEX_LIVE_FETCH_TIMEOUT_MS + CODEX_SESSION_OVERHEAD_TIMEOUT_MS;
|
||||||
const CODEX_SOURCE_TIMEOUT_MS = CODEX_TOTAL_FETCH_TIMEOUT_MS + 500;
|
const CODEX_SOURCE_TIMEOUT_MS = CODEX_TOTAL_FETCH_TIMEOUT_MS + 500;
|
||||||
const CODEX_LIVE_ONLY_FALLBACK_TOTAL_TIMEOUT_MS =
|
const CODEX_LIVE_ONLY_FALLBACK_TOTAL_TIMEOUT_MS =
|
||||||
CODEX_LIVE_FETCH_TIMEOUT_MS + CODEX_SESSION_OVERHEAD_TIMEOUT_MS + 1_500;
|
CODEX_INITIALIZE_TIMEOUT_MS + CODEX_LIVE_FETCH_TIMEOUT_MS + CODEX_SESSION_OVERHEAD_TIMEOUT_MS;
|
||||||
|
|
||||||
function isInteractiveSource(source: unknown): boolean {
|
function isInteractiveSource(source: unknown): boolean {
|
||||||
return source === 'vscode' || source === 'cli';
|
return source === 'vscode' || source === 'cli';
|
||||||
|
|
@ -88,6 +89,7 @@ export class CodexRecentProjectsSourceAdapter implements RecentProjectsSourcePor
|
||||||
limit: CODEX_THREAD_LIMIT,
|
limit: CODEX_THREAD_LIMIT,
|
||||||
liveRequestTimeoutMs: CODEX_LIVE_FETCH_TIMEOUT_MS,
|
liveRequestTimeoutMs: CODEX_LIVE_FETCH_TIMEOUT_MS,
|
||||||
archivedRequestTimeoutMs: CODEX_ARCHIVED_FETCH_TIMEOUT_MS,
|
archivedRequestTimeoutMs: CODEX_ARCHIVED_FETCH_TIMEOUT_MS,
|
||||||
|
initializeTimeoutMs: CODEX_INITIALIZE_TIMEOUT_MS,
|
||||||
totalTimeoutMs: CODEX_TOTAL_FETCH_TIMEOUT_MS,
|
totalTimeoutMs: CODEX_TOTAL_FETCH_TIMEOUT_MS,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -137,6 +139,7 @@ export class CodexRecentProjectsSourceAdapter implements RecentProjectsSourcePor
|
||||||
const liveFallback = await this.deps.appServerClient.listRecentLiveThreads(binaryPath, {
|
const liveFallback = await this.deps.appServerClient.listRecentLiveThreads(binaryPath, {
|
||||||
limit: CODEX_THREAD_LIMIT,
|
limit: CODEX_THREAD_LIMIT,
|
||||||
requestTimeoutMs: CODEX_LIVE_FETCH_TIMEOUT_MS,
|
requestTimeoutMs: CODEX_LIVE_FETCH_TIMEOUT_MS,
|
||||||
|
initializeTimeoutMs: CODEX_INITIALIZE_TIMEOUT_MS,
|
||||||
totalTimeoutMs: CODEX_LIVE_ONLY_FALLBACK_TOTAL_TIMEOUT_MS,
|
totalTimeoutMs: CODEX_LIVE_ONLY_FALLBACK_TOTAL_TIMEOUT_MS,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import type { JsonRpcSession, JsonRpcStdioClient } from './JsonRpcStdioClient';
|
||||||
|
|
||||||
const DEFAULT_REQUEST_TIMEOUT_MS = 3_000;
|
const DEFAULT_REQUEST_TIMEOUT_MS = 3_000;
|
||||||
const DEFAULT_TOTAL_TIMEOUT_MS = 8_000;
|
const DEFAULT_TOTAL_TIMEOUT_MS = 8_000;
|
||||||
|
const DEFAULT_INITIALIZE_TIMEOUT_MS = 6_000;
|
||||||
const MIN_SESSION_OVERHEAD_TIMEOUT_MS = 1_500;
|
const MIN_SESSION_OVERHEAD_TIMEOUT_MS = 1_500;
|
||||||
const SUPPRESSED_NOTIFICATION_METHODS = [
|
const SUPPRESSED_NOTIFICATION_METHODS = [
|
||||||
'thread/started',
|
'thread/started',
|
||||||
|
|
@ -52,6 +53,7 @@ export interface CodexRecentThreadsResult {
|
||||||
interface ThreadListSessionOptions {
|
interface ThreadListSessionOptions {
|
||||||
binaryPath: string;
|
binaryPath: string;
|
||||||
requestTimeoutMs: number;
|
requestTimeoutMs: number;
|
||||||
|
initializeTimeoutMs: number;
|
||||||
totalTimeoutMs: number;
|
totalTimeoutMs: number;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
@ -64,19 +66,25 @@ export class CodexAppServerClient {
|
||||||
options: {
|
options: {
|
||||||
limit: number;
|
limit: number;
|
||||||
requestTimeoutMs?: number;
|
requestTimeoutMs?: number;
|
||||||
|
initializeTimeoutMs?: number;
|
||||||
totalTimeoutMs?: number;
|
totalTimeoutMs?: number;
|
||||||
}
|
}
|
||||||
): Promise<CodexThreadSegmentResult> {
|
): Promise<CodexThreadSegmentResult> {
|
||||||
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
||||||
|
const initializeTimeoutMs = Math.max(
|
||||||
|
options.initializeTimeoutMs ?? DEFAULT_INITIALIZE_TIMEOUT_MS,
|
||||||
|
requestTimeoutMs
|
||||||
|
);
|
||||||
const totalTimeoutMs = Math.max(
|
const totalTimeoutMs = Math.max(
|
||||||
options.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
|
options.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
|
||||||
requestTimeoutMs + MIN_SESSION_OVERHEAD_TIMEOUT_MS
|
initializeTimeoutMs + requestTimeoutMs + MIN_SESSION_OVERHEAD_TIMEOUT_MS
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.#withThreadListSession(
|
return this.#withThreadListSession(
|
||||||
{
|
{
|
||||||
binaryPath,
|
binaryPath,
|
||||||
requestTimeoutMs,
|
requestTimeoutMs,
|
||||||
|
initializeTimeoutMs,
|
||||||
totalTimeoutMs,
|
totalTimeoutMs,
|
||||||
label: 'codex app-server thread/list live',
|
label: 'codex app-server thread/list live',
|
||||||
},
|
},
|
||||||
|
|
@ -104,21 +112,27 @@ export class CodexAppServerClient {
|
||||||
limit: number;
|
limit: number;
|
||||||
liveRequestTimeoutMs?: number;
|
liveRequestTimeoutMs?: number;
|
||||||
archivedRequestTimeoutMs?: number;
|
archivedRequestTimeoutMs?: number;
|
||||||
|
initializeTimeoutMs?: number;
|
||||||
totalTimeoutMs?: number;
|
totalTimeoutMs?: number;
|
||||||
}
|
}
|
||||||
): Promise<CodexRecentThreadsResult> {
|
): Promise<CodexRecentThreadsResult> {
|
||||||
const liveRequestTimeoutMs = options.liveRequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
const liveRequestTimeoutMs = options.liveRequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
||||||
const archivedRequestTimeoutMs = options.archivedRequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
const archivedRequestTimeoutMs = options.archivedRequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
||||||
const sessionRequestTimeoutMs = Math.max(liveRequestTimeoutMs, archivedRequestTimeoutMs);
|
const sessionRequestTimeoutMs = Math.max(liveRequestTimeoutMs, archivedRequestTimeoutMs);
|
||||||
|
const initializeTimeoutMs = Math.max(
|
||||||
|
options.initializeTimeoutMs ?? DEFAULT_INITIALIZE_TIMEOUT_MS,
|
||||||
|
sessionRequestTimeoutMs
|
||||||
|
);
|
||||||
const totalTimeoutMs = Math.max(
|
const totalTimeoutMs = Math.max(
|
||||||
options.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
|
options.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
|
||||||
liveRequestTimeoutMs + archivedRequestTimeoutMs + MIN_SESSION_OVERHEAD_TIMEOUT_MS
|
initializeTimeoutMs + sessionRequestTimeoutMs + MIN_SESSION_OVERHEAD_TIMEOUT_MS
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.#withThreadListSession(
|
return this.#withThreadListSession(
|
||||||
{
|
{
|
||||||
binaryPath,
|
binaryPath,
|
||||||
requestTimeoutMs: sessionRequestTimeoutMs,
|
requestTimeoutMs: sessionRequestTimeoutMs,
|
||||||
|
initializeTimeoutMs,
|
||||||
totalTimeoutMs,
|
totalTimeoutMs,
|
||||||
label: 'codex app-server thread/list',
|
label: 'codex app-server thread/list',
|
||||||
},
|
},
|
||||||
|
|
@ -193,7 +207,7 @@ export class CodexAppServerClient {
|
||||||
optOutNotificationMethods: SUPPRESSED_NOTIFICATION_METHODS,
|
optOutNotificationMethods: SUPPRESSED_NOTIFICATION_METHODS,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options.requestTimeoutMs
|
options.initializeTimeoutMs
|
||||||
);
|
);
|
||||||
|
|
||||||
await session.notify('initialized');
|
await session.notify('initialized');
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ describe('CodexAppServerClient', () => {
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
binaryPath: '/usr/local/bin/codex',
|
binaryPath: '/usr/local/bin/codex',
|
||||||
requestTimeoutMs: 4500,
|
requestTimeoutMs: 4500,
|
||||||
totalTimeoutMs: 8500,
|
totalTimeoutMs: 12000,
|
||||||
}),
|
}),
|
||||||
expect.any(Function)
|
expect.any(Function)
|
||||||
);
|
);
|
||||||
|
|
@ -135,7 +135,7 @@ describe('CodexAppServerClient', () => {
|
||||||
|
|
||||||
expect(withSession).toHaveBeenCalledWith(
|
expect(withSession).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
totalTimeoutMs: 8500,
|
totalTimeoutMs: 12000,
|
||||||
}),
|
}),
|
||||||
expect.any(Function)
|
expect.any(Function)
|
||||||
);
|
);
|
||||||
|
|
@ -171,7 +171,7 @@ describe('CodexAppServerClient', () => {
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
binaryPath: '/usr/local/bin/codex',
|
binaryPath: '/usr/local/bin/codex',
|
||||||
requestTimeoutMs: 4500,
|
requestTimeoutMs: 4500,
|
||||||
totalTimeoutMs: 6000,
|
totalTimeoutMs: 12000,
|
||||||
label: 'codex app-server thread/list live',
|
label: 'codex app-server thread/list live',
|
||||||
}),
|
}),
|
||||||
expect.any(Function)
|
expect.any(Function)
|
||||||
|
|
@ -180,4 +180,32 @@ describe('CodexAppServerClient', () => {
|
||||||
threads: [{ id: 'live-1', cwd: '/Users/test/live-project', source: 'cli' }],
|
threads: [{ id: 'live-1', cwd: '/Users/test/live-project', source: 'cli' }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses the longer initialize timeout for app-server startup', async () => {
|
||||||
|
const request = vi.fn().mockImplementation((method: string, _params?: unknown, timeoutMs?: number) => {
|
||||||
|
if (method === 'initialize') {
|
||||||
|
expect(timeoutMs).toBe(6000);
|
||||||
|
return Promise.resolve({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === 'thread/list') {
|
||||||
|
return Promise.resolve({ data: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(new Error(`Unexpected method: ${method}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
const session = createSession(request);
|
||||||
|
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(request).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue