3030import com .fasterxml .jackson .databind .JsonNode ;
3131import com .fasterxml .jackson .databind .ObjectMapper ;
3232import com .github .copilot .sdk .generated .AssistantMessageEvent ;
33+ import com .github .copilot .sdk .generated .rpc .SessionCommandsHandlePendingCommandParams ;
34+ import com .github .copilot .sdk .generated .rpc .SessionPermissionsHandlePendingPermissionRequestParams ;
35+ import com .github .copilot .sdk .generated .rpc .SessionRpc ;
3336import com .github .copilot .sdk .generated .CapabilitiesChangedEvent ;
3437import com .github .copilot .sdk .generated .CommandExecuteEvent ;
3538import com .github .copilot .sdk .generated .ElicitationRequestedEvent ;
@@ -134,6 +137,7 @@ public final class CopilotSession implements AutoCloseable {
134137 private volatile SessionCapabilities capabilities = new SessionCapabilities ();
135138 private final SessionUiApi ui ;
136139 private final JsonRpcClient rpc ;
140+ private volatile SessionRpc sessionRpc ;
137141 private final Set <Consumer <SessionEvent >> eventHandlers = ConcurrentHashMap .newKeySet ();
138142 private final Map <String , ToolDefinition > toolHandlers = new ConcurrentHashMap <>();
139143 private final Map <String , CommandHandler > commandHandlers = new ConcurrentHashMap <>();
@@ -183,6 +187,7 @@ public final class CopilotSession implements AutoCloseable {
183187 this .rpc = rpc ;
184188 this .workspacePath = workspacePath ;
185189 this .ui = new SessionUiApiImpl ();
190+ this .sessionRpc = rpc != null ? new SessionRpc (rpc ::invoke , sessionId ) : null ;
186191 var executor = new ScheduledThreadPoolExecutor (1 , r -> {
187192 var t = new Thread (r , "sendAndWait-timeout" );
188193 t .setDaemon (true );
@@ -219,6 +224,7 @@ public String getSessionId() {
219224 */
220225 void setActiveSessionId (String sessionId ) {
221226 this .sessionId = sessionId ;
227+ this .sessionRpc = rpc != null ? new SessionRpc (rpc ::invoke , sessionId ) : null ;
222228 }
223229
224230 /**
@@ -269,6 +275,25 @@ public SessionUiApi getUi() {
269275 return ui ;
270276 }
271277
278+ /**
279+ * Returns the typed RPC client for this session.
280+ * <p>
281+ * Provides strongly-typed access to all session-level API namespaces. The
282+ * {@code sessionId} is injected automatically into every call.
283+ * <p>
284+ * Example usage:
285+ *
286+ * <pre>{@code
287+ * var agents = session.getRpc().agent.list().get();
288+ * }</pre>
289+ *
290+ * @return the session-scoped typed RPC client
291+ * @since 1.0.0
292+ */
293+ public SessionRpc getRpc () {
294+ return sessionRpc ;
295+ }
296+
272297 /**
273298 * Sets a custom error handler for exceptions thrown by event handlers.
274299 * <p>
@@ -851,8 +876,9 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques
851876 try {
852877 PermissionRequestResult denied = new PermissionRequestResult ();
853878 denied .setKind (PermissionRequestResultKind .DENIED_COULD_NOT_REQUEST_FROM_USER );
854- rpc .invoke ("session.permissions.handlePendingPermissionRequest" ,
855- Map .of ("sessionId" , sessionId , "requestId" , requestId , "result" , denied ), Object .class );
879+ sessionRpc .permissions .handlePendingPermissionRequest (
880+ new SessionPermissionsHandlePendingPermissionRequestParams (sessionId , requestId ,
881+ denied ));
856882 } catch (Exception e ) {
857883 LOG .log (Level .WARNING , "Error sending permission denied for requestId=" + requestId , e );
858884 }
@@ -863,8 +889,8 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques
863889 try {
864890 PermissionRequestResult denied = new PermissionRequestResult ();
865891 denied .setKind (PermissionRequestResultKind .DENIED_COULD_NOT_REQUEST_FROM_USER );
866- rpc . invoke ( "session. permissions.handlePendingPermissionRequest" ,
867- Map . of ( " sessionId" , sessionId , " requestId" , requestId , "result" , denied ), Object . class );
892+ sessionRpc . permissions .handlePendingPermissionRequest (
893+ new SessionPermissionsHandlePendingPermissionRequestParams ( sessionId , requestId , denied ));
868894 } catch (Exception sendEx ) {
869895 LOG .log (Level .WARNING , "Error sending permission denied for requestId=" + requestId , sendEx );
870896 }
@@ -908,8 +934,8 @@ private void executeCommandAndRespondAsync(String requestId, String commandName,
908934 Runnable task = () -> {
909935 if (handler == null ) {
910936 try {
911- rpc . invoke ( "session. commands.handlePendingCommand" , Map . of ( " sessionId" , sessionId , "requestId" ,
912- requestId , "error" , " Unknown command: " + commandName ), Object . class );
937+ sessionRpc . commands .handlePendingCommand ( new SessionCommandsHandlePendingCommandParams ( sessionId ,
938+ requestId , "Unknown command: " + commandName ));
913939 } catch (Exception e ) {
914940 LOG .log (Level .WARNING , "Error sending command error for requestId=" + requestId , e );
915941 }
@@ -920,16 +946,16 @@ private void executeCommandAndRespondAsync(String requestId, String commandName,
920946 .setArgs (args );
921947 handler .handle (ctx ).thenRun (() -> {
922948 try {
923- rpc . invoke ( "session. commands.handlePendingCommand" ,
924- Map . of ( " sessionId" , sessionId , " requestId" , requestId ), Object . class );
949+ sessionRpc . commands .handlePendingCommand (
950+ new SessionCommandsHandlePendingCommandParams ( sessionId , requestId , null ) );
925951 } catch (Exception e ) {
926952 LOG .log (Level .WARNING , "Error sending command result for requestId=" + requestId , e );
927953 }
928954 }).exceptionally (ex -> {
929955 try {
930956 String msg = ex .getMessage () != null ? ex .getMessage () : ex .toString ();
931- rpc . invoke ( "session. commands.handlePendingCommand" ,
932- Map . of ( " sessionId" , sessionId , " requestId" , requestId , "error" , msg ), Object . class );
957+ sessionRpc . commands .handlePendingCommand (
958+ new SessionCommandsHandlePendingCommandParams ( sessionId , requestId , msg ));
933959 } catch (Exception e ) {
934960 LOG .log (Level .WARNING , "Error sending command error for requestId=" + requestId , e );
935961 }
@@ -939,8 +965,8 @@ private void executeCommandAndRespondAsync(String requestId, String commandName,
939965 LOG .log (Level .WARNING , "Error executing command for requestId=" + requestId , e );
940966 try {
941967 String msg = e .getMessage () != null ? e .getMessage () : e .toString ();
942- rpc . invoke ( "session. commands.handlePendingCommand" ,
943- Map . of ( " sessionId" , sessionId , " requestId" , requestId , "error" , msg ), Object . class );
968+ sessionRpc . commands .handlePendingCommand (
969+ new SessionCommandsHandlePendingCommandParams ( sessionId , requestId , msg ));
944970 } catch (Exception sendEx ) {
945971 LOG .log (Level .WARNING , "Error sending command error for requestId=" + requestId , sendEx );
946972 }
0 commit comments