Having two “services” for each entity is what i prefer. One “high level script” (or “behavior script”) with continuations, that gives orders like this:
set_destination(target)
wait_until_target_reached_or_failed
Then a second, tick based loop, which handles the per-frame updates to position, direction, path following etc. This second service can run in a separate native thread, as you are suggesting, without using green threads. Then the two threads exchange messages. The path generation can be done on the script thread because that thread is not time sensitive, and generating a path is a one-time big time expense, while path-following is not. If some script event is delayed, thats not a problem. The bot might just stand a couple of more milliseconds where its path ended before continuing with its next action, but that is not game-breaking. But statistically, bots will be always following a path, and will rarely require attention from the script thread. Attacking the player, however, must be done on the tick thread, because the bot must react to player right away. What the script thread can do, is to set the bots reaction to player. Something like: “attack on sight”, “neutral”, “friendly”, “ignore”, “stop the path if attacked” etc. Then the condition checking and actual attacking is done by the per-frame thread. If the script wants the attack to end, it has to change the reaction type.