Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/main/java/com/github/copilot/sdk/CopilotClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.github.copilot.sdk.json.DeleteSessionResponse;
import com.github.copilot.sdk.json.GetAuthStatusResponse;
import com.github.copilot.sdk.json.GetLastSessionIdResponse;
import com.github.copilot.sdk.json.GetSessionMetadataResponse;
import com.github.copilot.sdk.json.GetModelsResponse;
import com.github.copilot.sdk.json.GetStatusResponse;
import com.github.copilot.sdk.json.ListSessionsResponse;
Expand Down Expand Up @@ -628,6 +629,34 @@ public CompletableFuture<List<SessionMetadata>> listSessions(SessionListFilter f
});
}

/**
* Gets metadata for a specific session by ID.
* <p>
* This provides an efficient O(1) lookup of a single session's metadata instead
* of listing all sessions. Returns {@code null} if the session is not found.
*
* <h2>Example Usage</h2>
*
* <pre>{@code
* SessionMetadata metadata = client.getSessionMetadata("session-123").get();
* if (metadata != null) {
* System.out.println("Session started at: " + metadata.getStartTime());
* }
* }</pre>
*
* @param sessionId
* the ID of the session to look up
* @return a future that resolves with the session metadata, or {@code null} if
* not found
* @see SessionMetadata
* @see #listSessions()
*/
public CompletableFuture<SessionMetadata> getSessionMetadata(String sessionId) {
return ensureConnected().thenCompose(connection -> connection.rpc
.invoke("session.getMetadata", Map.of("sessionId", sessionId), GetSessionMetadataResponse.class)
.thenApply(GetSessionMetadataResponse::session));
}

/**
* Gets the ID of the session currently displayed in the TUI.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

package com.github.copilot.sdk.json;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Internal response object from getting session metadata.
* <p>
* This is a low-level class for JSON-RPC communication containing the metadata
* for a specific session, or {@code null} if the session was not found.
*
* @see com.github.copilot.sdk.CopilotClient#getSessionMetadata(String)
* @see SessionMetadata
* @since 1.0.0
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @since tag is set to 1.0.0, but this response type (and CopilotClient#getSessionMetadata(String)) is being introduced in this PR. Please update @since to the version where this API is actually added (consistent with other newer additions that use @since 1.2.0, etc.).

Suggested change
* @since 1.0.0
* @since 1.2.0

Copilot uses AI. Check for mistakes.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public record GetSessionMetadataResponse(
/** The session metadata, or {@code null} if the session was not found. */
@JsonProperty("session") SessionMetadata session) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
* Metadata about an existing Copilot session.
* <p>
* This class represents session information returned when listing available
* sessions via {@link com.github.copilot.sdk.CopilotClient#listSessions()}. It
* sessions via {@link com.github.copilot.sdk.CopilotClient#listSessions()} or
* looking up a specific session via
* {@link com.github.copilot.sdk.CopilotClient#getSessionMetadata(String)}. It
* includes timing information, a summary of the conversation, and whether the
* session is stored remotely.
*
Expand Down
44 changes: 44 additions & 0 deletions src/test/java/com/github/copilot/sdk/CopilotSessionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand All @@ -19,6 +20,7 @@

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import com.github.copilot.sdk.events.AbstractSessionEvent;
Expand Down Expand Up @@ -693,6 +695,48 @@ void testShouldDeleteSession() throws Exception {
}
}

/**
* Verifies that session metadata can be retrieved by ID.
* <p>
* TODO: Re-enable once test harness CAPI proxy supports this test's session
* lifecycle.
*
* @see Snapshot: session/should_get_session_metadata
*/
@Disabled("Needs test harness CAPI proxy support")
@Test
void testShouldGetSessionMetadata() throws Exception {
ctx.configureForTest("session", "should_get_session_metadata");

try (CopilotClient client = ctx.createClient()) {
CopilotSession session = client
.createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get();

session.sendAndWait(new MessageOptions().setPrompt("Say hello")).get(60, TimeUnit.SECONDS);

// Small delay to ensure session file is written to disk
Thread.sleep(200);

// Get metadata for the session we just created
var metadata = client.getSessionMetadata(session.getSessionId()).get(30, TimeUnit.SECONDS);
assertNotNull(metadata);
assertEquals(session.getSessionId(), metadata.getSessionId());
assertNotNull(metadata.getStartTime());
assertNotNull(metadata.getModifiedTime());

// Verify context field
if (metadata.getContext() != null) {
assertNotNull(metadata.getContext().getCwd());
}

// Verify non-existent session returns null
var notFound = client.getSessionMetadata("non-existent-session-id").get(30, TimeUnit.SECONDS);
assertNull(notFound);

session.close();
}
}

/**
* Verifies that sessions can be created with custom tools.
*
Expand Down
Loading