feat(autonomy): exclude process by default for non-cli channels

This commit is contained in:
argenis de la rosa 2026-03-01 14:53:45 -05:00 committed by Argenis
parent 0787a9cebe
commit cc9ff1820b
4 changed files with 55 additions and 1 deletions

View File

@ -78,6 +78,7 @@ Notes:
- You can restrict who can use approval-management commands via `[autonomy].non_cli_approval_approvers`.
- Configure natural-language approval mode via `[autonomy].non_cli_natural_language_approval_mode`.
- `autonomy.non_cli_excluded_tools` is reloaded from `config.toml` at runtime; `/approvals` shows the currently effective list.
- Default non-CLI exclusions include both `shell` and `process`; remove `process` from `[autonomy].non_cli_excluded_tools` only when you explicitly want background command execution in chat channels.
- Each incoming message injects a runtime tool-availability snapshot into the system prompt, derived from the same exclusion policy used by execution.
## Inbound Image Marker Protocol

View File

@ -868,7 +868,7 @@ Environment overrides:
| `allow_sensitive_file_writes` | `false` | allow `file_write`/`file_edit` on sensitive files/dirs (for example `.env`, `.aws/credentials`, private keys) |
| `auto_approve` | `[]` | tool operations always auto-approved |
| `always_ask` | `[]` | tool operations that always require approval |
| `non_cli_excluded_tools` | `[]` | tools hidden from non-CLI channel tool specs |
| `non_cli_excluded_tools` | built-in denylist (includes `shell`, `process`, `file_write`, ...) | tools hidden from non-CLI channel tool specs |
| `non_cli_approval_approvers` | `[]` | optional allowlist for who can run non-CLI approval-management commands |
| `non_cli_natural_language_approval_mode` | `direct` | natural-language behavior for approval-management commands (`direct`, `request_confirm`, `disabled`) |
| `non_cli_natural_language_approval_mode_by_channel` | `{}` | per-channel override map for natural-language approval mode |
@ -908,6 +908,7 @@ Notes:
- `telegram:alice` allows only that channel+sender pair.
- `telegram:*` allows any sender on Telegram.
- `*:alice` allows `alice` on any channel.
- By default, `process` is excluded on non-CLI channels alongside `shell`. To opt in intentionally, remove `"process"` from `[autonomy].non_cli_excluded_tools` in `config.toml`.
- Use `/unapprove <tool>` to remove persisted approval from `autonomy.auto_approve`.
- `/approve-pending` lists pending requests for the current sender+chat/channel scope.
- If a tool remains unavailable after approval, check `autonomy.non_cli_excluded_tools` (runtime `/approvals` shows this list). Channel runtime reloads this list from `config.toml` automatically.

View File

@ -6747,6 +6747,36 @@ BTC is currently around $65,000 based on latest tool output."#
}
}
struct MockProcessTool;
#[async_trait::async_trait]
impl Tool for MockProcessTool {
fn name(&self) -> &str {
"process"
}
fn description(&self) -> &str {
"Mock process tool for runtime visibility tests"
}
fn parameters_schema(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"action": { "type": "string" }
}
})
}
async fn execute(&self, _args: serde_json::Value) -> anyhow::Result<ToolResult> {
Ok(ToolResult {
success: true,
output: String::new(),
error: None,
})
}
}
#[test]
fn build_runtime_tool_visibility_prompt_respects_excluded_snapshot() {
let tools: Vec<Box<dyn Tool>> = vec![Box::new(MockPriceTool), Box::new(MockEchoTool)];
@ -6765,6 +6795,23 @@ BTC is currently around $65,000 based on latest tool output."#
assert!(!native.contains("## Tool Use Protocol"));
}
#[test]
fn build_runtime_tool_visibility_prompt_excludes_process_with_default_policy() {
let tools: Vec<Box<dyn Tool>> = vec![Box::new(MockProcessTool), Box::new(MockEchoTool)];
let excluded = crate::config::AutonomyConfig::default().non_cli_excluded_tools;
assert!(
excluded.contains(&"process".to_string()),
"default non-CLI exclusion list must include process"
);
let prompt = build_runtime_tool_visibility_prompt(&tools, &excluded, false);
assert!(prompt.contains("Excluded by runtime policy:"));
assert!(prompt.contains("process"));
assert!(!prompt.contains("**process**:"));
assert!(prompt.contains("`mock_echo`"));
}
#[tokio::test]
async fn process_channel_message_injects_runtime_tool_visibility_prompt() {
let channel_impl = Arc::new(RecordingChannel::default());

View File

@ -3320,6 +3320,7 @@ fn default_always_ask() -> Vec<String> {
fn default_non_cli_excluded_tools() -> Vec<String> {
[
"shell",
"process",
"file_write",
"file_edit",
"git_operations",
@ -9427,6 +9428,7 @@ mod tests {
assert!(!a.allow_sensitive_file_reads);
assert!(!a.allow_sensitive_file_writes);
assert!(a.non_cli_excluded_tools.contains(&"shell".to_string()));
assert!(a.non_cli_excluded_tools.contains(&"process".to_string()));
assert!(a.non_cli_excluded_tools.contains(&"delegate".to_string()));
}
@ -9460,6 +9462,9 @@ allowed_roots = []
"Missing command_context_rules must default to empty"
);
assert!(parsed.non_cli_excluded_tools.contains(&"shell".to_string()));
assert!(parsed
.non_cli_excluded_tools
.contains(&"process".to_string()));
assert!(parsed
.non_cli_excluded_tools
.contains(&"browser".to_string()));