@@ -709,6 +709,11 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques
709709 invocation .setSessionId (sessionId );
710710 handler .handle (permissionRequest , invocation ).thenAccept (result -> {
711711 try {
712+ if (PermissionRequestResultKind .NO_RESULT
713+ .equals (new PermissionRequestResultKind (result .getKind ()))) {
714+ // "no-result" means leave the request unanswered — no RPC call.
715+ return ;
716+ }
712717 rpc .invoke ("session.permissions.handlePendingPermissionRequest" ,
713718 Map .of ("sessionId" , sessionId , "requestId" , requestId , "result" , result ), Object .class );
714719 } catch (Exception e ) {
@@ -801,7 +806,17 @@ CompletableFuture<PermissionRequestResult> handlePermissionRequest(JsonNode perm
801806 PermissionRequest request = MAPPER .treeToValue (permissionRequestData , PermissionRequest .class );
802807 var invocation = new PermissionInvocation ();
803808 invocation .setSessionId (sessionId );
804- return handler .handle (request , invocation ).exceptionally (ex -> {
809+ return handler .handle (request , invocation ).thenApply (result -> {
810+ if (PermissionRequestResultKind .NO_RESULT .equals (new PermissionRequestResultKind (result .getKind ()))) {
811+ throw new IllegalStateException (
812+ "Permission handlers cannot return 'no-result' when connected to a protocol v2 server." );
813+ }
814+ return result ;
815+ }).exceptionally (ex -> {
816+ if (ex instanceof IllegalStateException ise && ise .getMessage () != null
817+ && ise .getMessage ().contains ("no-result" )) {
818+ throw (IllegalStateException ) ex ;
819+ }
805820 LOG .log (Level .SEVERE , "Permission handler threw an exception" , ex );
806821 PermissionRequestResult result = new PermissionRequestResult ();
807822 result .setKind ("denied-no-approval-rule-and-could-not-request-from-user" );
@@ -1000,8 +1015,39 @@ public CompletableFuture<Void> abort() {
10001015 * @since 1.0.11
10011016 */
10021017 public CompletableFuture <Void > setModel (String model ) {
1018+ return setModel (model , null );
1019+ }
1020+
1021+ /**
1022+ * Changes the model and reasoning effort for this session.
1023+ * <p>
1024+ * The new model and reasoning effort take effect for the next message.
1025+ * Conversation history is preserved.
1026+ *
1027+ * <pre>{@code
1028+ * session.setModel("claude-sonnet-4.6", "high").get();
1029+ * }</pre>
1030+ *
1031+ * @param model
1032+ * the model ID to switch to (e.g., {@code "gpt-4.1"})
1033+ * @param reasoningEffort
1034+ * the reasoning effort level ({@code "low"}, {@code "medium"},
1035+ * {@code "high"}, {@code "xhigh"}), or {@code null} to use the
1036+ * model's default
1037+ * @return a future that completes when the model switch is acknowledged
1038+ * @throws IllegalStateException
1039+ * if this session has been terminated
1040+ * @since 1.1.0
1041+ */
1042+ public CompletableFuture <Void > setModel (String model , String reasoningEffort ) {
10031043 ensureNotTerminated ();
1004- return rpc .invoke ("session.model.switchTo" , Map .of ("sessionId" , sessionId , "modelId" , model ), Void .class );
1044+ var params = new java .util .LinkedHashMap <String , Object >();
1045+ params .put ("sessionId" , sessionId );
1046+ params .put ("modelId" , model );
1047+ if (reasoningEffort != null ) {
1048+ params .put ("reasoningEffort" , reasoningEffort );
1049+ }
1050+ return rpc .invoke ("session.model.switchTo" , params , Void .class );
10051051 }
10061052
10071053 /**
0 commit comments