Skip to content

Commit 4cbf616

Browse files
committed
fix(codex): premark terminal app-server turns
1 parent c65801c commit 4cbf616

2 files changed

Lines changed: 63 additions & 11 deletions

File tree

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4849,6 +4849,46 @@ describe("runCodexAppServerAttempt", () => {
48494849
expect(result.timedOut).toBe(false);
48504850
});
48514851

4852+
it("does not fail when a buffered terminal notification is followed by client close", async () => {
4853+
let harness: ReturnType<typeof createAppServerHarness>;
4854+
let resolveBufferedTerminal!: () => void;
4855+
const bufferedTerminal = new Promise<void>((resolve) => {
4856+
resolveBufferedTerminal = resolve;
4857+
});
4858+
harness = createAppServerHarness(async (method) => {
4859+
if (method === "thread/start") {
4860+
return threadStartResult();
4861+
}
4862+
if (method === "turn/start") {
4863+
await harness.notify({
4864+
method: "item/started",
4865+
params: {
4866+
threadId: "thread-1",
4867+
turnId: "turn-1",
4868+
item: { id: "tool-1", type: "commandExecution" },
4869+
},
4870+
});
4871+
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
4872+
resolveBufferedTerminal();
4873+
return turnStartResult("turn-1", "inProgress");
4874+
}
4875+
return {};
4876+
});
4877+
4878+
const run = runCodexAppServerAttempt(
4879+
createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
4880+
{ turnTerminalIdleTimeoutMs: 60_000 },
4881+
);
4882+
await bufferedTerminal;
4883+
await new Promise<void>((resolve) => setImmediate(resolve));
4884+
harness.close();
4885+
4886+
const result = await run;
4887+
expect(result.promptError ?? undefined).toBeUndefined();
4888+
expect(result.aborted).toBe(false);
4889+
expect(result.timedOut).toBe(false);
4890+
});
4891+
48524892
it("does not time out when turn progress arrives before turn/start returns", async () => {
48534893
let harness: ReturnType<typeof createAppServerHarness>;
48544894
harness = createAppServerHarness(async (method) => {

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

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,19 @@ export async function runCodexAppServerAttempt(
14641464
});
14651465
};
14661466

1467+
const isTerminalTurnNotificationForTurn = (
1468+
notification: CodexServerNotification,
1469+
notificationTurnId: string,
1470+
): boolean => {
1471+
if (!isTurnNotification(notification.params, thread.threadId, notificationTurnId)) {
1472+
return false;
1473+
}
1474+
return (
1475+
notification.method === "turn/completed" ||
1476+
isCodexTurnAbortMarkerNotification(notification, { currentPromptText: promptBuild.prompt })
1477+
);
1478+
};
1479+
14671480
const handleNotification = async (notification: CodexServerNotification) => {
14681481
userInputBridge?.handleNotification(notification);
14691482
if (!projector || !turnId) {
@@ -1562,7 +1575,7 @@ export async function runCodexAppServerAttempt(
15621575
const isTurnAbortMarker =
15631576
isCurrentTurnNotification &&
15641577
isCodexTurnAbortMarkerNotification(notification, { currentPromptText: promptBuild.prompt });
1565-
const isTurnTerminal = isTurnCompletion || isTurnAbortMarker;
1578+
const isTurnTerminal = isTerminalTurnNotificationForTurn(notification, turnId);
15661579
if (isTurnTerminal) {
15671580
terminalTurnNotificationQueued = true;
15681581
}
@@ -1596,16 +1609,7 @@ export async function runCodexAppServerAttempt(
15961609
pendingNotifications.push(notification);
15971610
return Promise.resolve();
15981611
}
1599-
const isCurrentTurnNotification = isTurnNotification(
1600-
notification.params,
1601-
thread.threadId,
1602-
turnId,
1603-
);
1604-
if (
1605-
isCurrentTurnNotification &&
1606-
(notification.method === "turn/completed" ||
1607-
isCodexTurnAbortMarkerNotification(notification, { currentPromptText: promptBuild.prompt }))
1608-
) {
1612+
if (isTerminalTurnNotificationForTurn(notification, turnId)) {
16091613
terminalTurnNotificationQueued = true;
16101614
}
16111615
notificationQueue = notificationQueue.then(
@@ -2034,6 +2038,14 @@ export async function runCodexAppServerAttempt(
20342038
nativePostToolUseRelayEnabled:
20352039
nativeHookRelay?.allowedEvents.includes("post_tool_use") === true,
20362040
});
2041+
if (
2042+
isTerminalTurnStatus(turn.turn.status) ||
2043+
pendingNotifications.some((notification) =>
2044+
isTerminalTurnNotificationForTurn(notification, activeTurnId),
2045+
)
2046+
) {
2047+
terminalTurnNotificationQueued = true;
2048+
}
20372049
closeCleanup = (
20382050
client as {
20392051
addCloseHandler?: (handler: (client: CodexAppServerClient) => void) => () => void;

0 commit comments

Comments
 (0)