fix(aws): the file viewer's change poll no longer counts as workspace activity

Its 2 s sha256sum poll now carries X-Triple-C-Background, so an open viewer does
not keep an idle AWS workspace up. Opening and saving a file stay foreground;
Docker is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shadowdaoandClaude Opus 5.5 committed 2026-11-15 13:08:05 -08:00
1 parent 33eb63d907
commit be2825d344
4 files changed
+46 -1

No files matched your search

+3 -1
View File
@@ -51,8 +51,10 @@ pub async fn poll_file(
"poll".to_string(),
container_path.to_string(),
];
// A background poll: an open viewer must not keep an idle AWS workspace
// up. Opening and saving a file are the user's own actions and count.
let (stdout, stderr, code) = container
.exec_oneshot_streams_as("claude", cmd, Vec::new())
.exec_oneshot_streams_background("claude", cmd)
.await?;
if code != 0 && code != 4 {
return Err(format!(
+11
View File
@@ -400,6 +400,17 @@ pub async fn exec_oneshot_streams_as(
Ok((out.stdout(), out.stderr(), out.exit_code))
}
/// [`exec_oneshot_streams_as`] marked as a background poll (the file
/// viewer's change check), so it never keeps an idle workspace up.
pub async fn exec_oneshot_streams_background(
ep: &AgentEndpoint,
user: &str,
cmd: Vec<String>,
) -> Result<(String, String, i64), String> {
let out = exec_oneshot_raw_marked(ep, user, cmd, Vec::new(), MAX_ONESHOT_OUTPUT, true).await?;
Ok((out.stdout(), out.stderr(), out.exit_code))
}
// ── detached execs, status, resize ───────────────────────────────────────────
#[derive(Deserialize)]
+16
View File
@@ -323,6 +323,22 @@ async fn only_a_background_poll_carries_the_background_marker() {
assert_eq!(seen.exec_bodies[0], seen.exec_bodies[1], "the request is otherwise the same");
}
#[tokio::test]
async fn the_file_viewers_poll_is_a_background_request_and_its_open_and_save_are_not() {
let (ep, fake) = fake_agent().await;
let t = target(&ep);
// The 2 s change check, as the viewer runs it.
let _ = crate::file_viewer::poll::poll_file(&t, "/workspace/x").await;
t.exec_oneshot_streams_background("claude", cmd("sh")).await.unwrap();
// What opening or saving goes through: user actions, which count.
t.exec_oneshot_streams_as("claude", cmd("sh"), Vec::new()).await.unwrap();
let seen = fake.seen.lock().unwrap();
assert_eq!(seen.exec_background, [Some("1".to_string()), Some("1".to_string()), None]);
let src = |s: &str| s.contains("exec_oneshot_streams_as(");
assert!(!src(include_str!("../../file_viewer/poll.rs")), "the poll stays a background request");
assert!(src(include_str!("../../file_viewer/resolve.rs")) && src(include_str!("../../file_viewer/write.rs")));
}
// ── one-shot execs ───────────────────────────────────────────────────────────
#[tokio::test]
+16
View File
@@ -302,6 +302,22 @@ impl ContainerTarget {
}
}
/// [`Self::exec_oneshot_streams_as`] for a background poll (the file
/// viewer's change check): on the agent it carries the background marker,
/// so it never counts as activity for idle stop. The same call on Docker.
pub async fn exec_oneshot_streams_background(
&self,
user: &str,
cmd: Vec<String>,
) -> Result<(String, String, i64), String> {
match self {
ContainerTarget::Docker { container_id } => {
docker::exec::exec_oneshot_streams_as(container_id, user, cmd, Vec::new()).await
}
ContainerTarget::Agent(ep) => agent::exec_oneshot_streams_background(ep, user, cmd).await,
}
}
/// Start a command and return without waiting or reading its output.
/// Returns the exec id.
pub async fn exec_detached(&self, spec: ExecSpec) -> Result<String, String> {