@@ -295,7 +295,8 @@ void toolCallHandlerFails() throws Exception {
295295 // ===== permission.request tests =====
296296
297297 @ Test
298- void permissionRequestWithUnknownSession () throws Exception {
298+ void permissionRequestWithUnknownSessionAndNoFallback () throws Exception {
299+ // No sessions registered → denied
299300 ObjectNode params = MAPPER .createObjectNode ();
300301 params .put ("sessionId" , "nonexistent" );
301302 params .putObject ("permissionRequest" );
@@ -307,6 +308,25 @@ void permissionRequestWithUnknownSession() throws Exception {
307308 assertEquals ("denied-no-approval-rule-and-could-not-request-from-user" , result .get ("kind" ).asText ());
308309 }
309310
311+ @ Test
312+ void permissionRequestWithSubAgentSessionFallsBackToParentWithHandler () throws Exception {
313+ // Register a parent session with a permission handler, invoke with a sub-agent
314+ // session ID
315+ CopilotSession parentSession = createSession ("parent-session" );
316+ parentSession .registerPermissionHandler ((request , invocation ) -> CompletableFuture
317+ .completedFuture (new PermissionRequestResult ().setKind ("allow" )));
318+
319+ ObjectNode params = MAPPER .createObjectNode ();
320+ params .put ("sessionId" , "sub-agent-session-id" ); // Not in registry
321+ params .putObject ("permissionRequest" );
322+
323+ invokeHandler ("permission.request" , "15" , params );
324+
325+ JsonNode response = readResponse ();
326+ JsonNode result = response .get ("result" ).get ("result" );
327+ assertEquals ("allow" , result .get ("kind" ).asText ());
328+ }
329+
310330 @ Test
311331 void permissionRequestWithHandler () throws Exception {
312332 CopilotSession session = createSession ("s1" );
@@ -453,7 +473,8 @@ void userInputRequestHandlerFails() throws Exception {
453473 // ===== hooks.invoke tests =====
454474
455475 @ Test
456- void hooksInvokeWithUnknownSession () throws Exception {
476+ void hooksInvokeWithUnknownSessionAndNoFallback () throws Exception {
477+ // No sessions registered at all → returns null output (no-op)
457478 ObjectNode params = MAPPER .createObjectNode ();
458479 params .put ("sessionId" , "nonexistent" );
459480 params .put ("hookType" , "preToolUse" );
@@ -462,8 +483,52 @@ void hooksInvokeWithUnknownSession() throws Exception {
462483 invokeHandler ("hooks.invoke" , "30" , params );
463484
464485 JsonNode response = readResponse ();
465- assertNotNull (response .get ("error" ));
466- assertEquals (-32602 , response .get ("error" ).get ("code" ).asInt ());
486+ // No sessions with hooks → null output, not an error
487+ assertNull (response .get ("error" ), "Should not return an error when no fallback session exists" );
488+ JsonNode output = response .get ("result" ).get ("output" );
489+ assertTrue (output == null || output .isNull (), "Output should be null when no session with hooks is found" );
490+ }
491+
492+ @ Test
493+ void hooksInvokeWithSubAgentSessionFallsBackToParentWithHooks () throws Exception {
494+ // Register a parent session with hooks, but invoke with a sub-agent session ID
495+ CopilotSession parentSession = createSession ("parent-session" );
496+ parentSession .registerHooks (new SessionHooks ().setOnPreToolUse (
497+ (input , invocation ) -> CompletableFuture .completedFuture (PreToolUseHookOutput .allow ())));
498+
499+ ObjectNode params = MAPPER .createObjectNode ();
500+ params .put ("sessionId" , "sub-agent-session-id" ); // Not in registry
501+ params .put ("hookType" , "preToolUse" );
502+ ObjectNode input = params .putObject ("input" );
503+ input .put ("toolName" , "glob" );
504+ input .put ("toolCallId" , "tc-sub-1" );
505+
506+ invokeHandler ("hooks.invoke" , "35" , params );
507+
508+ JsonNode response = readResponse ();
509+ assertNull (response .get ("error" ), "Should not return an error when a fallback session with hooks exists" );
510+ JsonNode output = response .get ("result" ).get ("output" );
511+ assertNotNull (output );
512+ assertEquals ("allow" , output .get ("permissionDecision" ).asText ());
513+ }
514+
515+ @ Test
516+ void hooksInvokeWithSubAgentSessionAndNoSessionHasHooks () throws Exception {
517+ // Register a parent session WITHOUT hooks, invoke with unknown sub-agent
518+ // session ID
519+ createSession ("parent-session-no-hooks" );
520+
521+ ObjectNode params = MAPPER .createObjectNode ();
522+ params .put ("sessionId" , "sub-agent-session-id" ); // Not in registry
523+ params .put ("hookType" , "preToolUse" );
524+ params .putObject ("input" );
525+
526+ invokeHandler ("hooks.invoke" , "36" , params );
527+
528+ JsonNode response = readResponse ();
529+ assertNull (response .get ("error" ), "Should not return an error when no fallback session has hooks" );
530+ JsonNode output = response .get ("result" ).get ("output" );
531+ assertTrue (output == null || output .isNull (), "Output should be null when no session with hooks is found" );
467532 }
468533
469534 @ Test
0 commit comments