What happened?
When running gemini-cli in non-interactive mode (using the -p or --prompt flag), the Policy Engine ignores "allow" decisions for specific tools, even when a high-priority rule is explicitly defined. This is because the CLI's configuration layer applies a hardcoded "extra exclude" list to tools like run_shell_command in auto_edit mode when interaction is disabled, which prevents the tool from even being registered, regardless of the Policy Engine's rules.
What did you expect to happen?
The Policy Engine should be the source of truth for tool availability. If a policy explicitly allows a tool (or a specific command prefix), it should be available for execution even in non-interactive mode.
Client information
Client Information
- **CLI Version:** 0.30.0-nightly.20260210.a2174751d
- **Git Commit:** ac454bc
- **OS:** Linux
- **Approval Mode:** auto_edit
- **Interface:** Non-interactive (-p flag)
Login information
API key
Anything else we need to know?
Steps to Reproduce
- Create a policy file named
repro-policy.toml:
[[rule]]
toolName = "run_shell_command"
commandPrefix = "cargo"
decision = "allow"
priority = 100
- Create a fake responses file
repro-responses.jsonl:
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I will check cargo options."},{"functionCall":{"args":{"command":"cargo --help"},"name":"run_shell_command"}}]},"finishReason":"STOP"}]}]}
- Run the CLI in non-interactive mode with the policy and fake responses:
npm run start --
--model gemini-2.0-flash
--fake-responses ./repro-responses.jsonl
--policy ./repro-policy.toml
--approval-mode auto_edit
-p "describe the options to the cargo command"
- Observe the error:
Error executing tool run_shell_command: Tool "run_shell_command" not found. Did you mean one of: "get_sheet_content", "read_document", "grep_search"?
Further Research
Behavior Across Approval Modes (Non-interactive)
Testing with a custom allow policy for run_shell_command shows inconsistent behavior across modes:
| Mode |
Result |
Reasoning |
yolo |
Success |
Explicitly bypasses hardcoded excludes. |
auto_edit |
Failure |
Hardcoded as excluded in packages/cli/src/config/config.ts. |
default |
Failure |
Part of defaultExcludes in non-interactive mode. |
plan |
Failure |
Part of defaultExcludes in non-interactive mode. |
Root Cause Analysis
It appears that the Config class in packages/core/src/config/config.ts treats hardcoded exclusions and Policy Engine exclusions as a simple union. There is no mechanism for the Policy Engine to "re-allow" a tool that has been statically excluded by the CLI.
When ToolRegistry initializes, it calls config.getExcludeTools(). The implementation is as follows:
// packages/core/src/config/config.ts
getExcludeTools(...): Set<string> | undefined {
// 1. Start with hardcoded excludes from settings/CLI flags
const excludeToolsSet = new Set([...(this.excludeTools ?? [])]);
// 2. Add exclusions from extensions ...
// 3. Add exclusions from the Policy Engine
const policyExclusions = this.policyEngine.getExcludedTools(toolMetadata, allToolNames);
for (const tool of policyExclusions) {
excludeToolsSet.add(tool);
}
return excludeToolsSet;
}
Because run_shell_command is added to this.excludeTools by the CLI's configuration logic (in packages/cli/src/config/config.ts), it is present in the set before the Policy Engine is consulted. Even if the Policy Engine has a high-priority allow rule, it simply results in the tool not being added to policyExclusions. It cannot remove the tool from the already-populated excludeToolsSet.
As a result, the tool is never registered in the ToolRegistry, leading to the "Tool not found" error during execution.
Workarounds
I can use --allowed-tools, but that's deprecated. It'd just be nice if this worked before we remove that.
What happened?
When running
gemini-cliin non-interactive mode (using the-por--promptflag), the Policy Engine ignores "allow" decisions for specific tools, even when a high-priority rule is explicitly defined. This is because the CLI's configuration layer applies a hardcoded "extra exclude" list to tools likerun_shell_commandinauto_editmode when interaction is disabled, which prevents the tool from even being registered, regardless of the Policy Engine's rules.What did you expect to happen?
The Policy Engine should be the source of truth for tool availability. If a policy explicitly allows a tool (or a specific command prefix), it should be available for execution even in non-interactive mode.
Client information
Client Information
- **CLI Version:** 0.30.0-nightly.20260210.a2174751d - **Git Commit:** ac454bc - **OS:** Linux - **Approval Mode:** auto_edit - **Interface:** Non-interactive (-p flag)Login information
API key
Anything else we need to know?
Steps to Reproduce
repro-policy.toml:repro-responses.jsonl:{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I will check cargo options."},{"functionCall":{"args":{"command":"cargo --help"},"name":"run_shell_command"}}]},"finishReason":"STOP"}]}]}npm run start -- --model gemini-2.0-flash --fake-responses ./repro-responses.jsonl --policy ./repro-policy.toml --approval-mode auto_edit -p "describe the options to the cargo command"Further Research
Behavior Across Approval Modes (Non-interactive)
Testing with a custom
allowpolicy forrun_shell_commandshows inconsistent behavior across modes:yoloauto_editpackages/cli/src/config/config.ts.defaultdefaultExcludesin non-interactive mode.plandefaultExcludesin non-interactive mode.Root Cause Analysis
It appears that the
Configclass inpackages/core/src/config/config.tstreats hardcoded exclusions and Policy Engine exclusions as a simple union. There is no mechanism for the Policy Engine to "re-allow" a tool that has been statically excluded by the CLI.When
ToolRegistryinitializes, it callsconfig.getExcludeTools(). The implementation is as follows:Because
run_shell_commandis added tothis.excludeToolsby the CLI's configuration logic (inpackages/cli/src/config/config.ts), it is present in the set before the Policy Engine is consulted. Even if the Policy Engine has a high-priorityallowrule, it simply results in the tool not being added topolicyExclusions. It cannot remove the tool from the already-populatedexcludeToolsSet.As a result, the tool is never registered in the
ToolRegistry, leading to the "Tool not found" error during execution.Workarounds
I can use
--allowed-tools, but that's deprecated. It'd just be nice if this worked before we remove that.