From ac9269d6ce0e52592e2c4c7b78202e7b73b30099 Mon Sep 17 00:00:00 2001 From: Hare Date: Mon, 31 Aug 2026 23:14:33 +0900 Subject: [PATCH] fix: omit local bash spill path from remote commands --- .../src/feature/builtin/manage_workdir.rs | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/worker/src/feature/builtin/manage_workdir.rs b/crates/worker/src/feature/builtin/manage_workdir.rs index 46397ea0..2647bded 100644 --- a/crates/worker/src/feature/builtin/manage_workdir.rs +++ b/crates/worker/src/feature/builtin/manage_workdir.rs @@ -339,7 +339,13 @@ impl WorkdirSession for WorkspaceAttachedWorkdirSession { } } - async fn start_command(&self, request: CommandRequest) -> Result { + async fn start_command( + &self, + mut request: CommandRequest, + ) -> Result { + // `spill_dir` belongs to the Worker host running BashTool. This remote + // WorkdirSession provider cannot safely resolve or write that host path. + request.spill_dir = None; match self.operate(WorkdirSessionOperation::CommandStart(request))? { WorkdirSessionOperationResult::CommandStart(result) => Ok(result), _ => Err(Self::mismatch("command_start")), @@ -1118,6 +1124,42 @@ mod tests { assert!(body.get("session_id").is_none()); } + #[tokio::test] + async fn attached_session_does_not_send_worker_local_bash_spill_path() { + let client = Arc::new(RecordingWorkspaceClient::new(vec![response(json!({ + "operation": "command_start", + "result": "command-1" + }))])); + let session = WorkspaceAttachedWorkdirSession::handle(client.clone()); + + let handle = session + .start_command(CommandRequest { + command: "true".to_string(), + timeout_secs: 120, + output_limit: 1024, + spill_dir: Some("/worker-local/bash-output".into()), + tool_call_id: Some("call-1".to_string()), + }) + .await + .unwrap(); + + assert_eq!(handle, CommandHandle("command-1".to_string())); + let requests = client.requests(); + let body: serde_json::Value = + serde_json::from_str(requests[0].body.as_deref().unwrap()).unwrap(); + assert_eq!(body["operation"]["operation"], "command_start"); + assert_eq!(body["operation"]["request"]["command"], "true"); + assert!(body["operation"]["request"]["spill_dir"].is_null()); + assert_eq!(body["operation"]["request"]["tool_call_id"], "call-1"); + assert!( + !requests[0] + .body + .as_deref() + .unwrap() + .contains("/worker-local/bash-output") + ); + } + #[tokio::test] async fn delegated_attached_session_carries_captured_fence_on_operations() { let client = Arc::new(RecordingWorkspaceClient::new(vec![