Dada esta funcion:  public JsonResult EnviarTareas(string[] ids)
{
    try
    {
        destinatarios = construirDestinatarios();
        List<string> tareasFormateadas = new List<string>();
        Dictionary<string, string> tareasPorId = new Dictionary<string, string>(); // Para asociar ID con su mensaje

        foreach (string id in ids)
        {
            BlocNotasModel tarea = ConsultarTareaPorID(Int32.Parse(id));

            if (tarea != null)
            {
                // Eliminar caracteres de superíndice y subíndice
                string tareaLimpia = EliminarSuperSubindices(tarea.TAREA);

                // Formatear la tarea
                string tareaFormateada = $">>{tareaLimpia} - {tarea.FECHA:dd/MM/yyyy} - {tarea.TURNO.Trim()} - {tarea.ESTADO.Trim()} - OT:{tarea.OT} - {tarea.EQUIPO.Trim()} - {tarea.USUARIO.Trim()}";

                tareasFormateadas.Add(tareaFormateada);
                tareasPorId[id] = tareaFormateada; // Guardar la tarea con su ID
            }
        }

        // Unir todas las tareas con saltos de línea
        string resultadoFinal = string.Join("\r\n", tareasFormateadas);

        // Dividir el mensaje en paquetes de máximo 450 caracteres
        List<string> paquetes = DividirEnPaquetes(resultadoFinal, 450);

        // Diccionarios para almacenar envíos exitosos y fallidos
        List<string> idsEnviados = new List<string>();
        List<string> idsFallidos = new List<string>();

        // Enviar cada paquete por separado
        foreach (var paquete in paquetes)
        {
            bool enviado = enviarSMS(paquete);

            // Verificar qué IDs pertenecen a este paquete
            List<string> idsEnEstePaquete = tareasPorId
                .Where(kvp => paquete.Contains(kvp.Value)) // Buscar qué tareas están en este paquete
                .Select(kvp => kvp.Key)
                .ToList();

            if (enviado)
            {
                idsEnviados.AddRange(idsEnEstePaquete);
            }
            else
            {
                idsFallidos.AddRange(idsEnEstePaquete);
            }

            Thread.Sleep(1000); // Evitar spam de mensajes
        }

        // Marcar todas las tareas enviadas como enviadas
        foreach (string id in idsEnviados)
        {
            ActualizarEnviada(id);
        }

        // Respuesta detallada con IDs enviados y fallidos
        return Json(new
        {
            success = idsFallidos.Count == 0,
            message = idsFallidos.Count == 0 ? "Mensajes enviados correctamente." : "Algunos mensajes no se enviaron correctamente.",
            enviados = idsEnviados,
            fallidos = idsFallidos
        }, JsonRequestBehavior.AllowGet);
    }
    catch (SqlException ex)
    {
        return Json(new { success = false, message = $"Error de SQL: {ex.Message}" }, JsonRequestBehavior.AllowGet);
    }
    catch (Exception ex)
    {
        return Json(new { success = false, message = $"Error: {ex.Message}" }, JsonRequestBehavior.AllowGet);
    }
} y esta private List<string> DividirEnPaquetes(string texto, int maxTamano)
{
    List<string> paquetes = new List<string>();
    string[] partes = texto.Split(new string[] { "\r\n" }, StringSplitOptions.None); // Separar por tareas (cada una comienza con ">")

    StringBuilder paqueteActual = new StringBuilder();

    foreach (var parte in partes)
    {
        string tarea = parte.Trim();
        if (string.IsNullOrEmpty(tarea)) continue;

        // Si agregar esta tarea supera el límite, guardamos el paquete actual y comenzamos otro
        if (paqueteActual.Length + tarea.Length + 2 > maxTamano) // +2 por los saltos de línea
        {
            paquetes.Add(paqueteActual.ToString().Trim());
            paqueteActual.Clear();
        }

        // Agregar la tarea al paquete actual
        if (paqueteActual.Length > 0)
        {
            paqueteActual.Append("\n "); // Mantener el formato con saltos de línea
        }
        paqueteActual.Append(tarea);
    }

    // Agregar el último paquete si quedó algo pendiente
    if (paqueteActual.Length > 0)
    {
        paquetes.Add(paqueteActual.ToString().Trim());
    }

    return paquetes;
} quiero que las tareas formateadas se vayan rellenando de la siguiente manera: primero se ponga una cabecera con la fecha el turno el usuario y el equipo y todas las tareas que compartan esos campos se pongan a continuación con su ot y su estado.  Asi debe hacerse para todas posibles combinaciones hasta que no quede ninguna tarea