feat(plugins): hot reload des plugins sans relancer IdeA — use case ReloadPlugin (invalidation registry/catalogue, resultat hashes+changed), tools MCP d'administration plugin agent-facing (mcp/plugins.rs + PluginToolProvider), evenement domaine plugin_reloaded relaye sur le wire (dto/events), exposition openai_tools rafraichie, wiring app-tauri — #271/#269 (QA verte #273: application 32, mcp_server 38, openai_tools 10, plugin_install_load 5, plugin 10, wire 1 — 96 tests ciblés; app-tauri compile sans desktop runtime)

This commit is contained in:
2026-09-07 19:29:25 +02:00
parent 8d2be75f8f
commit 805b1b4d0d
14 changed files with 863 additions and 32 deletions

View File

@ -127,6 +127,18 @@ pub enum DomainEventDto {
/// Installed version.
version: String,
},
/// A plugin package was hot-reloaded.
#[serde(rename_all = "camelCase")]
PluginReloaded {
/// Plugin id.
plugin_id: String,
/// Previous content hash.
old_content_hash: String,
/// New content hash.
new_content_hash: String,
/// Whether the content changed.
changed: bool,
},
/// A plugin was enabled.
#[serde(rename_all = "camelCase")]
PluginEnabled {
@ -782,6 +794,17 @@ impl From<&DomainEvent> for DomainEventDto {
plugin_id: plugin_id.to_string(),
version: version.as_str().to_owned(),
},
DomainEvent::PluginReloaded {
plugin_id,
old_content_hash,
new_content_hash,
changed,
} => Self::PluginReloaded {
plugin_id: plugin_id.to_string(),
old_content_hash: old_content_hash.clone(),
new_content_hash: new_content_hash.clone(),
changed: *changed,
},
DomainEvent::PluginEnabled { plugin_id } => Self::PluginEnabled {
plugin_id: plugin_id.to_string(),
},
@ -1327,6 +1350,24 @@ mod tests {
assert_eq!(json["status"]["reused"], true);
}
#[test]
fn plugin_reloaded_relays_hashes_and_changed_flag_to_wire() {
let plugin_id = domain::PluginId::new("dev.acme.gitgraph").unwrap();
let dto = DomainEventDto::from(&DomainEvent::PluginReloaded {
plugin_id,
old_content_hash: "abc123".to_owned(),
new_content_hash: "def456".to_owned(),
changed: true,
});
let json = serde_json::to_value(&dto).expect("serialisable");
assert_eq!(json["type"], "pluginReloaded");
assert_eq!(json["pluginId"], "dev.acme.gitgraph");
assert_eq!(json["oldContentHash"], "abc123");
assert_eq!(json["newContentHash"], "def456");
assert_eq!(json["changed"], true);
}
#[test]
fn model_server_status_changed_relays_downloading_to_dto_and_wire() {
let dto = DomainEventDto::from(&DomainEvent::ModelServerStatusChanged {