-
Notifications
You must be signed in to change notification settings - Fork 907
Description
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)