Skip to content

Commit 0e768ff

Browse files
committed
fix(codex): await app-server elicitation bridge
1 parent ed91eb1 commit 0e768ff

2 files changed

Lines changed: 80 additions & 1 deletion

File tree

extensions/codex/src/app-server/run-attempt.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3276,6 +3276,85 @@ describe("runCodexAppServerAttempt", () => {
32763276
expect(result.promptError).toBeNull();
32773277
});
32783278

3279+
it("keeps turn request activity active until elicitation handling resolves", async () => {
3280+
const harness = createStartedThreadHarness();
3281+
const bridgedResponse = {
3282+
action: "accept",
3283+
content: null,
3284+
_meta: null,
3285+
} as const;
3286+
let resolveBridge!: (value: typeof bridgedResponse) => void;
3287+
const bridgePromise = new Promise<typeof bridgedResponse>((resolve) => {
3288+
resolveBridge = resolve;
3289+
});
3290+
vi.spyOn(elicitationBridge, "handleCodexAppServerElicitationRequest").mockImplementation(
3291+
async () => await bridgePromise,
3292+
);
3293+
const params = createParams(
3294+
path.join(tempDir, "session.jsonl"),
3295+
path.join(tempDir, "workspace"),
3296+
);
3297+
params.timeoutMs = 500;
3298+
const onRunProgress = vi.fn();
3299+
params.onRunProgress = onRunProgress;
3300+
3301+
const run = runCodexAppServerAttempt(params, {
3302+
turnCompletionIdleTimeoutMs: 1_000,
3303+
turnAssistantCompletionIdleTimeoutMs: 1_000,
3304+
turnTerminalIdleTimeoutMs: 1_000,
3305+
});
3306+
await harness.waitForMethod("turn/start");
3307+
3308+
const response = harness.handleServerRequest({
3309+
id: "request-pending-elicitation",
3310+
method: "mcpServer/elicitation/request",
3311+
params: {
3312+
threadId: "thread-1",
3313+
turnId: "turn-1",
3314+
mode: "form",
3315+
message: "Approve?",
3316+
requestedSchema: { type: "object", properties: {} },
3317+
serverName: "server-1",
3318+
_meta: null,
3319+
},
3320+
});
3321+
await vi.waitFor(
3322+
() =>
3323+
expect(onRunProgress).toHaveBeenCalledWith(
3324+
expect.objectContaining({
3325+
reason: "request:mcpServer/elicitation/request:start",
3326+
}),
3327+
),
3328+
fastWait,
3329+
);
3330+
await new Promise((resolve) => setTimeout(resolve, 60));
3331+
expect(
3332+
onRunProgress.mock.calls.some(
3333+
([event]) =>
3334+
(event as { reason?: string }).reason ===
3335+
"request:mcpServer/elicitation/request:response",
3336+
),
3337+
).toBe(false);
3338+
3339+
resolveBridge(bridgedResponse);
3340+
await expect(response).resolves.toEqual(bridgedResponse);
3341+
await vi.waitFor(
3342+
() =>
3343+
expect(onRunProgress).toHaveBeenCalledWith(
3344+
expect.objectContaining({
3345+
reason: "request:mcpServer/elicitation/request:response",
3346+
}),
3347+
),
3348+
fastWait,
3349+
);
3350+
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
3351+
3352+
const result = await run;
3353+
expect(result.aborted).toBe(false);
3354+
expect(result.timedOut).toBe(false);
3355+
expect(result.promptError).toBeNull();
3356+
});
3357+
32793358
it("counts pending user input requests as turn attempt progress", async () => {
32803359
const harness = createStartedThreadHarness();
32813360
const params = createParams(

extensions/codex/src/app-server/run-attempt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2059,7 +2059,7 @@ export async function runCodexAppServerAttempt(
20592059
armCompletionWatchOnResponse = true;
20602060
markCurrentTurnRequestProgress();
20612061
}
2062-
return handleCodexAppServerElicitationRequest({
2062+
return await handleCodexAppServerElicitationRequest({
20632063
requestParams: request.params,
20642064
paramsForRun: params,
20652065
threadId: thread.threadId,

0 commit comments

Comments
 (0)