72dd611f8c
chat_action was stored in two places — the model column and config JSON — and dispatch_helpers unconditionally overrode the config value with the column. The frontend only ever wrote the JSON path, so the UI choice silently had no effect on outgoing chat actions. Make the column the single source of truth: frontend sends chat_action top-level, dispatch_helpers reads from the column, and a one-time backfill migrates existing config values to the column and strips the legacy key. Also fix a long-standing race where the keepalive's bare sleep(4) + finally cancel could fire one last sendChatAction after the response already arrived, leaving a phantom indicator for ~5s. Replace with a stop event + wait_for so callers can signal stop cleanly via the new stop_keepalive helper.