Group by consecutive values in first column and second column value
In your code, you are checking for changes in a vector or for the differences between consecutive elements of a vector by using lag and then cleaning up the introduced NA value. When looking for changes, I would find it cleaner to handle the first element separately, which enables you to do the operation in a single line of code. For differences in timestamp, diff would make everything a lot cleaner:

metrics &lt;- metrics %&gt;%
  mutate(typing_episode = cumsum(c(1, head(package_name, -1) != tail(package_name, -1)))) %&gt;%
  group_by(typing_episode) %&gt;%
  mutate(second_group = cumsum(c(1, diff(timestamp) &gt; 1000 * 60 * 3)),
         typing_episode2 = typing_episode * 1000 + second_group)
print(metrics)


As you note, typing_episode2 might have repeats if second_group can exceed 1000. A reasonable alternative might be to do something like typing_episode2 = paste0(typing_episode, "_", second_group). Then you won't need to worry about non-unique typing_episode2 values.