Repository: block/goose
File: crates/goose-acp/src/server.rs (Rust)

Code before:
    #[test_case(
        RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new(PermissionOptionId::from("reject_once".to_string()))),
        PermissionConfirmation { principal_type: PrincipalType::Tool, permission: Permission::DenyOnce };
        "reject_once_maps_to_deny_once"
    )]
    #[test_case(
        RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new(PermissionOptionId::from("reject_always".to_string()))),
        PermissionConfirmation { principal_type: PrincipalType::Tool, permission: Permission::AlwaysDeny };
        "reject_always_maps_to_always_deny"
    )]
    #[test_case(
        RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new(PermissionOptionId::from("unknown".to_string()))),
        PermissionConfirmation { principal_type: PrincipalType::Tool, permission: Permission::Cancel };
        "unknown_option_maps_to_cancel"
    )]
    #[test_case(
        RequestPermissionOutcome::Cancelled,
        PermissionConfirmation { principal_type: PrincipalType::Tool, permission: Permission::Cancel };
        "cancelled_maps_to_cancel"
    )]
    fn test_outcome_to_confirmation(
        input: RequestPermissionOutcome,
        expected: PermissionConfirmation,
    ) {
        assert_eq!(outcome_to_confirmation(&input), expected);
    }

    use goose::providers::errors::ProviderError;

    struct MockModelProvider {
        models: Result<Vec<String>, ProviderError>,
    }

    #[async_trait::async_trait]
    impl goose::providers::base::Provider for MockModelProvider {
        fn get_name(&self) -> &str {
            "mock"
        }

        async fn stream(
            &self,
            _model_config: &goose::model::ModelConfig,
            _session_id: &str,
            _system: &str,
            _messages: &[goose::conversation::message::Message],
            _tools: &[rmcp::model::Tool],
        ) -> Result<goose::providers::base::MessageStream, ProviderError> {
            unimplemented!()
        }

        fn get_model_config(&self) -> goose::model::ModelConfig {

Review comment (suggestion):
The copilot explanation is convincing. That is quite a lot of .awaits, each one growing the size of the state machine. 85kb is quite a lot less than the [default 2mb](https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.html#method.thread_stack_size) though.

Code after:
                                    serde_json::from_value(req.params).map_err(|e| {
                                        sacp::Error::invalid_params().data(e.to_string())
                                    })?;
                                let resp = agent
                                    .on_set_model(&params.session_id.0, &params.model_id.0)
                                    .await?;
                                let json = serde_json::to_value(resp).map_err(|e| {
                                    sacp::Error::internal_error().data(e.to_string())
                                })?;
                                request_cx.respond(json)?;
                                Ok(())
                            }
                            MessageCx::Request(req, request_cx) if req.method.starts_with('_') => {
                                match agent.handle_custom_request(&req.method, req.params).await {
                                    Ok(json) => request_cx.respond(json)?,
                                    Err(e) => request_cx.respond_with_error(e)?,
                                }
                                Ok(())
                            }
                            _ => Err(sacp::Error::method_not_found()),
                        }
                    }
                })
                .await
                .map(|()| Handled::Yes)
        })
    }
}

pub fn serve<R, W>(
    agent: Arc<GooseAcpAgent>,
    read: R,
    write: W,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<()>> + Send>>
where
    R: futures::AsyncRead + Unpin + Send + 'static,
    W: futures::AsyncWrite + Unpin + Send + 'static,
{
    Box::pin(async move {
        let handler = GooseAcpHandler { agent };

        AgentToClient::builder()
            .name("goose-acp")
            .with_handler(handler)
            .serve(ByteStreams::new(write, read))
            .await?;

        Ok(())
    })
}