Fix Codex replacement turn idle flash

This commit is contained in:
Mohamed Boudra
2026-04-22 18:54:20 +07:00
parent 407b4b68b0
commit 89efb007f9
4 changed files with 337 additions and 1 deletions
@@ -13,10 +13,17 @@ const turnFailedEvent: AgentStreamEventPayload = {
error: "failed",
};
const turnCanceledEvent: AgentStreamEventPayload = {
type: "turn_canceled",
provider: "codex",
reason: "interrupted",
};
describe("session stream lifecycle helpers", () => {
it("derives optimistic terminal lifecycle only when current status is running", () => {
expect(deriveOptimisticLifecycleStatus("running", turnCompletedEvent)).toBe("idle");
expect(deriveOptimisticLifecycleStatus("running", turnFailedEvent)).toBe("error");
expect(deriveOptimisticLifecycleStatus("running", turnCanceledEvent)).toBe(null);
expect(deriveOptimisticLifecycleStatus("initializing", turnCompletedEvent)).toBe(null);
expect(deriveOptimisticLifecycleStatus("idle", turnFailedEvent)).toBe(null);
});
@@ -10,10 +10,13 @@ export function deriveOptimisticLifecycleStatus(
}
switch (event.type) {
case "turn_completed":
case "turn_canceled":
return "idle";
case "turn_failed":
return "error";
case "turn_canceled":
// A canceled turn can be either a final user cancel or an interrupt before
// a replacement turn starts. The daemon snapshot is authoritative here.
return null;
default:
return null;
}
@@ -602,6 +602,28 @@ describe("processAgentStreamEvent", () => {
expect(result.agent!.status).toBe("error");
});
it("does not derive optimistic idle status on turn_canceled for running agent", () => {
const turnCanceledEvent: AgentStreamEventPayload = {
type: "turn_canceled",
provider: "codex",
reason: "interrupted",
};
const result = processAgentStreamEvent({
...baseStreamInput,
event: turnCanceledEvent,
currentAgent: {
status: "running",
updatedAt: new Date(1000),
lastActivityAt: new Date(1000),
},
timestamp: new Date(2000),
});
expect(result.agentChanged).toBe(false);
expect(result.agent).toBe(null);
});
it("does not change agent when status is not running", () => {
const turnCompletedEvent: AgentStreamEventPayload = {
type: "turn_completed",