fix(tui): default interactive Agent shell to approval-gated on

Interactive TUI sessions should expose shell tools in Agent mode so normal approval prompts can gate command execution. Keep the GHSA-safe Config::allow_shell() default for durable tasks and explicit allow_shell=false overrides, and document the distinction in the mode guide.

Tests: cargo fmt --all --check

Tests: git diff --check

Tests: ./scripts/release/check-versions.sh

Tests: cargo test -p codewhale-tui --bin codewhale-tui --locked interactive_tui_defaults_agent_shell_to_approval_gated_on

Tests: cargo test -p codewhale-tui --bin codewhale-tui --locked allow_shell_defaults_to_false_when_unset

Tests: cargo test -p codewhale-tui --bin codewhale-tui --locked add_task_without_optional_fields_does_not_grant_shell_or_auto_approve

Tests: cargo test -p codewhale-tui --bin codewhale-tui --locked build_tool_context_uses_typed_shell_policy_per_mode

Tests: cargo test -p codewhale-tui --bin codewhale-tui --locked agent_tools_with_allow_shell
This commit is contained in:
Hunter B committed 2026-06-28 18:36:13 -07:00
1 parent 0fd9515922
commit baa4b4b73f
2 files changed
+40 -8

No files matched your search

+34 -2
View File
@@ -1420,7 +1420,7 @@ async fn main() -> Result<()> {
};
// Default: Interactive TUI
// --yolo starts in YOLO mode (auto-approve; shell if allow_shell=true)
// --yolo starts in YOLO mode (auto-approve; shell enabled)
run_interactive(&cli, &config, resume_session_id, None).await
}
@@ -6409,6 +6409,10 @@ fn normalize_windows_config_path_str(path: &str) -> String {
normalized.to_ascii_lowercase()
}
fn interactive_tui_allow_shell(yolo: bool, config: &Config) -> bool {
yolo || config.allow_shell.unwrap_or(true)
}
async fn run_interactive(
cli: &Cli,
config: &Config,
@@ -6513,7 +6517,7 @@ async fn run_interactive(
workspace,
config_path: cli.config.clone(),
config_profile: cli.profile.clone(),
allow_shell: yolo || config.allow_shell(),
allow_shell: interactive_tui_allow_shell(yolo, config),
use_alt_screen,
use_mouse_capture,
use_bracketed_paste,
@@ -8820,6 +8824,34 @@ mod terminal_mode_tests {
}
}
#[cfg(test)]
mod interactive_startup_tests {
use super::*;
#[test]
fn interactive_tui_defaults_agent_shell_to_approval_gated_on() {
let default_config = Config::default();
assert!(
interactive_tui_allow_shell(false, &default_config),
"interactive Agent mode should expose shell tools by default so approvals can gate commands"
);
let disabled = Config {
allow_shell: Some(false),
..Config::default()
};
assert!(
!interactive_tui_allow_shell(false, &disabled),
"explicit allow_shell=false still hides shell tools"
);
assert!(
interactive_tui_allow_shell(true, &disabled),
"YOLO forces shell access for its no-guardrails contract"
);
}
}
#[cfg(test)]
mod project_config_tests {
use super::*;
+6 -6
View File
@@ -28,7 +28,7 @@ Run `/mode` to open the mode picker, or switch directly with `/mode agent`,
`/mode plan`, `/mode yolo`, `/mode 1`, `/mode 2`, or `/mode 3`.
- **Plan**: design-first prompting. Read-only investigation tools stay available; shell and patch execution stay off. Use this when you want to think out loud and produce a plan to hand to a human (yourself later, or a reviewer).
- **Agent**: multi-step tool use. Shell execution (`exec_shell`, `task_shell_start`, `task_shell_wait`) requires `allow_shell = true` in config; approval prompts gate each call. File writes are allowed without a prompt.
- **Agent**: multi-step tool use. In interactive TUI sessions, shell tools (`exec_shell`, `task_shell_start`, `task_shell_wait`) are available by default and approval prompts gate each call. Set top-level `allow_shell = false` to hide shell tools for a workspace/profile. File writes are allowed without a prompt.
- **YOLO**: enables shell + trust mode and auto-approves all tools. Use only in trusted repos.
### Tool availability by mode
@@ -37,15 +37,15 @@ Run `/mode` to open the mode picker, or switch directly with `/mode agent`,
|:---|:---:|:---:|:---:|
| Read-only file, search, and diagnostic tools | yes | yes | yes |
| File write and patch tools | no | yes | yes |
| Shell tools (`exec_shell`, `task_shell_start`, waits, interact, cancel) | no | approval-gated, when `allow_shell = true` | yes |
| Shell tools (`exec_shell`, `task_shell_start`, waits, interact, cancel) | no | approval-gated by default, hidden when `allow_shell = false` | yes |
| Paid or external-service tools | approval-gated | approval-gated | auto-approved |
| Access outside the workspace root | no | only with trust mode | yes |
If a shell tool is missing from the model-visible catalog in Agent mode, check
`allow_shell` first. The setting can come from the active config/profile or from
the runtime session. YOLO turns shell access on together with trust mode and
auto-approval, which is why shell commands may work there even when the Agent
mode catalog does not list them.
for an explicit `allow_shell = false` in the active config/profile or runtime
session. Durable tasks and automation keep conservative omitted-field defaults;
they only receive shell access when their task settings explicitly grant it.
YOLO turns shell access on together with trust mode and auto-approval.
All action-capable modes have access to persistent RLM sessions through `rlm_open`, `rlm_eval`, `rlm_configure`, and `rlm_close`. Inside an RLM Python REPL, `sub_query_batch` fans out 1-16 cheap parallel child calls pinned to `deepseek-v4-flash`. The model reaches for it when work is too large or repetitive for the parent transcript.