Skip to content

Commit d649959

Browse files
DavsterlCopilot
andcommitted
feat: expose clientName in SDK session metadata
Adds clientName field to SessionMetadata across all SDK languages (Node, Python, Go, .NET, Rust, Java) to expose the runtime client identifier that created or last resumed a session. Changes: - Updated generated RPC types to include clientName field - Added clientName to public SessionMetadata models - Added serialization tests for new field The clientName field allows SDK consumers to identify which client (CLI, SDK app, extension) owns a session when listing sessions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 89fd30c commit d649959

19 files changed

Lines changed: 212 additions & 1 deletion

File tree

dotnet/src/Generated/Rpc.cs

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dotnet/src/Types.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2667,6 +2667,10 @@ public sealed class SessionMetadata
26672667
/// </summary>
26682668
public string? Summary { get; set; }
26692669
/// <summary>
2670+
/// Identifier of the client driving the session.
2671+
/// </summary>
2672+
public string? ClientName { get; set; }
2673+
/// <summary>
26702674
/// Whether the session is running on a remote server.
26712675
/// </summary>
26722676
public bool IsRemote { get; set; }

dotnet/test/Unit/SerializationTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,30 @@ public void ResumeSessionRequest_CanSerializeInstructionDirectories_WithSdkOptio
171171
Assert.Equal("C:\\resume-instructions", root.GetProperty("instructionDirectories")[0].GetString());
172172
}
173173

174+
[Fact]
175+
public void SessionMetadata_CanRoundTripClientName_WithSdkOptions()
176+
{
177+
var options = GetSerializerOptions();
178+
var original = new SessionMetadata
179+
{
180+
SessionId = "session-1",
181+
StartTime = DateTimeOffset.Parse("2025-01-01T00:00:00Z"),
182+
ModifiedTime = DateTimeOffset.Parse("2025-01-01T01:00:00Z"),
183+
Summary = "loaded session",
184+
ClientName = "my-app",
185+
IsRemote = false
186+
};
187+
188+
var json = JsonSerializer.Serialize(original, options);
189+
using var document = JsonDocument.Parse(json);
190+
var root = document.RootElement;
191+
Assert.Equal("my-app", root.GetProperty("clientName").GetString());
192+
193+
var deserialized = JsonSerializer.Deserialize<SessionMetadata>(json, options);
194+
Assert.NotNull(deserialized);
195+
Assert.Equal("my-app", deserialized.ClientName);
196+
}
197+
174198
[Fact]
175199
public void CreateSessionRequest_CanSerializeEnableSessionTelemetry_WithSdkOptions()
176200
{

go/client_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,33 @@ func TestResumeSessionRequest_ClientName(t *testing.T) {
402402
})
403403
}
404404

405+
func TestSessionMetadata_ClientName(t *testing.T) {
406+
t.Run("round-trips clientName in JSON", func(t *testing.T) {
407+
var metadata SessionMetadata
408+
if err := json.Unmarshal([]byte(`{
409+
"sessionId":"s1",
410+
"startTime":"2025-01-01T00:00:00Z",
411+
"modifiedTime":"2025-01-01T01:00:00Z",
412+
"summary":"loaded session",
413+
"clientName":"my-app",
414+
"isRemote":false
415+
}`), &metadata); err != nil {
416+
t.Fatalf("Failed to unmarshal: %v", err)
417+
}
418+
if metadata.ClientName == nil || *metadata.ClientName != "my-app" {
419+
t.Fatalf("Expected clientName to be my-app, got %v", metadata.ClientName)
420+
}
421+
422+
data, err := json.Marshal(metadata)
423+
if err != nil {
424+
t.Fatalf("Failed to marshal: %v", err)
425+
}
426+
if !strings.Contains(string(data), `"clientName":"my-app"`) {
427+
t.Fatalf("Expected marshaled JSON to include clientName, got %s", string(data))
428+
}
429+
})
430+
}
431+
405432
func TestCreateSessionRequest_Agent(t *testing.T) {
406433
t.Run("includes agent in JSON when set", func(t *testing.T) {
407434
req := createSessionRequest{Agent: "test-agent"}

go/rpc/zrpc.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,7 @@ type SessionMetadata struct {
13601360
StartTime time.Time `json:"startTime"`
13611361
ModifiedTime time.Time `json:"modifiedTime"`
13621362
Summary *string `json:"summary,omitempty"`
1363+
ClientName *string `json:"clientName,omitempty"`
13631364
IsRemote bool `json:"isRemote"`
13641365
Context *SessionContext `json:"context,omitempty"`
13651366
}

java/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionMetadata.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public record SessionMetadata(
3131
@JsonProperty("summary") String summary,
3232
/** Optional human-friendly name set via /rename */
3333
@JsonProperty("name") String name,
34+
/** Runtime client name that created/last resumed this session */
35+
@JsonProperty("clientName") String clientName,
3436
/** True for remote (GitHub) sessions; false for local */
3537
@JsonProperty("isRemote") Boolean isRemote,
3638
/** Schema for the `SessionContext` type. */

java/src/main/java/com/github/copilot/sdk/json/SessionMetadata.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public class SessionMetadata {
4646
@JsonProperty("summary")
4747
private String summary;
4848

49+
@JsonProperty("clientName")
50+
private String clientName;
51+
4952
@JsonProperty("isRemote")
5053
private boolean isRemote;
5154

@@ -130,6 +133,25 @@ public void setSummary(String summary) {
130133
this.summary = summary;
131134
}
132135

136+
/**
137+
* Gets the identifier of the client driving the session.
138+
*
139+
* @return the client name, or {@code null} if not available
140+
*/
141+
public String getClientName() {
142+
return clientName;
143+
}
144+
145+
/**
146+
* Sets the client identifier for the session.
147+
*
148+
* @param clientName
149+
* the client name
150+
*/
151+
public void setClientName(String clientName) {
152+
this.clientName = clientName;
153+
}
154+
133155
/**
134156
* Returns whether this session is stored remotely.
135157
*

java/src/test/java/com/github/copilot/sdk/ModelInfoTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ void sessionMetadataGettersAndSetters() {
6060
assertNull(meta.getStartTime());
6161
assertNull(meta.getModifiedTime());
6262
assertNull(meta.getSummary());
63+
assertNull(meta.getClientName());
6364
assertFalse(meta.isRemote());
6465

66+
meta.setClientName("my-app");
67+
assertEquals("my-app", meta.getClientName());
6568
meta.setRemote(true);
6669
assertTrue(meta.isRemote());
6770
}

nodejs/src/client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ export class CopilotClient {
13471347
startTime: string;
13481348
modifiedTime: string;
13491349
summary?: string;
1350+
clientName?: string;
13501351
isRemote: boolean;
13511352
context?: { cwd: string; gitRoot?: string; repository?: string; branch?: string };
13521353
}>;
@@ -1402,6 +1403,7 @@ export class CopilotClient {
14021403
startTime: string;
14031404
modifiedTime: string;
14041405
summary?: string;
1406+
clientName?: string;
14051407
isRemote: boolean;
14061408
context?: { cwd: string; gitRoot?: string; repository?: string; branch?: string };
14071409
}): SessionMetadata {
@@ -1411,6 +1413,7 @@ export class CopilotClient {
14111413
startTime: new Date(raw.startTime),
14121414
modifiedTime: new Date(raw.modifiedTime),
14131415
summary: raw.summary,
1416+
clientName: raw.clientName,
14141417
isRemote: raw.isRemote,
14151418
context: context
14161419
? {

0 commit comments

Comments
 (0)