show remote host in exec tui cells

This commit is contained in:
Nathan Liebrecht
2026-04-10 12:57:00 -07:00
parent 6a44fb6309
commit 41e490df30
17 changed files with 239 additions and 43 deletions
@@ -1790,6 +1790,7 @@ mod tests {
cmd: "echo hello world".into(),
}],
source: ExecCommandSource::Agent,
host_id: None,
interaction_input: None,
stdout: String::new(),
stderr: String::new(),
@@ -2007,6 +2008,7 @@ mod tests {
cwd: PathBuf::from("/tmp"),
parsed_cmd: vec![ParsedCommand::Unknown { cmd: "ls".into() }],
source: ExecCommandSource::Agent,
host_id: None,
interaction_input: None,
stdout: String::new(),
stderr: "exec command rejected by user".into(),
@@ -2247,6 +2249,7 @@ mod tests {
cmd: "echo done".into(),
}],
source: ExecCommandSource::Agent,
host_id: None,
interaction_input: None,
stdout: "done\n".into(),
stderr: String::new(),
@@ -2336,6 +2339,7 @@ mod tests {
cmd: "echo done".into(),
}],
source: ExecCommandSource::Agent,
host_id: None,
interaction_input: None,
stdout: "done\n".into(),
stderr: String::new(),
+4
View File
@@ -152,6 +152,7 @@ pub(crate) async fn execute_user_shell_command(
cwd: cwd.to_path_buf(),
parsed_cmd: parsed_cmd.clone(),
source: ExecCommandSource::UserShell,
host_id: None,
interaction_input: None,
}),
)
@@ -220,6 +221,7 @@ pub(crate) async fn execute_user_shell_command(
cwd: cwd.to_path_buf(),
parsed_cmd: parsed_cmd.clone(),
source: ExecCommandSource::UserShell,
host_id: None,
interaction_input: None,
stdout: String::new(),
stderr: aborted_message.clone(),
@@ -244,6 +246,7 @@ pub(crate) async fn execute_user_shell_command(
cwd: cwd.to_path_buf(),
parsed_cmd: parsed_cmd.clone(),
source: ExecCommandSource::UserShell,
host_id: None,
interaction_input: None,
stdout: output.stdout.text.clone(),
stderr: output.stderr.text.clone(),
@@ -288,6 +291,7 @@ pub(crate) async fn execute_user_shell_command(
cwd: cwd.to_path_buf(),
parsed_cmd,
source: ExecCommandSource::UserShell,
host_id: None,
interaction_input: None,
stdout: exec_output.stdout.text.clone(),
stderr: exec_output.stderr.text.clone(),
+19 -25
View File
@@ -61,27 +61,20 @@ pub(crate) enum ToolEventFailure {
Rejected(String),
}
pub(crate) async fn emit_exec_command_begin(
ctx: ToolEventCtx<'_>,
command: &[String],
cwd: &Path,
parsed_cmd: &[ParsedCommand],
source: ExecCommandSource,
interaction_input: Option<String>,
process_id: Option<&str>,
) {
async fn emit_exec_command_begin(ctx: ToolEventCtx<'_>, exec_input: &ExecCommandInput<'_>) {
ctx.session
.send_event(
ctx.turn,
EventMsg::ExecCommandBegin(ExecCommandBeginEvent {
call_id: ctx.call_id.to_string(),
process_id: process_id.map(str::to_owned),
process_id: exec_input.process_id.map(str::to_owned),
turn_id: ctx.turn.sub_id.clone(),
command: command.to_vec(),
cwd: cwd.to_path_buf(),
parsed_cmd: parsed_cmd.to_vec(),
source,
interaction_input,
command: exec_input.command.to_vec(),
cwd: exec_input.cwd.to_path_buf(),
parsed_cmd: exec_input.parsed_cmd.to_vec(),
source: exec_input.source,
host_id: exec_input.host_id.map(str::to_owned),
interaction_input: exec_input.interaction_input.map(str::to_owned),
}),
)
.await;
@@ -105,6 +98,7 @@ pub(crate) enum ToolEmitter {
source: ExecCommandSource,
parsed_cmd: Vec<ParsedCommand>,
process_id: Option<String>,
host_id: Option<String>,
},
}
@@ -137,6 +131,7 @@ impl ToolEmitter {
cwd: PathBuf,
source: ExecCommandSource,
process_id: Option<String>,
host_id: Option<String>,
) -> Self {
let parsed_cmd = parse_command(command);
Self::UnifiedExec {
@@ -145,6 +140,7 @@ impl ToolEmitter {
source,
parsed_cmd,
process_id,
host_id,
}
}
@@ -169,6 +165,7 @@ impl ToolEmitter {
*source,
/*interaction_input*/ None,
/*process_id*/ None,
/*host_id*/ None,
),
stage,
)
@@ -266,6 +263,7 @@ impl ToolEmitter {
source,
parsed_cmd,
process_id,
host_id,
},
stage,
) => {
@@ -278,6 +276,7 @@ impl ToolEmitter {
*source,
/*interaction_input*/ None,
process_id.as_deref(),
host_id.as_deref(),
),
stage,
)
@@ -370,6 +369,7 @@ struct ExecCommandInput<'a> {
source: ExecCommandSource,
interaction_input: Option<&'a str>,
process_id: Option<&'a str>,
host_id: Option<&'a str>,
}
impl<'a> ExecCommandInput<'a> {
@@ -380,6 +380,7 @@ impl<'a> ExecCommandInput<'a> {
source: ExecCommandSource,
interaction_input: Option<&'a str>,
process_id: Option<&'a str>,
host_id: Option<&'a str>,
) -> Self {
Self {
command,
@@ -388,6 +389,7 @@ impl<'a> ExecCommandInput<'a> {
source,
interaction_input,
process_id,
host_id,
}
}
}
@@ -409,16 +411,7 @@ async fn emit_exec_stage(
) {
match stage {
ToolEventStage::Begin => {
emit_exec_command_begin(
ctx,
exec_input.command,
exec_input.cwd,
exec_input.parsed_cmd,
exec_input.source,
exec_input.interaction_input.map(str::to_owned),
exec_input.process_id,
)
.await;
emit_exec_command_begin(ctx, &exec_input).await;
}
ToolEventStage::Success(output)
| ToolEventStage::Failure(ToolEventFailure::Output(output)) => {
@@ -482,6 +475,7 @@ async fn emit_exec_end(
cwd: exec_input.cwd.to_path_buf(),
parsed_cmd: exec_input.parsed_cmd.to_vec(),
source: exec_input.source,
host_id: exec_input.host_id.map(str::to_owned),
interaction_input: exec_input.interaction_input.map(str::to_owned),
stdout: exec_result.stdout,
stderr: exec_result.stderr,
@@ -185,12 +185,13 @@ impl ToolHandler for UnifiedExecHandler {
"exec_command" => {
let cwd = resolve_workdir_base_path(&arguments, &context.turn.cwd)?;
let args: ExecCommandArgs = parse_arguments_with_base_path(&arguments, &cwd)?;
let environment = resolve_exec_environment(
let resolved_environment = resolve_exec_environment(
session.as_ref(),
turn.as_ref(),
args.host_id.as_deref(),
)
.await?;
let environment = resolved_environment.environment;
let default_host_cwd = if environment.is_remote() && args.workdir.is_none() {
environment
.default_cwd()
@@ -232,7 +233,7 @@ impl ToolHandler for UnifiedExecHandler {
)
.map_err(FunctionCallError::RespondToModel)?;
let command_for_display = codex_shell_command::parse_command::shlex_join(&command);
let request_host_id = args.host_id.clone();
let request_host_id = resolved_environment.host_id;
let ExecCommandArgs {
workdir,
@@ -409,11 +410,16 @@ fn emit_unified_exec_tty_metric(session_telemetry: &SessionTelemetry, tty: bool)
);
}
struct ResolvedExecEnvironment {
environment: Arc<codex_exec_server::Environment>,
host_id: Option<String>,
}
async fn resolve_exec_environment(
session: &crate::codex::Session,
turn: &crate::codex::TurnContext,
host_id: Option<&str>,
) -> Result<Arc<codex_exec_server::Environment>, FunctionCallError> {
) -> Result<ResolvedExecEnvironment, FunctionCallError> {
let environment = match host_id {
Some(host_id) => session
.services
@@ -428,8 +434,28 @@ async fn resolve_exec_environment(
None => turn.environment.clone(),
};
environment.ok_or_else(|| {
let environment = environment.ok_or_else(|| {
FunctionCallError::RespondToModel("unified exec is unavailable in this session".to_string())
})?;
let host_id = if environment.is_remote() {
Some(match host_id {
Some(host_id) => host_id.to_string(),
None => {
let default_host_id = session.services.environment_manager.default_host_id();
if default_host_id == codex_exec_server::LOCAL_HOST_ID {
"remote".to_string()
} else {
default_host_id
}
}
})
} else {
None
};
Ok(ResolvedExecEnvironment {
environment,
host_id,
})
}
@@ -110,6 +110,7 @@ pub(crate) fn spawn_exit_watcher(
turn_ref: Arc<TurnContext>,
call_id: String,
command: Vec<String>,
host_id: Option<String>,
cwd: PathBuf,
process_id: i32,
transcript: Arc<Mutex<HeadTailBuffer>>,
@@ -129,6 +130,7 @@ pub(crate) fn spawn_exit_watcher(
turn_ref,
call_id,
command,
host_id,
cwd,
Some(process_id.to_string()),
transcript,
@@ -143,6 +145,7 @@ pub(crate) fn spawn_exit_watcher(
turn_ref,
call_id,
command,
host_id,
cwd,
Some(process_id.to_string()),
transcript,
@@ -196,6 +199,7 @@ pub(crate) async fn emit_exec_end_for_unified_exec(
turn_ref: Arc<TurnContext>,
call_id: String,
command: Vec<String>,
host_id: Option<String>,
cwd: PathBuf,
process_id: Option<String>,
transcript: Arc<Mutex<HeadTailBuffer>>,
@@ -223,6 +227,7 @@ pub(crate) async fn emit_exec_end_for_unified_exec(
cwd,
ExecCommandSource::UnifiedExecStartup,
process_id,
host_id,
);
emitter
.emit(event_ctx, ToolEventStage::Success(output))
@@ -235,6 +240,7 @@ pub(crate) async fn emit_failed_exec_end_for_unified_exec(
turn_ref: Arc<TurnContext>,
call_id: String,
command: Vec<String>,
host_id: Option<String>,
cwd: PathBuf,
process_id: Option<String>,
transcript: Arc<Mutex<HeadTailBuffer>>,
@@ -266,6 +272,7 @@ pub(crate) async fn emit_failed_exec_end_for_unified_exec(
cwd,
ExecCommandSource::UnifiedExecStartup,
process_id,
host_id,
);
emitter
.emit(
@@ -192,6 +192,7 @@ impl UnifiedExecProcessManager {
cwd.to_path_buf(),
ExecCommandSource::UnifiedExecStartup,
Some(request.process_id.to_string()),
request.host_id.clone(),
);
emitter.emit(event_ctx, ToolEventStage::Begin).await;
@@ -208,6 +209,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&process),
context,
&request.command,
request.host_id.clone(),
cwd.clone(),
start,
request.process_id,
@@ -255,6 +257,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&context.turn),
context.call_id.clone(),
request.command.clone(),
request.host_id.clone(),
cwd.to_path_buf(),
Some(request.process_id.to_string()),
Arc::clone(&transcript),
@@ -298,6 +301,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&context.turn),
context.call_id.clone(),
request.command.clone(),
request.host_id.clone(),
cwd.to_path_buf(),
Some(process_id.to_string()),
Arc::clone(&transcript),
@@ -526,6 +530,7 @@ impl UnifiedExecProcessManager {
process: Arc<UnifiedExecProcess>,
context: &UnifiedExecContext,
command: &[String],
host_id: Option<String>,
cwd: AbsolutePathBuf,
started_at: Instant,
process_id: i32,
@@ -572,6 +577,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&context.turn),
context.call_id.clone(),
command.to_vec(),
host_id,
cwd.to_path_buf(),
process_id,
transcript,
+11 -4
View File
@@ -414,7 +414,14 @@ impl Environment {
match exec_server_url {
Some(exec_server_url) => {
Self::create_remote(exec_server_url, None, None, None, "codex-environment").await
Self::create_remote(
exec_server_url,
/*default_cwd*/ None,
/*default_shell*/ None,
/*ssh_exec_server*/ None,
"codex-environment",
)
.await
}
None => Ok(Self::default()),
}
@@ -426,9 +433,9 @@ impl Environment {
HostConnection::ExecServerUrl { url } => {
Self::create_remote(
url.clone(),
None,
None,
None,
/*default_cwd*/ None,
/*default_shell*/ None,
/*ssh_exec_server*/ None,
format!("codex-environment-{}", host_config.id),
)
.await
+8
View File
@@ -3027,6 +3027,10 @@ pub struct ExecCommandBeginEvent {
/// Where the command originated. Defaults to Agent for backward compatibility.
#[serde(default)]
pub source: ExecCommandSource,
/// Remote host id when the command is executed outside the local environment.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub host_id: Option<String>,
/// Raw input sent to a unified exec session (if this is an interaction event).
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
@@ -3051,6 +3055,10 @@ pub struct ExecCommandEndEvent {
/// Where the command originated. Defaults to Agent for backward compatibility.
#[serde(default)]
pub source: ExecCommandSource,
/// Remote host id when the command is executed outside the local environment.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub host_id: Option<String>,
/// Raw input sent to a unified exec session (if this is an interaction event).
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
@@ -922,6 +922,7 @@ fn command_execution_started_event(turn_id: &str, item: &ThreadItem) -> Option<V
.map(codex_app_server_protocol::CommandAction::into_core)
.collect(),
source: source.to_core(),
host_id: None,
interaction_input: None,
}),
}])
@@ -982,6 +983,7 @@ fn command_execution_completed_event(turn_id: &str, item: &ThreadItem) -> Option
.map(codex_app_server_protocol::CommandAction::into_core)
.collect(),
source: source.to_core(),
host_id: None,
interaction_input: None,
stdout: String::new(),
stderr: String::new(),
+17 -3
View File
@@ -397,6 +397,7 @@ struct RunningCommand {
command: Vec<String>,
parsed_cmd: Vec<ParsedCommand>,
source: ExecCommandSource,
host_id: Option<String>,
}
struct UnifiedExecProcessSummary {
@@ -4319,9 +4320,14 @@ impl ChatWidget {
if self.suppressed_exec_calls.remove(&ev.call_id) {
return;
}
let (command, parsed, source) = match running {
Some(rc) => (rc.command, rc.parsed_cmd, rc.source),
None => (ev.command.clone(), ev.parsed_cmd.clone(), ev.source),
let (command, parsed, source, host_id) = match running {
Some(rc) => (rc.command, rc.parsed_cmd, rc.source, rc.host_id),
None => (
ev.command.clone(),
ev.parsed_cmd.clone(),
ev.source,
ev.host_id.clone(),
),
};
let parsed = self.annotate_skill_reads_in_parsed_cmd(parsed);
let is_unified_exec_interaction =
@@ -4382,6 +4388,7 @@ impl ChatWidget {
command,
parsed,
source,
host_id,
ev.interaction_input.clone(),
self.config.animations,
);
@@ -4403,6 +4410,7 @@ impl ChatWidget {
command,
parsed,
source,
host_id,
ev.interaction_input.clone(),
self.config.animations,
);
@@ -4550,6 +4558,7 @@ impl ChatWidget {
command: ev.command.clone(),
parsed_cmd: parsed_cmd.clone(),
source: ev.source,
host_id: ev.host_id.clone(),
},
);
let is_wait_interaction = matches!(ev.source, ExecCommandSource::UnifiedExecInteraction)
@@ -4583,6 +4592,7 @@ impl ChatWidget {
ev.command.clone(),
parsed_cmd.clone(),
ev.source,
ev.host_id.clone(),
interaction_input.clone(),
)
{
@@ -4596,6 +4606,7 @@ impl ChatWidget {
ev.command.clone(),
parsed_cmd,
ev.source,
ev.host_id,
interaction_input,
self.config.animations,
)));
@@ -6206,6 +6217,7 @@ impl ChatWidget {
.map(codex_app_server_protocol::CommandAction::into_core)
.collect(),
source: source.to_core(),
host_id: None,
interaction_input: None,
});
} else {
@@ -6221,6 +6233,7 @@ impl ChatWidget {
.map(codex_app_server_protocol::CommandAction::into_core)
.collect(),
source: source.to_core(),
host_id: None,
interaction_input: None,
stdout: String::new(),
stderr: String::new(),
@@ -6792,6 +6805,7 @@ impl ChatWidget {
.map(codex_app_server_protocol::CommandAction::into_core)
.collect(),
source: source.to_core(),
host_id: None,
interaction_input: None,
});
}
@@ -346,6 +346,82 @@ async fn exec_history_cell_shows_working_then_failed() {
assert!(blob.to_lowercase().contains("bloop"), "expected error text");
}
#[tokio::test]
async fn exec_history_cell_shows_remote_host_while_running() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let command = vec![
"bash".to_string(),
"-lc".to_string(),
"printf remote".to_string(),
];
let parsed_cmd = vec![ParsedCommand::Unknown {
cmd: "printf remote".to_string(),
}];
chat.handle_codex_event(Event {
id: "call-remote-running".to_string(),
msg: EventMsg::ExecCommandBegin(ExecCommandBeginEvent {
call_id: "call-remote-running".to_string(),
process_id: Some("proc-remote".to_string()),
turn_id: "turn-1".to_string(),
command,
cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
parsed_cmd,
source: ExecCommandSource::Agent,
host_id: Some("remote-host".to_string()),
interaction_input: None,
}),
});
let blob = active_blob(&chat);
assert!(
blob.contains("• Running on remote-host"),
"expected remote host in running header: {blob:?}"
);
}
#[tokio::test]
async fn exec_history_cell_shows_remote_host_after_completion() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.on_task_started();
let command = vec![
"bash".to_string(),
"-lc".to_string(),
"echo remote".to_string(),
];
let parsed_cmd = codex_shell_command::parse_command::parse_command(&command);
chat.handle_codex_event(Event {
id: "call-remote-complete".to_string(),
msg: EventMsg::ExecCommandEnd(ExecCommandEndEvent {
call_id: "call-remote-complete".to_string(),
process_id: Some("proc-remote".to_string()),
turn_id: "turn-1".to_string(),
command,
cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
parsed_cmd,
source: ExecCommandSource::UnifiedExecStartup,
host_id: Some("remote-host".to_string()),
interaction_input: None,
stdout: "remote\n".to_string(),
stderr: String::new(),
aggregated_output: "remote\n".to_string(),
exit_code: 0,
duration: std::time::Duration::from_millis(5),
formatted_output: "remote\n".to_string(),
status: CoreExecCommandStatus::Completed,
}),
});
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1, "expected finalized remote exec cell");
let blob = lines_to_single_string(&cells[0]);
assert!(
blob.contains("• Ran on remote-host echo remote"),
"expected remote host in completed header: {blob:?}"
);
}
#[tokio::test]
async fn exec_end_without_begin_uses_event_command() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
@@ -366,6 +442,7 @@ async fn exec_end_without_begin_uses_event_command() {
cwd,
parsed_cmd,
source: ExecCommandSource::Agent,
host_id: None,
interaction_input: None,
stdout: "done".to_string(),
stderr: String::new(),
@@ -493,6 +493,7 @@ pub(super) fn begin_exec_with_source(
cwd,
parsed_cmd,
source,
host_id: None,
interaction_input,
};
chat.handle_codex_event(Event {
@@ -518,6 +519,7 @@ pub(super) fn begin_unified_exec_startup(
cwd,
parsed_cmd: Vec::new(),
source: ExecCommandSource::UnifiedExecStartup,
host_id: None,
interaction_input: None,
};
chat.handle_codex_event(Event {
@@ -632,6 +634,7 @@ pub(super) fn end_exec(
cwd,
parsed_cmd,
source,
host_id,
interaction_input,
process_id,
} = begin_event;
@@ -645,6 +648,7 @@ pub(super) fn end_exec(
cwd,
parsed_cmd,
source,
host_id,
interaction_input,
stdout: stdout.to_string(),
stderr: stderr.to_string(),
@@ -1566,6 +1566,7 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() {
cwd: cwd.clone(),
parsed_cmd: parsed_cmd.clone(),
source: ExecCommandSource::Agent,
host_id: None,
interaction_input: None,
}),
});
@@ -1579,6 +1580,7 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() {
cwd,
parsed_cmd,
source: ExecCommandSource::Agent,
host_id: None,
interaction_input: None,
stdout: String::new(),
stderr: String::new(),
+10 -1
View File
@@ -27,6 +27,7 @@ pub(crate) struct ExecCall {
pub(crate) parsed: Vec<ParsedCommand>,
pub(crate) output: Option<CommandOutput>,
pub(crate) source: ExecCommandSource,
pub(crate) host_id: Option<String>,
pub(crate) start_time: Option<Instant>,
pub(crate) duration: Option<Duration>,
pub(crate) interaction_input: Option<String>,
@@ -52,6 +53,7 @@ impl ExecCell {
command: Vec<String>,
parsed: Vec<ParsedCommand>,
source: ExecCommandSource,
host_id: Option<String>,
interaction_input: Option<String>,
) -> Option<Self> {
let call = ExecCall {
@@ -60,11 +62,18 @@ impl ExecCell {
parsed,
output: None,
source,
host_id,
start_time: Some(Instant::now()),
duration: None,
interaction_input,
};
if self.is_exploring_cell() && Self::is_exploring_call(&call) {
if self.is_exploring_cell()
&& Self::is_exploring_call(&call)
&& self
.calls
.iter()
.all(|existing| existing.host_id == call.host_id)
{
Some(Self {
calls: [self.calls.clone(), vec![call]].concat(),
animations_enabled: self.animations_enabled,
+25 -6
View File
@@ -43,6 +43,7 @@ pub(crate) fn new_active_exec_command(
command: Vec<String>,
parsed: Vec<ParsedCommand>,
source: ExecCommandSource,
host_id: Option<String>,
interaction_input: Option<String>,
animations_enabled: bool,
) -> ExecCell {
@@ -53,6 +54,7 @@ pub(crate) fn new_active_exec_command(
parsed,
output: None,
source,
host_id,
start_time: Some(Instant::now()),
duration: None,
interaction_input,
@@ -61,6 +63,13 @@ pub(crate) fn new_active_exec_command(
)
}
fn remote_host_title_suffix(host_id: Option<&str>) -> String {
host_id
.filter(|host_id| !host_id.trim().is_empty())
.map(|host_id| format!(" on {host_id}"))
.unwrap_or_default()
}
fn format_unified_exec_interaction(command: &[String], input: Option<&str>) -> String {
let command_display = if let Some((_, script)) = extract_bash_command(command) {
script.to_string()
@@ -261,6 +270,8 @@ impl ExecCell {
fn exploring_display_lines(&self, width: u16) -> Vec<Line<'static>> {
let mut out: Vec<Line<'static>> = Vec::new();
let host_suffix =
remote_host_title_suffix(self.calls.first().and_then(|call| call.host_id.as_deref()));
out.push(Line::from(vec![
if self.is_active() {
spinner(self.active_start_time(), self.animations_enabled())
@@ -269,9 +280,9 @@ impl ExecCell {
},
" ".into(),
if self.is_active() {
"Exploring".bold()
format!("Exploring{host_suffix}").bold()
} else {
"Explored".bold()
format!("Explored{host_suffix}").bold()
},
]));
@@ -375,13 +386,16 @@ impl ExecCell {
};
let is_interaction = call.is_unified_exec_interaction();
let title = if is_interaction {
""
String::new()
} else if self.is_active() {
"Running"
format!(
"Running{}",
remote_host_title_suffix(call.host_id.as_deref())
)
} else if call.is_user_shell_command() {
"You ran"
"You ran".to_string()
} else {
"Ran"
format!("Ran{}", remote_host_title_suffix(call.host_id.as_deref()))
};
let mut header_line = if is_interaction {
@@ -784,6 +798,7 @@ mod tests {
parsed: Vec::new(),
output: Some(output),
source: ExecCommandSource::UserShell,
host_id: None,
start_time: None,
duration: None,
interaction_input: None,
@@ -933,6 +948,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::UserShell,
host_id: None,
start_time: None,
duration: None,
interaction_input: None,
@@ -970,6 +986,7 @@ mod tests {
}],
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: None,
duration: None,
interaction_input: None,
@@ -1011,6 +1028,7 @@ mod tests {
aggregated_output: url.to_string(),
}),
source: ExecCommandSource::UserShell,
host_id: None,
start_time: None,
duration: None,
interaction_input: None,
@@ -1048,6 +1066,7 @@ mod tests {
aggregated_output: url.to_string(),
}),
source: ExecCommandSource::Agent,
host_id: None,
start_time: None,
duration: None,
interaction_input: None,
+12
View File
@@ -3959,6 +3959,7 @@ mod tests {
],
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -3986,6 +3987,7 @@ mod tests {
}],
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4005,6 +4007,7 @@ mod tests {
path: "shimmer.rs".into(),
}],
ExecCommandSource::Agent,
/*host_id*/ None,
/*interaction_input*/ None,
)
.unwrap();
@@ -4020,6 +4023,7 @@ mod tests {
path: "status_indicator_widget.rs".into(),
}],
ExecCommandSource::Agent,
/*host_id*/ None,
/*interaction_input*/ None,
)
.unwrap();
@@ -4055,6 +4059,7 @@ mod tests {
],
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4079,6 +4084,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4105,6 +4111,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4129,6 +4136,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4152,6 +4160,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4176,6 +4185,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4200,6 +4210,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
@@ -4250,6 +4261,7 @@ mod tests {
parsed: Vec::new(),
output: None,
source: ExecCommandSource::Agent,
host_id: None,
start_time: Some(Instant::now()),
duration: None,
interaction_input: None,
+1
View File
@@ -1012,6 +1012,7 @@ mod tests {
vec!["bash".into(), "-lc".into(), "ls".into()],
vec![ParsedCommand::Unknown { cmd: "ls".into() }],
ExecCommandSource::Agent,
/*host_id*/ None,
/*interaction_input*/ None,
/*animations_enabled*/ true,
);