Most Stoke category operators require the Position input. The Position Input node provides the voxel position (X,Y,Z) that the Magma iterates through implicitly. (In Krakatoa, the Magma loops over all incoming particles, in Stoke Field Magma, it loops over all voxels).
The reason we require the Position is so that you can provide any modified position if you wanted to. I originally requested to make the Position socket of these nodes default to the voxel position without connecting it, but it just never happened. I should probably ask again At least I made it so that if you drag from the Position socket, a new Position Input will be made, and if one exists, a connection to it will be created automatically without duplication. So it is still rather fast to build.
In short, for the Gradient node you need to connect the Position (second) socket to a Position input to tell it to sample every voxel in the Stoke Field Magma grid.
The Field (first) socket needs to be connected to the field whose gradient we want. For particle data, you should look at the ParticleSplat operator. It takes a particle source and a Position (same XYZ as the Gradient), and splats the particle data onto a grid that can be then passed as the Field input of the Gradient node. You can select which channels you want, in your case you need Density. You can then negate the Gradient output, multiply by some factor (since we need the Velocity to be in units per second and not units per frame), and output as Velocity.
Naturally, if you have very few particles moving through the grid, there will be some jumping of values around the grid as the particles move from one voxel to the next.
The alternative would be the old ParticleSumRadius operator in the Object category which lets you sample a particle system within a specified radius. This would be called for each voxel again, but you need to do a lot more work there since the output needs to be divided by the Weight, but if the Count of particles in the radius is 0, this could result in a division by zero and NaN values. So it then needs a Switch operator to check the count and pass the result as 0 if the count is 0. On top of that, if you pick a Particle Flow Event, it will not have a default Density (there are two workarounds here, let me know if you want to hear them). In contrast, with the ParticleSplat if a particle does not carry a Density channel, it will assume a 1.0 default when splatting…
Let me know if this helps.