Skip to content

[BUG]: Sub-agent tool calls bypass SDK session hooks and permission handlers (was: onPreToolUse hook does not fire for sub-agent tool calls) #64

Description

@bytehaufen

What happened?

When a parent agent delegates work to a sub-agent via the task tool, the onPreToolUse session hook only fires for the parent's own tool calls — not for any tools the sub-agent invokes.

This makes it impossible to intercept, audit, or deny tool calls made by sub-agents.

JUnit 5 test that should succeed

@Test
void onPreToolUse_should_fire_for_subagent_tool_calls() throws Exception {
    List<String> seenTools = new CopyOnWriteArrayList<>();

    SessionHooks hooks = new SessionHooks().setOnPreToolUse((input, invocation) -> {
        seenTools.add(input.getToolName());
        System.out.println("onPreToolUse: " + input.getToolName());
        return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
    });

    CustomAgentConfig parent = new CustomAgentConfig()
        .setName("parent")
        .setTools(List.of("task"))
        .setPrompt("Delegate all work to the sub-agent.");

    CustomAgentConfig child = new CustomAgentConfig()
        .setName("child")
        .setTools(List.of("glob"))
        .setPrompt("Use glob to list files, then respond.");

    SessionConfig config = new SessionConfig()
        .setModel("gpt-4.1")
        .setHooks(hooks)
        .setCustomAgents(List.of(parent, child))
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL);

    try (CopilotClient client = new CopilotClient()) {
        client.createSession(config)
            .get()
            .sendAndWait(new MessageOptions()
                .setPrompt("Use the sub-agent `child` to list files in the current directory."))
            .get(60, TimeUnit.SECONDS);
    }

    System.out.println("Seen tools: " + seenTools);

    // Observed: seenTools typically contains only ["task"].
    // Expected: it should also contain "glob".
    assertTrue(
        seenTools.contains("glob"),
        "Expected onPreToolUse to fire for sub-agent tool calls as well"
    );
}

Expected

onPreToolUse fires for all tool calls: the parent's task call and the sub-agent's glob call.

Actual

onPreToolUse only fires for task. The sub-agent's glob call is invisible to the hook.

Impact

  • Security: SDK users cannot enforce tool restrictions on sub-agents.
  • Observability: No way to log or audit what sub-agents actually do.
  • PermissionHandler has the same gap — it fires for sub-agent MCP tools but with null tool identity (extensionData is empty), making per-tool decisions impossible.

Versions

copilot-sdk-java: 0.2.2-java.1
maven: 3.9.12
copilot-cli: 1.0.22

Relevant log output

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. bytehaufen commented on Apr 12, 2026

    @bytehaufen
    Author

    Thanks for the explanation in #68 — that makes sense.

    So if I understand it right, sub-agents run with their own session IDs from the CLI, and that’s why onPreToolUse / PermissionHandler don’t fire properly for their tool calls.

    That would mean the fix has to happen in the CLI/runtime, not in the Java SDK.

    Is there already an issue for this on the CLI side, or should this be tracked there instead?

    This is pretty important for enforcing tool restrictions and auditing.

  2. edburns commented on Apr 21, 2026

    @edburns
    Collaborator

    @bytehaufen wrote:

    So if I understand it right, sub-agents run with their own session IDs from the CLI, and that’s why onPreToolUse / PermissionHandler don’t fire properly for their tool calls.

    That would mean the fix has to happen in the CLI/runtime, not in the Java SDK.

    Is there already an issue for this on the CLI side, or should this be tracked there instead?

    This is pretty important for enforcing tool restrictions and auditing.

    According to my copilot research regarding the approach the reference implementations use to address this corner case, the answer is: they don't.

    Copilot wrote:

    Neither reference implementation searches for another session with a compatible handler. They simply reject the request as an error if the session ID isn't in the registry.

    What this means for the review comment: The reviewer's concern about the wrong sessionId being stamped on invocation objects is technically valid — but the reviewer's suggested fix (plumbing the originating session ID through) is solving a problem that the reference implementations don't solve at all, because the reference implementations don't have the fallback path in the first place.

    So the real question is whether the Java SDK's fallback mechanism (PR #89) is even the right approach. The reference SDKs treat an unknown session ID as an error. If the Java SDK also wants to handle sub-agent sessions — which is a feature beyond the reference implementations — then the reviewer is correct that you'd need to propagate the original session ID. But if you want to stay aligned with the reference implementations, the fallback logic itself is the deviation, and the session ID issue is a downstream consequence of that deviation.

    I will take this up with the reference implementation team.

    I'll leave this open for now.

  3. changed the title [-][BUG]: `onPreToolUse` hook does not fire for sub-agent tool calls[/-] [+][BUG]: Sub-agent tool calls bypass SDK session hooks and permission handlers (was: `onPreToolUse` hook does not fire for sub-agent tool calls)[/+] on Apr 21, 2026
  4. edburns commented on Apr 21, 2026

    @edburns
    Collaborator
  5. bytehaufen commented on Apr 21, 2026

    @bytehaufen
    Author

    Thank you

  6. edburns commented on Apr 22, 2026

    @edburns
    Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions