diff --git a/tests/tools/test_delegate.py b/tests/tools/test_delegate.py index e41137c14d1..468fbdaf942 100644 --- a/tests/tools/test_delegate.py +++ b/tests/tools/test_delegate.py @@ -2032,6 +2032,32 @@ class TestOrchestratorRoleSchema(unittest.TestCase): self.assertIn("role", task_props) self.assertEqual(task_props["role"]["enum"], ["leaf", "orchestrator"]) + def test_acp_command_description_has_do_not_set_guidance(self): + # acp_command/acp_args descriptions must NOT bias the model toward + # assuming an ACP CLI (Claude, Copilot, etc.) is installed. They must + # carry explicit "do not set unless told" guidance so the model doesn't + # hallucinate ACP availability (#22013). + from tools.delegate_tool import DELEGATE_TASK_SCHEMA + props = DELEGATE_TASK_SCHEMA["parameters"]["properties"] + + top_acp_desc = props["acp_command"]["description"] + self.assertIn("Do NOT set", top_acp_desc) + self.assertIn("explicitly told you", top_acp_desc) + + task_props = props["tasks"]["items"]["properties"] + per_task_acp_desc = task_props["acp_command"]["description"] + self.assertIn("Do NOT set", per_task_acp_desc) + + def test_acp_command_description_has_no_claude_as_example(self): + # Descriptions must not list 'claude' as a canonical example value — + # that directly primes the model to attempt Claude ACP even when it is + # not installed (#22013). + from tools.delegate_tool import DELEGATE_TASK_SCHEMA + props = DELEGATE_TASK_SCHEMA["parameters"]["properties"] + top_acp_desc = props["acp_command"]["description"].lower() + self.assertNotIn("e.g. 'claude'", top_acp_desc) + self.assertNotIn("e.g. \"claude\"", top_acp_desc) + # Sentinel used to distinguish "role kwarg omitted" from "role=None". _SENTINEL = object() diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index e0511eeb647..b6b2dd443f3 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -2681,12 +2681,16 @@ DELEGATE_TASK_SCHEMA = { }, "acp_command": { "type": "string", - "description": "Per-task ACP command override (e.g. 'copilot'). Overrides the top-level acp_command for this task only.", + "description": ( + "Per-task ACP command override (e.g. 'copilot'). " + "Overrides the top-level acp_command for this task only. " + "Do NOT set unless the user explicitly told you an ACP CLI is installed." + ), }, "acp_args": { "type": "array", "items": {"type": "string"}, - "description": "Per-task ACP args override.", + "description": "Per-task ACP args override. Leave empty unless acp_command is set.", }, "role": { "type": "string", @@ -2713,7 +2717,10 @@ DELEGATE_TASK_SCHEMA = { "When set, children use ACP subprocess transport instead of inheriting " "the parent's transport. Requires an ACP-compatible CLI " "(currently GitHub Copilot CLI via 'copilot --acp --stdio'). " - "See agent/copilot_acp_client.py for the implementation." + "See agent/copilot_acp_client.py for the implementation. " + "IMPORTANT: Do NOT set this unless the user has explicitly told you " + "a specific ACP-compatible CLI is installed and configured. " + "Leave empty to use the parent's default transport (Hermes subagents)." ), }, "acp_args": { @@ -2721,7 +2728,8 @@ DELEGATE_TASK_SCHEMA = { "items": {"type": "string"}, "description": ( "Arguments for the ACP command (default: ['--acp', '--stdio']). " - "Only used when acp_command is set." + "Only used when acp_command is set. " + "Leave empty unless acp_command is explicitly provided." ), }, },