[gym.vector]: add `terminal_observation` to `info`
diff --git a/gym/vector/async_vector_env.py b/gym/vector/async_vector_env.py
index 331dfcd5568..e8b1f537de8 100644
--- a/gym/vector/async_vector_env.py
+++ b/gym/vector/async_vector_env.py
@@ -413,6 +413,7 @@ def _worker(index, env_fn, pipe, parent_pipe, shared_memory, error_queue):
             elif command == "step":
                 observation, reward, done, info = env.step(data)
                 if done:
+                    info["terminal_observation"] = observation
                     observation = env.reset()
                 pipe.send(((observation, reward, done, info), True))
             elif command == "seed":
@@ -453,6 +454,7 @@ def _worker_shared_memory(index, env_fn, pipe, parent_pipe, shared_memory, error
             elif command == "step":
                 observation, reward, done, info = env.step(data)
                 if done:
+                    info["terminal_observation"] = observation
                     observation = env.reset()
                 write_to_shared_memory(
                     index, observation, shared_memory, observation_space
diff --git a/gym/vector/sync_vector_env.py b/gym/vector/sync_vector_env.py
index 068022c56a0..5652f00294f 100644
--- a/gym/vector/sync_vector_env.py
+++ b/gym/vector/sync_vector_env.py
@@ -82,6 +82,7 @@ def step_wait(self):
         for i, (env, action) in enumerate(zip(self.envs, self._actions)):
             observation, self._rewards[i], self._dones[i], info = env.step(action)
             if self._dones[i]:
+                info["terminal_observation"] = observation
                 observation = env.reset()
             observations.append(observation)
             infos.append(info)
diff --git a/tests/vector/test_vector_env.py b/tests/vector/test_vector_env.py
index 6bd404b431d..7ea3664305a 100644
--- a/tests/vector/test_vector_env.py
+++ b/tests/vector/test_vector_env.py
@@ -34,8 +34,16 @@ def test_vector_env_equal(shared_memory):
             actions = async_env.action_space.sample()
             assert actions in sync_env.action_space
 
-            async_observations, async_rewards, async_dones, _ = async_env.step(actions)
-            sync_observations, sync_rewards, sync_dones, _ = sync_env.step(actions)
+            # fmt: off
+            async_observations, async_rewards, async_dones, async_infos = async_env.step(actions)
+            sync_observations, sync_rewards, sync_dones, sync_infos = sync_env.step(actions)
+            # fmt: on
+
+            for idx in range(len(sync_dones)):
+                if sync_dones[idx]:
+                    assert "terminal_observation" in async_infos[idx]
+                    assert "terminal_observation" in sync_infos[idx]
+                    assert sync_dones[idx]
 
             assert np.all(async_observations == sync_observations)
             assert np.all(async_rewards == sync_rewards)

This PR follows up with #1632 and adds the `terminal_observation` to `info` in the vectorized environments at the end of an env's episode. The key `terminal_observation` is consistent with SB3's vectorized environment. See

![image](https://user-images.githubusercontent.com/5555347/141665286-0af06f00-6440-4a52-95ce-206cda06b83f.png)
