diff --git a/extensions/copilot/src/event-bridge.test.ts b/extensions/copilot/src/event-bridge.test.ts index e7a5fe012032..9aa652a2ac3d 100644 --- a/extensions/copilot/src/event-bridge.test.ts +++ b/extensions/copilot/src/event-bridge.test.ts @@ -662,6 +662,33 @@ describe("attachEventBridge", () => { expect(bridge.isCompacting()).toBe(false); }); + it("ignores subagent compaction events when tracking the root session", async () => { + const session = createFakeSession(); + const onCompactionStart = vi.fn(); + const onCompactionComplete = vi.fn(); + const bridge = attachEventBridge(session, { + getSdkSessionId: () => "sdk-session-id", + isAborted: () => false, + onCompactionStart, + onCompactionComplete, + }); + + session.emit("session.compaction_start", { + ...makeEvent("session.compaction_start", {}), + agentId: "subagent-1", + }); + session.emit("session.compaction_complete", { + ...makeEvent("session.compaction_complete", { success: true }), + agentId: "subagent-1", + }); + await bridge.awaitCompactionCompletion(); + + expect(bridge.hasObservedCompaction()).toBe(false); + expect(bridge.isCompacting()).toBe(false); + expect(onCompactionStart).not.toHaveBeenCalled(); + expect(onCompactionComplete).not.toHaveBeenCalled(); + }); + it("session.error populates streamError with errorCode or errorType only when not aborted", () => { const activeSession = createFakeSession(); const activeBridge = attachEventBridge(activeSession, { diff --git a/extensions/copilot/src/event-bridge.ts b/extensions/copilot/src/event-bridge.ts index d520ffa22238..bf5cf12cb60e 100644 --- a/extensions/copilot/src/event-bridge.ts +++ b/extensions/copilot/src/event-bridge.ts @@ -183,7 +183,10 @@ export function attachEventBridge( } }); - registerListener(session, unsubscribeFns, "session.compaction_start", () => { + registerListener(session, unsubscribeFns, "session.compaction_start", (event) => { + if (!isRootCompactionEvent(event)) { + return; + } observedCompaction = true; if (activeCompactionCount === 0) { compactionIdle = new Promise((resolve) => { @@ -195,6 +198,9 @@ export function attachEventBridge( }); registerListener(session, unsubscribeFns, "session.compaction_complete", (event) => { + if (!isRootCompactionEvent(event)) { + return; + } activeCompactionCount = Math.max(0, activeCompactionCount - 1); enqueueCompactionCallback(() => options.onCompactionComplete?.({ @@ -410,6 +416,12 @@ function isAssistantMessageEvent( return event?.type === "assistant.message"; } +function isRootCompactionEvent(event: { agentId?: string }): boolean { + // SDK session events include subagent compaction; only root compaction + // affects the pooled root session's cleanup and reuse lifecycle. + return event.agentId === undefined; +} + function joinReasoning(order: string[], reasoningById: Map): string { return order.map((reasoningId) => reasoningById.get(reasoningId) ?? "").join(""); }