Summary
onPreToolUse and onPostToolUse hooks registered via SessionConfig.hooks are not invoked for tool calls made by sub-agents spawned via the built-in task tool. This means any security controls implemented through these hooks — such as denying specific tools, blocking dangerous shell commands, or redacting sensitive data from tool outputs — are bypassed when the model delegates work to a sub-agent.
Environment
@github/copilot-sdk: 0.2.2 (latest)
@github/copilot (CLI): 1.0.31 (latest)
- OS: Windows 11 / Linux (Ubuntu 24.04)
- Node.js: 22.x
Steps to Reproduce
- Create a session with
onPreToolUse and onPostToolUse hooks:
import { CopilotClient, approveAll } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
model: "claude-haiku-4.5",
onPermissionRequest: approveAll,
hooks: {
onPreToolUse: async (input, invocation) => {
console.log(`[pre-hook] tool=${input.toolName}`);
// Block destructive commands (from SDK docs example)
if (input.toolName === "bash") {
const cmd = String(input.toolArgs?.command || "");
if (/rm\s+-rf/i.test(cmd) || /Remove-Item\s+.*-Recurse/i.test(cmd)) {
return { permissionDecision: "deny", permissionDecisionReason: "Destructive command blocked" };
}
}
return undefined;
},
onPostToolUse: async (input, invocation) => {
console.log(`[post-hook] tool=${input.toolName}`);
return undefined;
},
},
});
- Also subscribe to
tool.execution_start events to track all tool calls:
session.on((event) => {
if (event.type === "tool.execution_start") {
console.log(`[EVENT] tool=${event.data.toolName} parent=${event.data.parentToolCallId ?? "none"}`);
}
});
- Send a prompt that forces the model to delegate via
task:
await session.sendAndWait({
prompt: "Use the task tool to spawn an explore agent that reads package.json and reports the name field.",
}, 120_000);
- Observe: hook log lines appear only for the parent agent's tool calls, never for the sub-agent's.
Observed Output
[EVENT] tool.execution_start tool=task parent=none
[PRE-HOOK] tool=task
[EVENT] tool.execution_start tool=view parent=toolu_01Ci9ftkLWQt6LLBMuu6FtiZ
[EVENT] tool.execution_complete tool=? parent=toolu_01Ci9ftkLWQt6LLBMuu6FtiZ
[POST-HOOK] tool=task
[EVENT] tool.execution_complete tool=? parent=none
The sub-agent's view call (lines 3–4, identified by parentToolCallId) starts and completes with no hook invocations. Only the parent's task call triggers onPreToolUse and onPostToolUse.
Expected Behavior
onPreToolUse and onPostToolUse hooks should be invoked for every tool call in the session, including tool calls made by sub-agents spawned via task. The hook contract should be transitive — if a parent session registers security hooks, all sub-agents operating within that session should inherit them.
Actual Behavior
Hooks are only invoked for direct tool calls in the parent agent. When the task tool spawns a sub-agent, that sub-agent's tool calls bypass hooks entirely while still emitting tool.execution_start / tool.execution_complete events (confirming the tools ran).
Summary
onPreToolUseandonPostToolUsehooks registered viaSessionConfig.hooksare not invoked for tool calls made by sub-agents spawned via the built-intasktool. This means any security controls implemented through these hooks — such as denying specific tools, blocking dangerous shell commands, or redacting sensitive data from tool outputs — are bypassed when the model delegates work to a sub-agent.Environment
@github/copilot-sdk: 0.2.2 (latest)@github/copilot(CLI): 1.0.31 (latest)Steps to Reproduce
onPreToolUseandonPostToolUsehooks:tool.execution_startevents to track all tool calls:task:Observed Output
The sub-agent's
viewcall (lines 3–4, identified byparentToolCallId) starts and completes with no hook invocations. Only the parent'staskcall triggersonPreToolUseandonPostToolUse.Expected Behavior
onPreToolUseandonPostToolUsehooks should be invoked for every tool call in the session, including tool calls made by sub-agents spawned viatask. The hook contract should be transitive — if a parent session registers security hooks, all sub-agents operating within that session should inherit them.Actual Behavior
Hooks are only invoked for direct tool calls in the parent agent. When the
tasktool spawns a sub-agent, that sub-agent's tool calls bypass hooks entirely while still emittingtool.execution_start/tool.execution_completeevents (confirming the tools ran).