Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions nodejs/test/e2e/session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,33 @@
expect(messages).toContainEqual(expect.objectContaining({ type: "session.resume" }));
});

it("should not receive duplicate events after resume", async () => {
// Regression test for https://github.com/github/copilot-sdk/issues/567
// Each resumeSession call was adding an additional event listener without
// removing the previous one, causing events to be delivered N+1 times after N resumes.

// Create session and do a turn, then let the session handle go away ungracefully
const session1 = await client.createSession({ onPermissionRequest: approveAll });
const sessionId = session1.sessionId;
await session1.sendAndWait({ prompt: "What is 1+1?" });

// Resume twice (simulating two reconnects) without cleaning up previous handles.
// The old session handles just go away as they would in a real disconnect.
await client.resumeSession(sessionId, { onPermissionRequest: approveAll });
const session3 = await client.resumeSession(sessionId, { onPermissionRequest: approveAll });

Comment on lines +207 to +221
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new regression test is expected (per PR description) to fail until the Copilot CLI/runtime fix lands, but this PR doesn’t bump the bundled CLI dependency or otherwise gate/skip the test. As-is, merging this PR is likely to make CI fail for the SDK repo. Consider temporarily marking this test as skipped with a TODO referencing the runtime PR (and unskip once the CLI package version used in tests includes the fix), or bump the CLI/runtime dependency in the same PR so the test is green immediately.

Copilot uses AI. Check for mistakes.
// Now send on the latest resumed session and collect events
const receivedEvents: Array<{ type: string }> = [];
session3.on((event) => {
receivedEvents.push({ type: event.type });
});
await session3.sendAndWait({ prompt: "What is 3+3?" });

// Each assistant.message should arrive exactly once, not duplicated
const assistantMessages = receivedEvents.filter((e) => e.type === "assistant.message");
expect(assistantMessages).toHaveLength(1);

Check failure on line 231 in nodejs/test/e2e/session.test.ts

View workflow job for this annotation

GitHub Actions / Node.js SDK Tests (macos-latest)

test/e2e/session.test.ts > Sessions > should not receive duplicate events after resume

AssertionError: expected [ { type: 'assistant.message' }, …(2) ] to have a length of 1 but got 3 - Expected + Received - 1 + 3 ❯ test/e2e/session.test.ts:231:35

Check failure on line 231 in nodejs/test/e2e/session.test.ts

View workflow job for this annotation

GitHub Actions / Node.js SDK Tests (windows-latest)

test/e2e/session.test.ts > Sessions > should not receive duplicate events after resume

AssertionError: expected [ { type: 'assistant.message' }, …(2) ] to have a length of 1 but got 3 - Expected + Received - 1 + 3 ❯ test/e2e/session.test.ts:231:35

Check failure on line 231 in nodejs/test/e2e/session.test.ts

View workflow job for this annotation

GitHub Actions / Node.js SDK Tests (ubuntu-latest)

test/e2e/session.test.ts > Sessions > should not receive duplicate events after resume

AssertionError: expected [ { type: 'assistant.message' }, …(2) ] to have a length of 1 but got 3 - Expected + Received - 1 + 3 ❯ test/e2e/session.test.ts:231:35
});

it("should throw error when resuming non-existent session", async () => {
await expect(
client.resumeSession("non-existent-session-id", { onPermissionRequest: approveAll })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
models:
- claude-sonnet-4.5
conversations:
- messages:
- role: system
content: ${system}
- role: user
content: What is 1+1?
- role: assistant
content: 1+1 = 2
- role: user
content: What is 3+3?
- role: assistant
content: 3+3 = 6
Loading