Fix duplicate Codex plan approval panels
This commit is contained in:
@@ -770,6 +770,14 @@ describe("Codex app-server provider", () => {
|
||||
turn: { status: "completed", error: null },
|
||||
});
|
||||
|
||||
expect(
|
||||
events.some(
|
||||
(event) =>
|
||||
event.type === "timeline" &&
|
||||
event.item.type === "tool_call" &&
|
||||
event.item.detail.type === "plan",
|
||||
),
|
||||
).toBe(false);
|
||||
expect(events.at(-2)).toEqual({
|
||||
type: "permission_requested",
|
||||
provider: "codex",
|
||||
@@ -804,6 +812,51 @@ describe("Codex app-server provider", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("does not emit Codex plan thread items as timeline cards while plan approval is pending", () => {
|
||||
const session = createSession({
|
||||
featureValues: { plan_mode: true, fast_mode: true },
|
||||
});
|
||||
const events: AgentStreamEvent[] = [];
|
||||
session.subscribe((event) => events.push(event));
|
||||
|
||||
asInternals(session).handleNotification("turn/started", {
|
||||
turn: { id: "turn-plan-thread-item" },
|
||||
});
|
||||
asInternals(session).handleNotification("item/completed", {
|
||||
item: {
|
||||
id: "plan-item-1",
|
||||
type: "plan",
|
||||
text: "- Inspect README\n- Add a short note",
|
||||
},
|
||||
});
|
||||
asInternals(session).handleNotification("turn/completed", {
|
||||
turn: { status: "completed", error: null },
|
||||
});
|
||||
|
||||
expect(events).not.toContainEqual(
|
||||
expect.objectContaining({
|
||||
type: "timeline",
|
||||
item: expect.objectContaining({
|
||||
type: "tool_call",
|
||||
detail: expect.objectContaining({ type: "plan" }),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(events.at(-2)).toEqual({
|
||||
type: "permission_requested",
|
||||
provider: "codex",
|
||||
turnId: "test-turn",
|
||||
request: expect.objectContaining({
|
||||
provider: "codex",
|
||||
name: "CodexPlanApproval",
|
||||
kind: "plan",
|
||||
input: {
|
||||
plan: "- Inspect README\n- Add a short note",
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
test("emits usage_updated on token usage updates and keeps usage on turn completion", () => {
|
||||
const session = createSession();
|
||||
const events: AgentStreamEvent[] = [];
|
||||
|
||||
@@ -3724,6 +3724,12 @@ class CodexAppServerAgentSession implements AgentSession {
|
||||
});
|
||||
if (timelineItem) {
|
||||
this.rememberPlanResult(timelineItem);
|
||||
// In plan mode, the same plan is rendered through the synthetic approval
|
||||
// permission. Keep the remembered text for that card, but do not also
|
||||
// emit a static timeline plan panel.
|
||||
if (this.planModeEnabled) {
|
||||
return;
|
||||
}
|
||||
this.emitEvent({ type: "timeline", provider: CODEX_PROVIDER, item: timelineItem });
|
||||
}
|
||||
}
|
||||
@@ -3883,6 +3889,11 @@ class CodexAppServerAgentSession implements AgentSession {
|
||||
if (timelineItem.type === "tool_call") {
|
||||
if (timelineItem.detail.type === "plan") {
|
||||
this.rememberPlanResult(timelineItem);
|
||||
// Codex can surface plans both as turn/plan updates and as completed
|
||||
// thread items. In plan mode, approval owns the visible plan card.
|
||||
if (this.planModeEnabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.warnOnIncompleteEditToolCall(timelineItem, "item_completed", parsed.item);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user