feat(424): rate-limit traffic-light dots and hard-block alerts
- Add HardBlock variant to WatcherEvent (story_id, agent_name, reset_time) - In pty.rs, distinguish allowed_warning (throttle) from hard blocks; emit RateLimitWarning for throttles, HardBlock for actual 429s - Add `throttled: bool` field to StoryAgent / AgentInfo - Pool spawns a background listener that sets throttled=true on RateLimitWarning or HardBlock events and fires AgentStateChanged - Status command shows traffic-light dots: ○ idle, ● running, ◑ throttled, ✗ blocked - Read blocked flag from story front matter for the ✗ dot - Notifications: RateLimitWarning silenced (too noisy); HardBlock sends urgent chat notification with optional reset time - Tests added for traffic_light_dot, read_story_blocked, status output, and all notification paths
This commit is contained in:
@@ -41,13 +41,47 @@ pub struct AgentPool {
|
||||
|
||||
impl AgentPool {
|
||||
pub fn new(port: u16, watcher_tx: broadcast::Sender<WatcherEvent>) -> Self {
|
||||
Self {
|
||||
let pool = Self {
|
||||
agents: Arc::new(Mutex::new(HashMap::new())),
|
||||
port,
|
||||
child_killers: Arc::new(Mutex::new(HashMap::new())),
|
||||
watcher_tx,
|
||||
watcher_tx: watcher_tx.clone(),
|
||||
merge_jobs: Arc::new(Mutex::new(HashMap::new())),
|
||||
};
|
||||
|
||||
// Spawn a background task (only when inside a tokio runtime) that
|
||||
// listens for RateLimitWarning and HardBlock events and updates the
|
||||
// throttled flag on the relevant agent so status dots stay current.
|
||||
if tokio::runtime::Handle::try_current().is_ok() {
|
||||
let agents_clone = Arc::clone(&pool.agents);
|
||||
let watcher_tx_clone = watcher_tx.clone();
|
||||
let mut rx = watcher_tx.subscribe();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let event = match rx.recv().await {
|
||||
Ok(e) => e,
|
||||
Err(broadcast::error::RecvError::Closed) => break,
|
||||
Err(broadcast::error::RecvError::Lagged(_)) => continue,
|
||||
};
|
||||
let (story_id, agent_name) = match &event {
|
||||
WatcherEvent::RateLimitWarning { story_id, agent_name }
|
||||
| WatcherEvent::HardBlock { story_id, agent_name, .. } => {
|
||||
(story_id.clone(), agent_name.clone())
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
let key = composite_key(&story_id, &agent_name);
|
||||
if let Ok(mut agents) = agents_clone.lock() {
|
||||
if let Some(agent) = agents.get_mut(&key) {
|
||||
agent.throttled = true;
|
||||
}
|
||||
}
|
||||
let _ = watcher_tx_clone.send(WatcherEvent::AgentStateChanged);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pool
|
||||
}
|
||||
|
||||
pub fn port(&self) -> u16 {
|
||||
|
||||
Reference in New Issue
Block a user