agent-ecosystem/src/main/services/infrastructure/codexAppServer/CodexAppServerSessionFactory.ts

135 lines
4 KiB
TypeScript

import type { JsonRpcSession, JsonRpcStdioClient } from './JsonRpcStdioClient';
import type { CodexAppServerInitializeResponse } from './protocol';
const DEFAULT_INITIALIZE_TIMEOUT_MS = 6_000;
const DEFAULT_REQUEST_TIMEOUT_MS = 3_000;
const DEFAULT_TOTAL_TIMEOUT_MS = 8_000;
export const DEFAULT_CODEX_APP_SERVER_SUPPRESSED_NOTIFICATION_METHODS = [
'thread/started',
'thread/status/changed',
'thread/archived',
'thread/unarchived',
'thread/closed',
'thread/name/updated',
'turn/started',
'turn/completed',
'item/agentMessage/delta',
'item/agentReasoning/delta',
'item/execCommandOutputDelta',
];
export interface CodexAppServerSession extends JsonRpcSession {
readonly initializeResponse: CodexAppServerInitializeResponse;
}
export class CodexAppServerSessionFactory {
constructor(private readonly rpcClient: JsonRpcStdioClient) {}
async withSession<T>(
options: {
binaryPath: string;
env?: NodeJS.ProcessEnv;
requestTimeoutMs?: number;
initializeTimeoutMs?: number;
totalTimeoutMs?: number;
label: string;
experimentalApi?: boolean;
optOutNotificationMethods?: string[];
},
handler: (session: CodexAppServerSession) => Promise<T>
): Promise<T> {
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
const initializeTimeoutMs = Math.max(
options.initializeTimeoutMs ?? DEFAULT_INITIALIZE_TIMEOUT_MS,
requestTimeoutMs
);
return this.rpcClient.withSession(
{
binaryPath: options.binaryPath,
args: ['app-server'],
env: options.env,
requestTimeoutMs,
totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,
label: options.label,
},
async (session) => {
const initializedSession = await this.initializeSession(session, {
initializeTimeoutMs,
experimentalApi: options.experimentalApi ?? false,
optOutNotificationMethods:
options.optOutNotificationMethods ??
DEFAULT_CODEX_APP_SERVER_SUPPRESSED_NOTIFICATION_METHODS,
});
return handler(initializedSession);
}
);
}
async openSession(options: {
binaryPath: string;
env?: NodeJS.ProcessEnv;
requestTimeoutMs?: number;
initializeTimeoutMs?: number;
experimentalApi?: boolean;
optOutNotificationMethods?: string[];
}): Promise<CodexAppServerSession> {
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
const initializeTimeoutMs = Math.max(
options.initializeTimeoutMs ?? DEFAULT_INITIALIZE_TIMEOUT_MS,
requestTimeoutMs
);
const session = await this.rpcClient.openSession({
binaryPath: options.binaryPath,
args: ['app-server'],
env: options.env,
requestTimeoutMs,
});
try {
return await this.initializeSession(session, {
initializeTimeoutMs,
experimentalApi: options.experimentalApi ?? false,
optOutNotificationMethods:
options.optOutNotificationMethods ??
DEFAULT_CODEX_APP_SERVER_SUPPRESSED_NOTIFICATION_METHODS,
});
} catch (error) {
await session.close().catch(() => undefined);
throw error;
}
}
private async initializeSession(
session: JsonRpcSession,
options: {
initializeTimeoutMs: number;
experimentalApi: boolean;
optOutNotificationMethods: string[];
}
): Promise<CodexAppServerSession> {
const initializeResponse = await session.request<CodexAppServerInitializeResponse>(
'initialize',
{
clientInfo: {
name: 'claude-agent-teams-ui',
title: 'Agent Teams UI',
version: '0.1.0',
},
capabilities: {
experimentalApi: options.experimentalApi,
optOutNotificationMethods: options.optOutNotificationMethods,
},
},
options.initializeTimeoutMs
);
await session.notify('initialized');
return {
...session,
initializeResponse,
};
}
}