mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(agents): block subagent spawning at the Claude Code level (disallow Task) (#377)
The fleet-wide subagent ban was implemented as an allowlist omission, but Task is a default-permitted Claude Code built-in — an allowlist auto-approves, it does not restrict. Under permission_mode="dontAsk" (intake/secretary SDK) and defaultMode="bypassPermissions" (fleet), Task ran regardless and can_use_tool was never invoked for it, so every Claude-path agent could still spawn subagents despite allows_subagent=False. Only the grok path blocked it. Explicitly disallow the subagent tool at every Claude-path spawn point: disallowed_tools=["Task"] on the intake and secretary SDK drivers, and "Task" in the fleet settings.json base_deny (an explicit deny applies even under bypassPermissions). This mirrors the grok path's --disallowed-tools Agent. Pins the ban in test_cc_lockdown.py (fleet settings deny Task) and a new test_sdk_driver_subagent_ban.py (intake + secretary options disallow Task). Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
"""The SDK-driver agents (intake, secretary) hard-disallow the `Task` tool.
|
||||
|
||||
`Task` is a default-permitted Claude Code built-in: omitting it from
|
||||
`allowed_tools` only removes an auto-approve entry, it does not restrict, and
|
||||
`permission_mode="dontAsk"` never routes a pre-permitted built-in through the
|
||||
`can_use_tool` gate. So the ONLY claude-code-level block is an explicit
|
||||
`disallowed_tools=["Task"]` (→ CLI `--disallowedTools Task`). These pin that
|
||||
the intake interviewer and the Secretary cannot fan out subagents.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from roboco.agent_sdk.intake_driver import build_intake_options
|
||||
from roboco.agent_sdk.secretary_driver import build_secretary_options
|
||||
|
||||
|
||||
def test_intake_options_disallow_task() -> None:
|
||||
opts = build_intake_options(
|
||||
system_prompt="x", cwd="/tmp", session_id="s1", model="sonnet"
|
||||
)
|
||||
assert "Task" in opts.disallowed_tools
|
||||
assert "Task" not in opts.allowed_tools
|
||||
|
||||
|
||||
def test_secretary_options_disallow_task() -> None:
|
||||
opts = build_secretary_options(system_prompt="x", cwd="/tmp", model="sonnet")
|
||||
assert "Task" in opts.disallowed_tools
|
||||
assert "Task" not in opts.allowed_tools
|
||||
Reference in New Issue
Block a user