github / github/copilot-sdk

SessionMetadataSnapshot.AlreadyInUse is always false for SDK-resumed sessions (never reflects concurrent use by another process)

Đang mở
#1,749 1 bình luận 1 reaction 0 người được giao Xem trên GitHub
bug
Ngôn ngữ chính
Java
Star
10.5k
Fork
1.5k
Merge trung bình
1 ngày 11 giờ
Pull request đã merge (30 ngày)
128

Mô tả

## Summary

`session.Rpc.Metadata.SnapshotAsync()` returns a `SessionMetadataSnapshot` whose `AlreadyInUse` is **always `false`** when a session is opened through the SDK — even when the same session is concurrently held open by a different live process. The documented contract says this field should be `true` "when the session was detected to be in use by another process."

The in-use information clearly exists at the time of the call (a live per-process lock file is present on disk in the session directory), but it is not reflected in the snapshot. This affects both a default resume and a resume with `SuppressResumeEvent = true`.

## Environment

- SDK: `GitHub.Copilot.SDK` (.NET) **v1.0.1**
- Bundled Copilot CLI: **1.0.63** (managed automatically by the SDK)
- OS: Windows
- Note: the root cause appears to be server-side (Copilot CLI), since the .NET client deserializes `alreadyInUse` straight from the `session.metadata.snapshot` RPC response. The same field exists in every language SDK (`alreadyInUse` / `AlreadyInUse`), so all clients are likely affected.

## Expected behavior

Per the generated contract for `SessionMetadataSnapshot.AlreadyInUse`:

> True when the session was detected to be in use by another process at construction time. Local consumers may surface a confirmation prompt before fully attaching. Always false for new sessions.

So when a session is resumed while another live process is holding it, `AlreadyInUse` should be `true`.

## Actual behavior

`AlreadyInUse` is `false` in that scenario.

## Minimal reproduction

Self-contained: two `CopilotClient` instances in one program (each spawns its own CLI server process, so the second one is genuinely "another process" relative to the first).

```csharp
using GitHub.Copilot;

#pragma warning disable GHCP001 // SnapshotAsync is experimental

// Client A creates a session, sends one message so it persists to disk,
// then stays alive (its CLI server keeps holding the session).
await using var holder = new CopilotClient();
var held = await holder.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
});
var sessionId = held.SessionId;
await held.SendAndWaitAsync(new MessageOptions { Prompt = "Reply with exactly: OK" });

// Client B (independent client + CLI server) resumes the SAME session
// using the DEFAULT resume path, then reads the snapshot.
await using var probe = new CopilotClient();
await using var resumed = await probe.ResumeSessionAsync(sessionId, new ResumeSessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
});

var snapshot = await resumed.Rpc.Metadata.SnapshotAsync();
Console.WriteLine($"AlreadyInUse = {snapshot.AlreadyInUse}"); // prints False; expected True
```

### Observed output

```
[holder] Created session: f5fdd147-8928-4759-b74c-fd3ffc2b469b
[disk] events.jsonl persisted: True (27817 bytes)
[disk] Lock files present (holder still alive): inuse.20772.lock
[probe] snapshot.AlreadyInUse = False (EXPECTED: True — held by [holder] process)
```

While `probe` calls `SnapshotAsync()`, the session directory
`~/.copilot/session-state/f5fdd147-.../` contains `inuse.20772.lock`, and PID `20772`
(the holder's CLI server) is alive — i.e. the session is demonstrably in use by another
process, yet the snapshot reports `AlreadyInUse = False`.

## Supporting evidence

- The session directory contains a per-process lock file named `inuse..lock` whose contents are the owning process PID. During the repro this file exists and its PID is a live process, so the "in use by another process" condition is objectively true at snapshot time.
- The behavior is identical with `SuppressResumeEvent = true` and with the default resume, so it is not a side effect of suppressing the resume event.

## Hypothesized root cause

On the SDK resume path, the runtime does not surface the in-use/lock-detection result onto the metadata snapshot — `AlreadyInUse` is left at its default (`false`). The underlying data is available (the lock files are on disk and detection runs), so this looks like the computed value simply isn't propagated to the snapshot for SDK-initiated resumes, whereas a brand-new session correctly reports `false`.

## Test coverage gap / suggested fix

The existing e2e tests assert `AlreadyInUse == false` only for a freshly-created session, which is correct per the "Always false for new sessions" clause, but none cover a session that is concurrently in use:

- `dotnet/test/E2E/RpcSessionStateE2ETests.cs` → `Assert.False(initialSnapshot.AlreadyInUse);`
- `go/internal/e2e/rpc_session_state_e2e_test.go`
- `nodejs/test/e2e/rpc_session_state.e2e.test.ts` → `expect(initialSnapshot.alreadyInUse).toBe(false);`

Suggested additions:
1. Populate `alreadyInUse` on the snapshot from the same in-use detection used elsewhere, on the SDK resume path.
2. Add an e2e test: create + hold a session in one client, resume it from a second client, and assert the second client's snapshot reports `AlreadyInUse == true`.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Start with the resume and snapshot behavior described in dotnet/test/E2E/RpcSessionStateE2ETests.cs, then compare the corresponding Go and Node.js end-to-end tests. Trace SDK resume handling and the session.metadata.snapshot response to find where the detected lock state is lost. Done means a held session resumed by another client reports AlreadyInUse=true, while new sessions remain false.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
csharp, go, typescript
Lĩnh vực
api, backend, testing
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
48/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.