Skip to content

PreToolUse hook block is ignored for invoke_subagent (a denied sub-agent spawns anyway) #640

Description

@collie-script

Summary

A PreToolUse hook that returns {"decision": "block"} reliably stops normal tools
(write_to_file, run_command, ...). It does not stop invoke_subagent: the hook
command is executed, its stdout returns block, and the sub-agent is created regardless.

Since hooks are the only enforcement mechanism available (there is no flag to disable
sub-agent spawning, cf. #241), this means sub-agent creation currently cannot be
restricted at all in non-interactive runs.

Environment

  • agy --version -> 1.1.4 (Linux x86_64; also reproduced on 1.1.1)
  • Non-interactive: agy -p "<prompt>" --dangerously-skip-permissions --add-dir <dir>
  • The hook lives in <dir>/.agents/hooks.json with an explicit timeout:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "*",
        "hooks": [{ "type": "command", "command": "node /path/to/hook.js", "timeout": 2700 }]
      }
    ]
  }
}

The hook command prints, on stdout:

{ "decision": "block", "reason": "This tool is not available here." }

Steps to reproduce

  1. Install the hook above (matcher *), returning block when
    toolCall.name === "invoke_subagent" and allow for everything else.
  2. Run a non-interactive turn that asks the model to spawn a sub-agent:
    agy -p 'Use invoke_subagent to spawn a subagent that replies GATE_TEST_3131.'

Expected

invoke_subagent is refused and the model is told why — the same behaviour every other
blocked tool gets.

Actual

The hook command runs (its own logging confirms it was invoked with the
invoke_subagent call) and answers block — and the CLI creates the sub-agent anyway.
The transcript shows:

INVOKE_SUBAGENT  Created the following subagents:
{ "conversationId": "8c3e6bef-8dad-4ec5-81a5-dd2eadb33a8a", ... }

and the model itself reports: "The native invoke_subagent tool was not refused;
the call succeeded and successfully spawned the subagent."

Control test (the hook itself works)

Same hook, same session, same --dangerously-skip-permissions, returning the identical
block for a file write:

write_to_file -> blocked
model: "The attempt to write the file /tmp/... was refused. When calling the
        write_to_file tool, the execution was blocked by the system"

...and the file is genuinely not created. So the hook is wired correctly and its block
is honoured — for invoke_subagent specifically it is not.

The same bypass is observed for define_subagent / manage_subagents.

Suggested fix (either would solve it)

  1. Honour PreToolUse block for invoke_subagent / define_subagent /
    manage_subagents, exactly as for other tools; or
  2. the flag requested in Feature request: disable subagent spawning (single-agent mode) for bounded/sandboxed -p runs #241 — a way to disable sub-agent spawning for a run.

Related: #528, #241, #156.

Metadata

Metadata

Assignees

Labels

1.1.4Issues related to 1.1.4comp:customizationsPlugins & Skills, MCP, Status Line, Window Titlesubtype:ubuntu/LinuxUbuntu/Linux build, execution, or keybinding issues

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions