This stuff is just an example, I haven't tried compiling it.
If ti_on_item_unwielded gets called on running out of throwing ammo (ideal, since it'll seem more seamless to the client)
     
### module_mission_templates.py
(ti_on_item_unwielded, 0, 0, [],
[
     (try_begin),
          (multiplayer_is_server),
          (store_trigger_param_1, ":agent_no"),
          (ge, ":agent_no", 0),
          (agent_is_human, ":agent_no"),
          (try_for_range, ":current_item_slot", 0, 4),    #check item slots 0 - 3 (weapon1, weapon2, weapon3, weapon4)
               (agent_get_item_slot, ":current_item_id", ":agent_no", ":current_item_slot"),          
               (gt, ":current_item_id", 0),   #if slot empty, skip to next slot
               (item_get_type, ":current_item_type", ":current_item_id"),
                    (try_begin),
                    (this_or_next|eq, itp_type_arrows, ":current_item_type"),  #slot contains arrows
                    (this_or_next|eq, itp_type_bolts, ":current_item_type"),  #slot contains bolts
                    (eq, itp_type_thrown, ":current_item_type"),  #slot contains a throwing weapon
                    (agent_get_item_slot_ammo, ":current_item_ammo_count", ":agent_no", ":current_item_slot"),    ###WSE Operation
                    (eq, 0, ":current_item_ammo_count"),
                    (store_add, ":slot_to_clear", 1, ":current_item_slot"), #add 1 to current_item_slot since agent_unequip_item uses values 1-4 to get weapon slots 1-4, while agent_get_item_slot uses values 0-3.
                    (agent_unequip_item, ":agent_no", ":current_item_id", "slot_to_clear"),    #remove item if it is a stack of arrows/bolts/throwing weapon with 0 ammo
               (try_end),
          (try_end),
     (try_end),
(try_end),
]),
If ti_on_item_unwielded doesn't get called by running out of a throwing weapon:
### module_mission_templates.py
(ti_on_item_wielded, 0, 0, [],
[
     (try_begin),
          (multiplayer_is_server),
          (store_trigger_param_1, ":agent_no"),
          (ge, ":agent_no", 0),
          (agent_is_human, ":agent_no"),
          (try_for_range, ":current_item_slot", 0, 4),    #check item slots 0 - 3 (weapon1, weapon2, weapon3, weapon4)
               (agent_get_item_slot, ":current_item_id", ":agent_no", ":current_item_slot"),          
               (gt, ":current_item_id", 0),   #if slot empty, skip to next slot
               (item_get_type, ":current_item_type", ":current_item_id"),
                    (try_begin),
                    (this_or_next|eq, itp_type_arrows, ":current_item_type"),  #slot contains arrows
                    (this_or_next|eq, itp_type_bolts, ":current_item_type"),  #slot contains bolts
                    (eq, itp_type_thrown, ":current_item_type"),  #slot contains a throwing weapon
                    (agent_get_item_slot_ammo, ":current_item_ammo_count", ":agent_no", ":current_item_slot"),    ###WSE Operation
                    (eq, 0, ":current_item_ammo_count"),
                    (store_add, ":slot_to_clear", 1, ":current_item_slot"), #add 1 to current_item_slot since agent_unequip_item uses values 1-4 to get weapon slots 1-4, while agent_get_item_slot uses values 0-3.
                    (agent_unequip_item, ":agent_no", ":current_item_id", "slot_to_clear"),    #remove item if it is a stack of arrows/bolts/throwing weapon with 0 ammo
               (try_end),
          (try_end),
     (try_end),
(try_end),
]),