# RdkTube Agent — Architecture (Agent_v4)

## Overview

- `heartbeat_loop`: reporta estado del agente a Supabase
- `live_detection_loop`: consulta tareas live y reporta resultados
- `plan_loop`: descarga plan del orquestador y aplica ventanas Chrome

Estado compartido en `_state` protegido por `_state._lock`.

## Helpers

- `fetch_channel_avatar(channel_url)`: scrapea avatar y usa cache persistente en `%USERPROFILE%\.rdk\avatars\`.
- `build_plan_map(plan)`: usa `normalize_yt()` para keys estables.
- `compute_tasks_hash(live_tasks, open_streams)`: incluye `is_live` para refrescar al cambiar estado.

## Monitor / Window placement

- `_panel_root_ref`: referencia al root del panel (set/clear en `open_panel`).
- `get_panel_monitor(panel_root)`: detecta monitor por `winfo_x()`.
- `compute_grid(..., sx_override, sy_override)`: offsets absolutos para la grilla.
- `apply_plan(...)`: abre Chrome en el mismo monitor donde vive el panel.

## Config (`%USERPROFILE%\.rdk\config.ini`)

### [Agent]
- `agent_id` (str, default: empty)
- `agent_token` (str, default: empty)

### [Intervals]
- `heartbeat_seconds` (int, default: 30)
- `plan_poll_seconds` (int, default: 30)
- `live_poll_fallback` (int, default: 60)

### [UI]
- `window_w` (int/"auto", default: 500)
- `window_h` (int/"auto", default: 400)
- `grid_cols` (int/"auto", default: auto)
- `grid_spacing` (int, default: 4)
- `start_x` (int, default: 0) *(offset relativo al monitor actual del panel)*
- `start_y` (int, default: 0) *(offset relativo al monitor actual del panel)*
- `stagger_ms` (int, default: 700)
- `force_resize` (yes/no, default: yes)
- `hard_kill` (yes/no, default: no)
- `use_app_mode` (yes/no, default: no)
- `language` (str, default: es)

### [Notifications]
- `enabled` (yes/no, default: yes)

## Avatar Cache

```text
%USERPROFILE%\.rdk\avatars\
  @handle_sanitized.jpg
```
