feat: agent Rust complet — systray, chat, enregistrement, floutage (2.4 MB)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-03-18 23:18:09 +01:00
parent ad7ff3bce4
commit 90ee91caf9
11 changed files with 2329 additions and 191 deletions

175
agent_rust/src/state.rs Normal file
View File

@@ -0,0 +1,175 @@
//! Etat partage thread-safe de l'agent.
//!
//! Centralise l'etat courant (enregistrement, replay, connexion, etc.)
//! accessible depuis tous les threads (systray, heartbeat, replay, recorder).
//! Equivalent de agent_v1/ui/shared_state.py.
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex};
/// Etats possibles de l'icone systray
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrayState {
/// Gris — en attente, pas de session active
Idle,
/// Rouge — enregistrement en cours
Recording,
/// Vert — connecte au serveur, pret
Connected,
/// Bleu — replay en cours
Replay,
}
/// Etat partage de l'agent, thread-safe via Arc + atomics.
///
/// Les booleens utilisent AtomicBool pour un acces lock-free.
/// Le nom de session utilise un Mutex car c'est une String.
#[derive(Debug)]
pub struct AgentState {
/// Enregistrement en cours (session de capture)
pub recording: AtomicBool,
/// Nom de la session d'enregistrement courante
pub recording_name: Mutex<String>,
/// Replay en cours (execution d'actions)
pub replay_active: AtomicBool,
/// Connecte au serveur streaming
pub connected: AtomicBool,
/// Nombre d'actions capturees dans la session courante
pub actions_count: AtomicU32,
/// L'agent est en cours d'execution (false = arret demande)
pub running: AtomicBool,
/// Fenetre de chat visible
pub chat_visible: AtomicBool,
/// Arret d'urgence active
pub emergency_stop: AtomicBool,
/// Dernier message de notification (pour eviter les doublons)
#[allow(dead_code)]
pub last_notification: Mutex<String>,
}
impl AgentState {
/// Cree un nouvel etat avec les valeurs par defaut.
pub fn new() -> Arc<Self> {
Arc::new(Self {
recording: AtomicBool::new(false),
recording_name: Mutex::new(String::new()),
replay_active: AtomicBool::new(false),
connected: AtomicBool::new(false),
actions_count: AtomicU32::new(0),
running: AtomicBool::new(true),
chat_visible: AtomicBool::new(false),
emergency_stop: AtomicBool::new(false),
last_notification: Mutex::new(String::new()),
})
}
/// Demarre un enregistrement avec le nom donne.
pub fn start_recording(&self, name: &str) {
self.recording.store(true, Ordering::SeqCst);
self.actions_count.store(0, Ordering::SeqCst);
if let Ok(mut n) = self.recording_name.lock() {
*n = name.to_string();
}
println!("[STATE] Enregistrement demarre : '{}'", name);
}
/// Arrete l'enregistrement en cours.
pub fn stop_recording(&self) -> (String, u32) {
self.recording.store(false, Ordering::SeqCst);
let count = self.actions_count.load(Ordering::SeqCst);
let name = self
.recording_name
.lock()
.map(|n| n.clone())
.unwrap_or_default();
println!("[STATE] Enregistrement arrete : '{}' ({} actions)", name, count);
(name, count)
}
/// Incremente le compteur d'actions capturees.
pub fn increment_actions(&self) -> u32 {
self.actions_count.fetch_add(1, Ordering::SeqCst) + 1
}
/// Verifie si l'agent est en cours d'execution.
pub fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
/// Demande l'arret de l'agent.
pub fn request_shutdown(&self) {
self.running.store(false, Ordering::SeqCst);
println!("[STATE] Arret demande");
}
/// Active/desactive le replay.
pub fn set_replay_active(&self, active: bool) {
self.replay_active.store(active, Ordering::SeqCst);
}
/// Met a jour le statut de connexion au serveur.
pub fn set_connected(&self, connected: bool) {
let was_connected = self.connected.swap(connected, Ordering::SeqCst);
if was_connected != connected {
println!(
"[STATE] Connexion serveur : {}",
if connected { "CONNECTE" } else { "DECONNECTE" }
);
}
}
/// Active l'arret d'urgence — stoppe tout immediatement.
pub fn emergency_stop(&self) {
self.emergency_stop.store(true, Ordering::SeqCst);
self.recording.store(false, Ordering::SeqCst);
self.replay_active.store(false, Ordering::SeqCst);
println!("[STATE] === ARRET D'URGENCE ACTIVE ===");
}
/// Retourne l'etat courant du systray.
pub fn tray_state(&self) -> TrayState {
if self.recording.load(Ordering::SeqCst) {
TrayState::Recording
} else if self.replay_active.load(Ordering::SeqCst) {
TrayState::Replay
} else if self.connected.load(Ordering::SeqCst) {
TrayState::Connected
} else {
TrayState::Idle
}
}
/// Retourne le nom de la session d'enregistrement courante.
pub fn current_recording_name(&self) -> String {
self.recording_name
.lock()
.map(|n| n.clone())
.unwrap_or_default()
}
}
impl Default for AgentState {
fn default() -> Self {
// Note: on ne peut pas retourner Arc<Self> depuis Default,
// donc on fournit les valeurs brutes. Utiliser new() de preference.
Self {
recording: AtomicBool::new(false),
recording_name: Mutex::new(String::new()),
replay_active: AtomicBool::new(false),
connected: AtomicBool::new(false),
actions_count: AtomicU32::new(0),
running: AtomicBool::new(true),
chat_visible: AtomicBool::new(false),
emergency_stop: AtomicBool::new(false),
last_notification: Mutex::new(String::new()),
}
}
}