Fix direct-provider command monitoring
This commit is contained in:
@@ -190,23 +190,6 @@ impl ShellCommandExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Decorate the command so that we can turn off pager.
|
||||
fn turn_off_pager_for_command(&self, command: &String, ctx: &mut ModelContext<Self>) -> String {
|
||||
match self.active_session.as_ref(ctx).shell_type(ctx) {
|
||||
// If it's a posix shell, we can use parentheses as the grouping character. Add command to
|
||||
// avoid cases with aliases.
|
||||
Some(ShellType::Zsh) | Some(ShellType::Bash) => format!("({command}) | command cat"),
|
||||
// Fish doesn't have grouping characters. We need to use begin; and end; to ensure the command
|
||||
// gets evaluated first.
|
||||
Some(ShellType::Fish) => format!("begin; {command} ;end | command cat"),
|
||||
// For powershell, we use Out-Host to send paged output to the
|
||||
// console. Add a backslash to avoid executing an alias.
|
||||
Some(ShellType::PowerShell) => format!("({command}) | \\Out-Host"),
|
||||
// If we can't determine a shell type, run command as it is.
|
||||
None => command.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn execute(
|
||||
&mut self,
|
||||
input: ExecuteActionInput,
|
||||
@@ -231,7 +214,6 @@ impl ShellCommandExecutor {
|
||||
match &input.action.action {
|
||||
AIAgentActionType::RequestCommandOutput {
|
||||
command,
|
||||
uses_pager,
|
||||
wait_until_completion,
|
||||
..
|
||||
} => {
|
||||
@@ -266,15 +248,13 @@ impl ShellCommandExecutor {
|
||||
RequestCommandOutputResult::CancelledBeforeExecution,
|
||||
));
|
||||
}
|
||||
// If the command might use pager and can't be interacted with,
|
||||
// we pipe its output to cat so we can prevent activating the altscreen.
|
||||
// The parentheses here ensures the command always gets evaluated first.
|
||||
let decorated_command =
|
||||
if uses_pager.is_some_and(|uses_pager| uses_pager) && *wait_until_completion {
|
||||
self.turn_off_pager_for_command(command, ctx)
|
||||
} else {
|
||||
command.clone()
|
||||
};
|
||||
// A command expected to finish must not enter an implicit pager. Do not trust the
|
||||
// model-provided pager hint: commands such as `git log` can page implicitly.
|
||||
let decorated_command = command_for_execution(
|
||||
command,
|
||||
self.active_session.as_ref(ctx).shell_type(ctx),
|
||||
*wait_until_completion,
|
||||
);
|
||||
ctx.emit(ShellCommandExecutorEvent::ExecuteCommand {
|
||||
action_id: action_id.clone(),
|
||||
command: decorated_command,
|
||||
@@ -710,6 +690,30 @@ impl ShellCommandExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
fn command_for_execution(
|
||||
command: &str,
|
||||
shell_type: Option<ShellType>,
|
||||
wait_until_completion: bool,
|
||||
) -> String {
|
||||
if !wait_until_completion {
|
||||
return command.to_string();
|
||||
}
|
||||
|
||||
match shell_type {
|
||||
// Pager environment variables preserve the command's output and exit status, unlike piping
|
||||
// through `cat`. Tool-specific variables override user configuration for common pagers.
|
||||
Some(ShellType::Zsh) | Some(ShellType::Bash) => format!(
|
||||
"(export PAGER=cat GIT_PAGER=cat GH_PAGER=cat AWS_PAGER=cat SYSTEMD_PAGER=cat; {command})"
|
||||
),
|
||||
Some(ShellType::Fish) => format!(
|
||||
"begin; set -lx PAGER cat; set -lx GIT_PAGER cat; set -lx GH_PAGER cat; set -lx AWS_PAGER cat; set -lx SYSTEMD_PAGER cat; {command}; end"
|
||||
),
|
||||
// PowerShell's pipeline host suppresses paging for commands that honor the host stream.
|
||||
Some(ShellType::PowerShell) => format!("({command}) | \\Out-Host"),
|
||||
None => command.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
enum BlockSelector {
|
||||
Id(BlockId),
|
||||
|
||||
@@ -5,7 +5,7 @@ use futures::channel::oneshot;
|
||||
use parking_lot::FairMutex;
|
||||
use warpui::{App, EntityId};
|
||||
|
||||
use super::{ActionResult, BlockSelector, ShellCommandExecutor};
|
||||
use super::{command_for_execution, ActionResult, BlockSelector, ShellCommandExecutor};
|
||||
use crate::ai::agent::ShellCommandDelay;
|
||||
use crate::terminal::event::{BlockMetadataReceivedEvent, BlockWorkingDirectoryUpdatedEvent};
|
||||
use crate::terminal::model::block::{BlockId, BlockMetadata};
|
||||
@@ -13,6 +13,23 @@ use crate::terminal::model::session::active_session::ActiveSession;
|
||||
use crate::terminal::model::session::Sessions;
|
||||
use crate::terminal::model::terminal_model::{BlockIndex, TerminalModel};
|
||||
use crate::terminal::model_events::{ModelEvent, ModelEventDispatcher};
|
||||
use crate::terminal::shell::ShellType;
|
||||
|
||||
#[test]
|
||||
fn wait_commands_disable_implicit_posix_pagers_without_masking_exit_status() {
|
||||
let command = "git log -8 --oneline && false";
|
||||
let decorated = command_for_execution(command, Some(ShellType::Zsh), true);
|
||||
|
||||
assert_eq!(
|
||||
decorated,
|
||||
"(export PAGER=cat GIT_PAGER=cat GH_PAGER=cat AWS_PAGER=cat SYSTEMD_PAGER=cat; git log -8 --oneline && false)"
|
||||
);
|
||||
assert!(!decorated.contains("| command cat"));
|
||||
assert_eq!(
|
||||
command_for_execution(command, Some(ShellType::Zsh), false),
|
||||
command
|
||||
);
|
||||
}
|
||||
|
||||
/// Locks in the contract that `ShellCommandExecutor`'s requested-command finish
|
||||
/// detector reacts only to `BlockMetadataReceived` (precmd) and not to
|
||||
|
||||
Reference in New Issue
Block a user