feat(agent): backend hot-swap profil (A0+A1) + inventaire reprise session (B1) — L15
Cadrage Architecture §15 (figé) : « agent = entité à session persistante ». - A0 (domaine) : Agent::with_profile, LayoutTree::leaf, event AgentProfileChanged (+ DTO miroir DomainEventDto camelCase). - A1 (application) : use case ChangeAgentProfile — no-op si profil identique, mutation manifeste, nettoyage conversation_id/agent_was_running sur layouts persistés, swap à chaud (kill PTY + relance même cellule via composition de LaunchAgent), event AgentProfileChanged. Décision : repartir à neuf (on garde .md + mémoire, on jette l'historique de conversation). - B1 (application) : use case ListResumableAgents (lecture seule) — inventaire des cellules was_running||conversation_id, resume_supported selon profil, best-effort. Aucun nouveau port/adapter (composition de l'existant). Hexagonal strict. Tests : domaine 11 + app-tauri dto + ChangeAgentProfile 9 + ListResumableAgents 8, suite application complète verte (0 régression). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -47,6 +47,14 @@ pub enum DomainEventDto {
|
||||
/// Exit code.
|
||||
code: i32,
|
||||
},
|
||||
/// An agent's runtime profile was changed (hot-swap of the AI engine).
|
||||
#[serde(rename_all = "camelCase")]
|
||||
AgentProfileChanged {
|
||||
/// Agent id.
|
||||
agent_id: String,
|
||||
/// The new runtime profile id.
|
||||
profile_id: String,
|
||||
},
|
||||
/// A template was updated.
|
||||
#[serde(rename_all = "camelCase")]
|
||||
TemplateUpdated {
|
||||
@ -171,6 +179,13 @@ impl From<&DomainEvent> for DomainEventDto {
|
||||
agent_id: agent_id.to_string(),
|
||||
code: *code,
|
||||
},
|
||||
DomainEvent::AgentProfileChanged {
|
||||
agent_id,
|
||||
profile_id,
|
||||
} => Self::AgentProfileChanged {
|
||||
agent_id: agent_id.to_string(),
|
||||
profile_id: profile_id.to_string(),
|
||||
},
|
||||
DomainEvent::TemplateUpdated {
|
||||
template_id,
|
||||
version,
|
||||
|
||||
@ -13,7 +13,7 @@ use domain::{Direction, LayoutNode, LayoutTree, LeafCell, NodeId};
|
||||
|
||||
use application::{AppError, HealthInput};
|
||||
use domain::events::DomainEvent;
|
||||
use domain::ids::{AgentId, SessionId};
|
||||
use domain::ids::{AgentId, ProfileId, SessionId};
|
||||
use domain::ProjectId;
|
||||
use domain::TemplateId;
|
||||
use domain::TemplateVersion;
|
||||
@ -93,6 +93,26 @@ fn domain_event_dto_maps_agent_launched() {
|
||||
assert_eq!(v["sessionId"], sid.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn domain_event_dto_maps_agent_profile_changed() {
|
||||
// LOT A0 : round-trip camelCase agentId/profileId du miroir DTO.
|
||||
let aid = AgentId::from_uuid(Uuid::from_u128(1));
|
||||
let pid = ProfileId::from_uuid(Uuid::from_u128(2));
|
||||
let dto = DomainEventDto::from(&DomainEvent::AgentProfileChanged {
|
||||
agent_id: aid,
|
||||
profile_id: pid,
|
||||
});
|
||||
let v = serde_json::to_value(&dto).unwrap();
|
||||
assert_eq!(
|
||||
v,
|
||||
json!({
|
||||
"type": "agentProfileChanged",
|
||||
"agentId": aid.to_string(),
|
||||
"profileId": pid.to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn domain_event_dto_maps_template_updated_version() {
|
||||
let tid = TemplateId::from_uuid(Uuid::nil());
|
||||
|
||||
Reference in New Issue
Block a user