Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/CodexAcpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,13 +600,15 @@ export class CodexAcpClient {
const additionalDirectories = readAdditionalDirectories(request.cwd, request.additionalDirectories, request._meta);
await this.refreshSkills(request.cwd, additionalDirectories);

const response = await this.codexClient.threadStart({
config: await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers),
modelProvider: this.getModelProvider(),
cwd: request.cwd,
});

const codexModels = await this.fetchAvailableModels();
const config = await this.createSessionConfig(request.cwd, additionalDirectories, request.mcpServers);
const [response, codexModels] = await Promise.all([
this.codexClient.threadStart({
config,
modelProvider: this.getModelProvider(),
cwd: request.cwd,
}),
this.fetchAvailableModels(),
]);
if (codexModels.length === 0) {
throw new Error("Codex did not return any models");
}
Expand Down
31 changes: 30 additions & 1 deletion src/__tests__/CodexACPAgent/CodexAcpClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ describe('ACP server test', { timeout: 40_000 }, () => {
"account/read",
"thread/start",
"model/list",
"thread/started",
"account/read",
"thread/started",
"skills/list",
]);
expect(loginRequest).toEqual({
Expand Down Expand Up @@ -475,6 +475,35 @@ describe('ACP server test', { timeout: 40_000 }, () => {
expect(listSkillsSpy.mock.invocationCallOrder[0]!).toBeLessThan(threadStartSpy.mock.invocationCallOrder[0]!);
});

it('fetches models while a new thread is starting', async () => {
const mockFixture = createCodexMockTestFixture();
const codexAcpClient = mockFixture.getCodexAcpClient();
const codexAppServerClient = mockFixture.getCodexAppServerClient();
const threadStart = deferred<Awaited<ReturnType<typeof codexAppServerClient.threadStart>>>();

vi.spyOn(codexAppServerClient, "listSkills").mockResolvedValue({data: []});
vi.spyOn(codexAppServerClient, "threadStart").mockReturnValue(threadStart.promise);
const listModelsSpy = vi.spyOn(codexAppServerClient, "listModels").mockResolvedValue({
data: [createTestModel({id: "gpt-5"})],
nextCursor: null,
});

const session = codexAcpClient.newSession({cwd: "/workspace", mcpServers: []});
await vi.waitFor(() => expect(listModelsSpy).toHaveBeenCalledOnce());

threadStart.resolve({
thread: {id: "thread-id"} as any,
model: "gpt-5",
reasoningEffort: "medium",
serviceTier: null,
} as Awaited<ReturnType<typeof codexAppServerClient.threadStart>>);

await expect(session).resolves.toMatchObject({
sessionId: "thread-id",
currentModelId: "gpt-5[medium]",
});
});

it('prefers ACP additional directories over legacy meta roots for new session skill discovery', async () => {
const mockFixture = createCodexMockTestFixture();
const codexAcpClient = mockFixture.getCodexAcpClient();
Expand Down