fix(sandbox): forward exec_command_streaming through WorktreeSandbox

This commit is contained in:
Bryan Helmkamp 2026-04-30 21:49:50 -04:00
parent 1383022678
commit 6da71be2fa
No known key found for this signature in database
2 changed files with 38 additions and 1 deletions

View file

@ -509,6 +509,15 @@ pub trait Sandbox: Send + Sync {
env_vars: Option<&std::collections::HashMap<String, String>>,
cancel_token: Option<CancellationToken>,
) -> crate::Result<ExecResult>;
/// Stream a command's output as it runs.
///
/// **Production sandboxes must override this.** The default falls back to
/// the non-streaming [`exec_command`](Self::exec_command) and replays its
/// output through `output_callback` at the end, marking
/// `live_streaming: false`. That's the right behavior for test mocks but
/// silently drops live output for any real sandbox that wraps another —
/// decorators in particular must forward to the inner sandbox's streaming
/// implementation rather than relying on this default.
async fn exec_command_streaming(
&self,
command: &str,

View file

@ -6,7 +6,10 @@ use async_trait::async_trait;
use tokio_util::sync::CancellationToken;
use crate::sandbox::fetch_source_run_ref;
use crate::{DirEntry, ExecResult, GitRunInfo, GitSetupIntent, GrepOptions, Sandbox, shell_quote};
use crate::{
CommandOutputCallback, DirEntry, ExecResult, ExecStreamingResult, GitRunInfo, GitSetupIntent,
GrepOptions, Sandbox, shell_quote,
};
/// Git command prefix that disables background maintenance.
const GIT: &str = "git -c maintenance.auto=0 -c gc.auto=0";
@ -227,6 +230,31 @@ impl Sandbox for WorktreeSandbox {
.await
}
/// Stream a command's output, forwarding to the inner sandbox's streaming
/// implementation so live output and `streams_separated` / `live_streaming`
/// flags survive the worktree wrapping.
async fn exec_command_streaming(
&self,
command: &str,
timeout_ms: u64,
working_dir: Option<&str>,
env_vars: Option<&HashMap<String, String>>,
cancel_token: Option<CancellationToken>,
output_callback: CommandOutputCallback,
) -> crate::Result<ExecStreamingResult> {
let wd = working_dir.unwrap_or(&self.config.worktree_path);
self.inner
.exec_command_streaming(
command,
timeout_ms,
Some(wd),
env_vars,
cancel_token,
output_callback,
)
.await
}
// --- Delegated methods ---
async fn read_file(