fix(tui): default interactive Agent shell to approval-gated on#3756
fix(tui): default interactive Agent shell to approval-gated on#3756Hmbown wants to merge 2 commits into
Conversation
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
|
Claude finished @Hmbown's task in 2m 10s —— View job PR Review — fix(tui): default interactive Agent shell to approval-gated on
SummaryClean, focused change. The logic is correct and the critical GHSA-72w5-pf8h-xfp4 invariants are preserved: CorrectnessGood: The bifurcation is right.
One subtle edge case worth documenting (not a bug, just a potential surprise for upgrading users): TestsCoverage is solid for the new behavior:
Missing but not blocking: No test verifies the full integrated path in SecurityNo new attack surface. Shell tools remain approval-gated in interactive mode (this doesn't add auto-approval). The change only moves shell from hidden to visible-but-gated in the interactive TUI, which is the intended UX improvement. Docs (
|
Stamp approved tool results with model-visible approval text and structured metadata so Agent mode can report that shell execution was user-approved instead of inferring from successful output alone. Covers both normal model tool calls and direct composer shell execution. 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 approval_stamp 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
|
Claude finished @Hmbown's task in 2m 59s —— View job PR Review — fix(tui): default interactive Agent shell to approval-gated on
This sync added a second commit ( baa4b4b — default interactive shell to approval-gated onNo new concerns. The bifurcation is correct:
The three branches of 50c6bcc — surface approval decisions in tool resultsThree call sites correctly stamp results:
These are separate execution paths; no double-stamp risk. Issue — metadata update is not idempotent but content guard is ( // metadata written unconditionally
let metadata = result.metadata.get_or_insert_with(|| json!({}));
if let Some(object) = metadata.as_object_mut() {
object.insert("approval".to_string(), approval_metadata); // always runs
}
// ...
if result.content.starts_with("[approval] ") {
return; // only content update is skipped
}If Minor — After Missing test coverage:
SecurityNo new attack surface. The change moves shell from hidden to visible-but-gated in interactive TUI (baa4b4b) and adds model-visible approval attribution to results (50c6bcc). Neither widens the actual execution surface. VerdictApprove. Both commits are functionally correct and the critical GHSA invariants are preserved. The issues above are non-blocking:
|
|
Heads-up from 0.8.66 release prep: this PR is bundled into the local While verifying it I found one internal inconsistency worth folding in before merge: The durable Agent permission baseline doesn't follow the new interactive default. In agent_allow_shell: if matches!(initial_mode, AppMode::Auto | AppMode::Yolo) {
config.allow_shell() // hard-defaults false (GHSA-72w5-pf8h-xfp4)
} else {
allow_shell // resolved interactive default (now true)
},
Suggested fix (keeps the security floor: explicit // crates/tui/src/config.rs
/// Interactive TUI Agent default: shell on, approval-gated. Honors an
/// explicit `allow_shell = false`. Single source of truth for startup
/// (run_interactive) and the durable Agent baseline (app.rs).
pub fn interactive_allow_shell(&self) -> bool { self.allow_shell.unwrap_or(true) }
All other I've implemented and tested exactly this in the 0.8.66 test build (added |
Summary
Verification