mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 17:30:31 +00:00
Track MCP elicitations in panel reuse guards
This commit is contained in:
parent
fb8f4eee91
commit
4b9575d6fb
4 changed files with 32 additions and 1 deletions
|
|
@ -9,6 +9,7 @@ export interface ChatPanelSnapshot {
|
|||
turnLifecycle: OpenCodexPanelTurnLifecycle;
|
||||
pendingApprovals: number;
|
||||
pendingUserInputs: number;
|
||||
pendingMcpElicitations: number;
|
||||
hasComposerDraft: boolean;
|
||||
connected: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -375,6 +375,7 @@ function isIdleEmptyPanelSnapshot(snapshot: ChatPanelSnapshot): boolean {
|
|||
snapshot.turnLifecycle.kind === "idle" &&
|
||||
snapshot.pendingApprovals === 0 &&
|
||||
snapshot.pendingUserInputs === 0 &&
|
||||
snapshot.pendingMcpElicitations === 0 &&
|
||||
!snapshot.hasComposerDraft
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ function openPanelSnapshot(
|
|||
turnLifecycle: OpenCodexPanelSnapshot["turnLifecycle"];
|
||||
pendingApprovals: number;
|
||||
pendingUserInputs: number;
|
||||
pendingMcpElicitations: number;
|
||||
hasComposerDraft: boolean;
|
||||
connected: boolean;
|
||||
lastFocused: boolean;
|
||||
|
|
@ -34,6 +35,7 @@ function openPanelSnapshot(
|
|||
turnLifecycle: { kind: "idle" },
|
||||
pendingApprovals: 0,
|
||||
pendingUserInputs: 0,
|
||||
pendingMcpElicitations: 0,
|
||||
hasComposerDraft: false,
|
||||
connected: true,
|
||||
...overrides,
|
||||
|
|
@ -91,10 +93,11 @@ describe("threads view renderer decisions", () => {
|
|||
openPanelSnapshot({ viewId: "running", threadId: "thread", turnLifecycle: { kind: "running", turnId: "turn" } }),
|
||||
openPanelSnapshot({ viewId: "approval", threadId: "thread", pendingApprovals: 1 }),
|
||||
openPanelSnapshot({ viewId: "input", threadId: "thread", pendingUserInputs: 1 }),
|
||||
openPanelSnapshot({ viewId: "mcp", threadId: "thread", pendingMcpElicitations: 1 }),
|
||||
],
|
||||
new Map(),
|
||||
)[0]?.live,
|
||||
).toMatchObject({ status: "needs-input", label: "Needs input", viewId: "input", openPanels: 4 });
|
||||
).toMatchObject({ status: "needs-input", label: "Needs input", viewId: "input", openPanels: 5 });
|
||||
|
||||
expect(
|
||||
threadRows(
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ describe("CodexPanelPlugin boot restored panel loading", () => {
|
|||
turnLifecycle: { kind: "idle" },
|
||||
pendingApprovals: 0,
|
||||
pendingUserInputs: 0,
|
||||
pendingMcpElicitations: 0,
|
||||
hasComposerDraft: false,
|
||||
connected: true,
|
||||
});
|
||||
|
|
@ -140,6 +141,7 @@ describe("CodexPanelPlugin boot restored panel loading", () => {
|
|||
turnLifecycle: { kind: "idle" },
|
||||
pendingApprovals: 0,
|
||||
pendingUserInputs: 0,
|
||||
pendingMcpElicitations: 0,
|
||||
hasComposerDraft: false,
|
||||
connected: true,
|
||||
});
|
||||
|
|
@ -152,6 +154,7 @@ describe("CodexPanelPlugin boot restored panel loading", () => {
|
|||
turnLifecycle: { kind: "idle" },
|
||||
pendingApprovals: 0,
|
||||
pendingUserInputs: 0,
|
||||
pendingMcpElicitations: 0,
|
||||
hasComposerDraft: false,
|
||||
connected: true,
|
||||
});
|
||||
|
|
@ -163,6 +166,28 @@ describe("CodexPanelPlugin boot restored panel loading", () => {
|
|||
expect(openEmptyThread).toHaveBeenCalledWith("thread-1");
|
||||
});
|
||||
|
||||
it("does not reuse an idle panel with a pending MCP elicitation as empty", async () => {
|
||||
const { CodexChatView } = await import("../src/features/chat/host/view");
|
||||
const pendingLeaf = leaf();
|
||||
pendingLeaf.view = chatView(CodexChatView, pendingLeaf);
|
||||
const pendingView = pendingLeaf.view as CodexChatView;
|
||||
vi.spyOn(pendingView.surface, "openPanelSnapshot").mockReturnValue(
|
||||
panelSnapshot({ viewId: "pending-view", threadId: null, pendingMcpElicitations: 1 }),
|
||||
);
|
||||
const openPendingThread = vi.spyOn(pendingView.surface, "openThread").mockResolvedValue(undefined);
|
||||
const emptyLeaf = leaf();
|
||||
emptyLeaf.view = chatView(CodexChatView, emptyLeaf);
|
||||
const emptyView = emptyLeaf.view as CodexChatView;
|
||||
vi.spyOn(emptyView.surface, "openPanelSnapshot").mockReturnValue(panelSnapshot({ viewId: "empty-view", threadId: null }));
|
||||
const openEmptyThread = vi.spyOn(emptyView.surface, "openThread").mockResolvedValue(undefined);
|
||||
const plugin = await pluginWithLeaves([pendingLeaf, emptyLeaf]);
|
||||
|
||||
await panels(plugin).openThreadInAvailableView("thread-1");
|
||||
|
||||
expect(openPendingThread).not.toHaveBeenCalled();
|
||||
expect(openEmptyThread).toHaveBeenCalledWith("thread-1");
|
||||
});
|
||||
|
||||
it("opens picker Enter selections in the most recent panel when the thread is not already open", async () => {
|
||||
const { CodexChatView } = await import("../src/features/chat/host/view");
|
||||
const olderLeaf = leaf();
|
||||
|
|
@ -740,6 +765,7 @@ function panelSnapshot(
|
|||
turnLifecycle: { kind: "idle" },
|
||||
pendingApprovals: 0,
|
||||
pendingUserInputs: 0,
|
||||
pendingMcpElicitations: 0,
|
||||
hasComposerDraft: false,
|
||||
connected: true,
|
||||
...overrides,
|
||||
|
|
|
|||
Loading…
Reference in a new issue