use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use crate::{
agent::{Agent, AgentEvent, SpawnedAgentStatus},
error::RuntimeError,
transcript::{DelegationArtifact, DelegationEdge},
};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum DeferredItem {
Context(String),
DelegationResult {
content: String,
delegation: DelegationArtifact,
edge: Option<DelegationEdge>,
},
SubagentFinished {
id: String,
status: SpawnedAgentStatus,
},
}
#[derive(Clone, Default)]
pub(crate) struct DeferredQueue {
by_agent: Arc<Mutex<HashMap<String, Vec<DeferredItem>>>>,
}
impl DeferredQueue {
fn push(&self, agent_id: &str, item: DeferredItem) {
self.lock()
.entry(agent_id.to_string())
.or_default()
.push(item);
}
fn drain(&self, agent_id: &str) -> Vec<DeferredItem> {
self.lock().remove(agent_id).unwrap_or_default()
}
fn has_pending(&self, agent_id: &str) -> bool {
self.lock()
.get(agent_id)
.is_some_and(|items| !items.is_empty())
}
fn lock(&self) -> std::sync::MutexGuard<'_, HashMap<String, Vec<DeferredItem>>> {
self.by_agent
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
#[derive(Clone)]
pub struct Deferred {
queue: DeferredQueue,
agent_id: String,
}
impl Deferred {
pub(crate) fn new(queue: DeferredQueue, agent_id: String) -> Self {
Self { queue, agent_id }
}
pub fn agent_id(&self) -> &str {
&self.agent_id
}
pub fn push(&self, item: DeferredItem) {
self.queue.push(&self.agent_id, item);
}
}
impl std::fmt::Debug for Deferred {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Deferred")
.field("agent_id", &self.agent_id)
.finish_non_exhaustive()
}
}
impl Agent {
pub(super) fn has_deferred(&self) -> bool {
self.runtime.collaboration.deferred.has_pending(&self.id)
}
pub(super) fn apply_deferred(&mut self) -> Result<(), RuntimeError> {
let items = self.runtime.collaboration.deferred.drain(&self.id);
if items.is_empty() {
return Ok(());
}
for item in items {
match item {
DeferredItem::Context(content) => self.record_canonical_context(content)?,
DeferredItem::DelegationResult {
content,
delegation,
edge,
} => self.record_delegation_result(content, delegation, edge)?,
DeferredItem::SubagentFinished { id, status } => {
if let Some(finished) = self.finish_subagent(&id, status) {
self.emit_event(AgentEvent::SubagentFinished { agent: finished });
}
}
}
}
self.sync_memory_snapshot();
Ok(())
}
}