feat(background): livraison auto au propriétaire + projection des tâches de fond dans le work-state (T1+T3)
T1 — Wake automatique du propriétaire à la complétion. La complétion d'une tâche de fond est désormais livrée à l'agent propriétaire dès que la session accepte l'envoi (wake.rs : mark_completion_delivered au send accepté), via un drain de flux dédié (structured.rs : drain_reply_stream_with_readiness). L'inbox médiée enfile l'item sans démarrer de tour ni marquer l'agent busy (input/mod.rs : enqueue FIFO silencieux). Régression couverte (tests/agent_wake.rs, tests input/mod.rs). T3 — Tâches de fond projetées dans le read-model du panneau Work. AgentWorkState porte désormais background_tasks (VO AgentBackgroundTaskState), alimenté par un builder best-effort with_background_tasks(store) : union list_open_for_agent + dispatch des completions non livrées par owner_agent_id, erreur store => Vec vide (aucune régression live/busy/tickets). DTO Tauri backgroundTasks et wiring du BackgroundTaskStore côté state.rs. Le frontend, déjà câblé, affiche Cancel/Retry (mapping queued/waiting -> pending, tri sur updatedAtMs). Borne V1 : une tâche terminale déjà livrée n'est plus énumérable (Retry limité à la fenêtre non livrée). Tests : cargo build --workspace OK ; cargo test -p application / -p app-tauri / -p infrastructure verts ; frontend build + vitest verts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -19,7 +19,7 @@ use domain::ports::{
|
||||
};
|
||||
use domain::project::Project;
|
||||
|
||||
use crate::agent::drain_with_readiness;
|
||||
use crate::agent::drain_reply_stream_with_readiness;
|
||||
|
||||
/// Provides a structured/headless session for a wake turn.
|
||||
///
|
||||
@ -96,7 +96,7 @@ impl AgentWakeService {
|
||||
});
|
||||
|
||||
if self.input.busy_state(agent).is_busy() {
|
||||
return Ok(());
|
||||
return Err(WakeError::AgentBusy { agent_id: agent });
|
||||
}
|
||||
|
||||
let Some(item) = self.inbox.dequeue_next(agent) else {
|
||||
@ -117,26 +117,29 @@ impl AgentWakeService {
|
||||
agent_id: agent,
|
||||
});
|
||||
let session = self.sessions.session_for_wake(project, agent).await?;
|
||||
drain_with_readiness(
|
||||
session.as_ref(),
|
||||
&delivery.prompt,
|
||||
None,
|
||||
self.input.as_ref(),
|
||||
agent,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| WakeError::Session(err.to_string()))?;
|
||||
|
||||
self.mailbox.cancel_head(agent, delivery.ticket_id);
|
||||
self.input.mark_idle(agent);
|
||||
guard.disarm();
|
||||
let stream = session
|
||||
.send(&delivery.prompt)
|
||||
.await
|
||||
.map_err(|err| WakeError::Session(err.to_string()))?;
|
||||
|
||||
if let Some(task_id) = delivery.delivered_task_id {
|
||||
self.tasks
|
||||
.mark_completion_delivered(task_id)
|
||||
.await
|
||||
.map_err(|err| WakeError::Store(err.to_string()))?;
|
||||
self.publish(DomainEvent::BackgroundTaskCompletionDelivered {
|
||||
project_id: project.id,
|
||||
task_id,
|
||||
owner_agent_id: agent,
|
||||
});
|
||||
}
|
||||
drain_reply_stream_with_readiness(stream, self.input.as_ref(), agent)
|
||||
.await
|
||||
.map_err(|err| WakeError::Session(err.to_string()))?;
|
||||
|
||||
self.mailbox.cancel_head(agent, delivery.ticket_id);
|
||||
self.input.mark_idle(agent);
|
||||
guard.disarm();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user