github / github/copilot-sdk

resumeSession() on already-active session causes doubled events — SDK should guard

未关闭 适合新手
#742 0 条评论 2 个 reaction 已指派 0 人 在 GitHub 查看
主要语言
Java
星标
10.5k
派生
1.5k
平均合并
1 天 11 小时
30 天内合并 PR
128

描述

## Problem

Calling `client.resumeSession(sessionId)` on a session that's already active (created via `createSession()` on the same connection) causes all subsequent `session.event` notifications to fire **twice**. This is a server-side issue (the CLI registers a second event subscription without deduplicating), but the SDK can and should protect callers.

## Reproduction

```javascript
const { CopilotClient, approveAll } = await import('@github/copilot-sdk');
const client = new CopilotClient({ cwd: process.cwd(), autoStart: true });

const session = await client.createSession({
model: 'claude-sonnet-4-5',
onPermissionRequest: approveAll,
});

// Events are single here ✅
let count1 = 0;
const unsub1 = session.on(() => count1++);
await session.sendAndWait({ prompt: 'Say hello' });
unsub1();

// Resume the SAME active session
const resumed = await client.resumeSession(session.sessionId, {
onPermissionRequest: approveAll,
});

// Events are now doubled ❌
let count2 = 0;
const unsub2 = resumed.on(() => count2++);
await resumed.sendAndWait({ prompt: 'Say hello again' });
unsub2();

console.log(count1, count2); // ~9, ~17
```

## Suggested Fix

In `client.ts` `resumeSession()`, check if the session already exists in the local `sessions` Map before sending `session.resume` to the server:

```typescript
async resumeSession(sessionId: string, config: ResumeSessionConfig): Promise {
// Guard: if we already have a live session for this ID, return it
const existing = this.sessions.get(sessionId);
if (existing) {
// Re-register handlers if config changed
existing.registerTools(config.tools);
existing.registerPermissionHandler(config.onPermissionRequest);
if (config.hooks) existing.registerHooks(config.hooks);
return existing;
}

// ... existing resumeSession logic for truly new sessions
}
```

This prevents the server-side duplicate subscription while still allowing callers to update tools/permissions. Callers who genuinely want a fresh session can `destroy()` first.

## Impact

Any SDK caller that uses `resumeSession()` as a health check (verify session is alive before sending a message) gets silently broken — doubled events for the rest of the session. We discovered this when implementing plan-mode session recovery in a VS Code extension.

## Workaround

We use `session.abort()` as a lightweight liveness check instead of `resumeSession()`. `abort()` is a no-op on idle sessions and throws `"Session not found"` if the session was garbage-collected. Same signal, no side effects.

## Environment

- SDK: v0.1.22
- CLI: v0.0.421
- Node: v24.13.1

贡献指南

打开贡献指南

调研方向

从 client.ts 中的 resumeSession() 开始,检查本地的 sessions Map 和现有的会话注册方法。重现所提供的 createSession/resumeSession 序列,然后验证已处于活动状态的会话不会产生重复通知,而真正的新会话仍能正常恢复。

由索引模型根据 Issue 内容生成。

评估

技术栈
node.js, typescript
领域
api
Issue 类型
缺陷
难度
2/5
预计耗时
1-3 小时
活跃度
停滞
描述清晰度
描述清楚
新手友好度
72/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。