fix(tui): default interactive Agent shell to approval-gated on#3756
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 |
Interactive Agent sessions now expose shell tools by default so approval prompts can gate commands, but Auto/YOLO startup still captured the durable Agent baseline through Config::allow_shell(), which defaults false for noninteractive safety. That made a YOLO-start session downshift to Agent with shell hidden unless the user had explicitly opted in. Add Config::interactive_allow_shell() as the shared interactive resolver and use it from both run_interactive and App::new's Agent baseline. Durable/background call sites continue to use Config::allow_shell() and keep the conservative default. Tests: cargo fmt --all --check Tests: git diff --check Tests: cargo test -p codewhale-tui --bin codewhale-tui --locked interactive_allow_shell_defaults_to_true_but_honors_explicit_opt_out 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 yolo_start_with_default_config_restores_interactive_agent_shell_baseline Tests: cargo test -p codewhale-tui --bin codewhale-tui --locked leaving_yolo_after_startup_restores_baseline_policies
|
Claude finished @Hmbown's task in 2m 48s —— View job PR Review — fix(tui): default interactive Agent shell to approval-gated on
This sync added Summary of the full stack
df16415 — correctness (the new commit)Fix is correct. The // Before (baa4b4b only):
agent_allow_shell: if matches!(initial_mode, AppMode::Auto | AppMode::Yolo) {
config.allow_shell() // → false for default Config — wrong
} else {
allow_shell // → true from interactive_tui_allow_shell
},
// After (df16415):
agent_allow_shell: if matches!(initial_mode, AppMode::Auto | AppMode::Yolo) {
config.interactive_allow_shell() // → true for default Config — correct
} else {
allow_shell // unchanged
},A YOLO/Auto-start session now restores the correct interactive default when downshifting to Agent. The
TestsNew tests added in df16415:
Minor gap (non-blocking): No test covers YOLO-start + Pre-existing issues (from 50c6bcc, not worsened by df16415)
The The metadata is written unconditionally while the content prepend is guarded by
Missing GHSA invariantsAll three critical call-site classes are correctly bifurcated:
VerdictApprove. The fix for the YOLO→Agent baseline drift (df16415) is correct, the GHSA invariants are preserved across all three commits, test coverage is solid for new behavior, and the docs accurately reflect the new interactive default. The remaining open items are all non-blocking: the idempotency ordering in |
docs(shell): align CONFIGURATION/TOOL_SURFACE/error message with shell default (#3756 follow-up)
…l default Hmbown#3756 flipped interactive Agent shell to on-by-default (approval-gated) and updated MODES.md, but CONFIGURATION.md, TOOL_SURFACE.md, and the exec_shell "unavailable" reason / allow_shell hint in tool_catalog.rs still described the old opt-in model. Post-merge `main` therefore had docs that contradicted both the runtime and MODES.md. Align all three with the shipped behavior: interactive TUI Agent exposes shell by default with approval prompts unless `allow_shell = false`; headless/durable/ noninteractive profiles keep the conservative omitted-field default and require `allow_shell = true`; Plan mode hides shell; YOLO enables shell + auto-approval. Behavior unchanged (docs + user-facing message only). Existing shell-message tests still pass (the improved message keeps the asserted substrings). Completes the documentation for Hmbown#3756.
Summary
Verification