@@ -498,7 +498,17 @@ impl AcpThreadView {
Some(new_version_available_tx),
);
- let connect_task = agent.connect(root_dir.as_deref(), delegate, cx);
+ let agent_name = agent.name();
+ let timeout = cx.background_executor().timer(Duration::from_secs(30));
+ let connect_task = smol::future::or(
+ agent.connect(root_dir.as_deref(), delegate, cx),
+ async move {
+ timeout.await;
+ Err(anyhow::Error::new(LoadError::Other(
+ format!("{agent_name} is unable to initialize after 30 seconds.").into(),
+ )))
+ },
+ );
let load_task = cx.spawn_in(window, async move |this, cx| {
let connection = match connect_task.await {
Ok((connection, login)) => {
@@ -7358,4 +7368,54 @@ pub(crate) mod tests {
assert_eq!(text, expected_txt);
})
}
+
+ #[gpui::test]
+ async fn test_initialize_timeout(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ struct InfiniteInitialize;
+
+ impl AgentServer for InfiniteInitialize {
+ fn telemetry_id(&self) -> &'static str {
+ "test"
+ }
+
+ fn logo(&self) -> ui::IconName {
+ ui::IconName::Ai
+ }
+
+ fn name(&self) -> SharedString {
+ "Test".into()
+ }
+
+ fn connect(
+ &self,
+ _root_dir: Option<&Path>,
+ _delegate: AgentServerDelegate,
+ cx: &mut App,
+ ) -> Task<gpui::Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>>
+ {
+ cx.spawn(async |_| futures::future::pending().await)
+ }
+
+ fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
+ self
+ }
+ }
+
+ let (thread_view, cx) = setup_thread_view(InfiniteInitialize, cx).await;
+
+ cx.executor().advance_clock(Duration::from_secs(31));
+ cx.run_until_parked();
+
+ let error = thread_view.read_with(cx, |thread_view, _| match &thread_view.thread_state {
+ ThreadState::LoadError(err) => err.clone(),
+ _ => panic!("Incorrect thread state"),
+ });
+
+ match error {
+ LoadError::Other(str) => assert!(str.contains("initialize")),
+ _ => panic!("Unexpected load error"),
+ }
+ }
}