Skip to content

resumeSession causes accumulating duplicate events #567

@fashxp

Description

@fashxp

Each call to client.resumeSession() causes the CLI binary to add an additional internal event subscription without removing the previous one. After N resume calls, every session.event JSON-RPC notification is delivered N+1 times.

Reproduction

  import { CopilotClient } from '@github/copilot-sdk'

  const client = new CopilotClient({ githubToken: process.env.GH_TOKEN })
  await client.start()

  const session = await client.createSession({ model: 'gpt-5-mini', streaming: true })
  const sid = session.sessionId

  // Baseline: count events on a normal send
  let baseline = 0
  const unsub1 = session.on(() => { baseline++ })
  await session.sendAndWait({ prompt: 'Reply with exactly: "ok"' })
  unsub1()
  console.log(`Baseline: ${baseline} events`)

  // Resume #1
  const s2 = await client.resumeSession(sid, { model: 'gpt-5-mini', streaming: true })
  let count2 = 0
  const unsub2 = s2.on(() => { count2++ })
  await s2.sendAndWait({ prompt: 'Reply with exactly: "ok"' })
  unsub2()
  console.log(`After resume #1: ${count2} events (expect ~2x baseline)`)

  // Resume #2
  const s3 = await client.resumeSession(sid, { model: 'gpt-5-mini', streaming: true })
  let count3 = 0
  const unsub3 = s3.on(() => { count3++ })
  await s3.sendAndWait({ prompt: 'Reply with exactly: "ok"' })
  unsub3()
  console.log(`After resume #2: ${count3} events (expect ~3x baseline)`)

  await client.stop()
  process.exit(0)

Run with:

GH_TOKEN=github_pat_... node test.mjs

Observed behavior

  Baseline:        10 events
  After resume #1: 23 events  (2.3x)
  After resume #2: 34 events  (3.3x)

Each resumeSession call adds roughly 1x baseline worth of duplicate events. After 5 resumes, every event arrives 6 times.
The duplicates are exact copies — same type, same data, same id, arriving within 0–1ms of each other. The session.resume event itself also arrives duplicated.

Expected behavior

Event count should remain constant regardless of how many times resumeSession is called. Each event should be delivered exactly once.

Why this matters

We are implementing an agent where we would like to adapt configurations of the session (e.g. available tools, used model) during the conversation depending on the tasks but without loosing the context and the history.
Just calling resumeSession does exactly what we need - except for the event duplication.

Additional observations

  • Happens with any resumeSession call, even with identical config (same model, same settings)
  • The config delta doesn't matter — same model, different model, different systemMessage, different excludedTools
    all trigger it equally
  • session.send() without resumeSession does not accumulate (baseline stays stable across multiple sends on the
    same session)
  • Calling unsubscribe() on the JS side has no effect
  • Consumed premium requests are not doubled

Environment

  • @github/copilot-sdk: 0.1.25 and 0.1.26 (both affected)
  • CLI binary: 0.0.411 and 0.0.417 (both affected)
  • Node.js: 22.x
  • OS: Linux (Docker, Debian-based)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions