feat(memory): config embedders (LOT C2) + suggestion contextuelle (LOT C3) + contexte projet partagé

- LOT C2 (§14.5.3) : use cases de configuration des embedders déclaratifs
  (List/Save/Delete + DescribeEmbedderEngines : modèles ONNX recommandés,
  environnement local détecté, stratégies compilées). UI EmbedderSettings.
- LOT C3 (§14.5.5) : suggestion contextuelle best-effort à l'activation quand la
  mémoire dépasse le budget de recall sans embedder configuré (event
  EmbedderSuggested, anti-spam 1×/session, « ne plus demander »).
- Contexte projet partagé .ideai/CONTEXT.md (model-agnostic) injecté à tous les
  agents/profils au lancement, avant la persona. UI ProjectContextPanel.

Tests : backend workspace vert (0 échec) ; frontend 306/306.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 09:24:51 +02:00
parent 32398827fb
commit 785e9935fd
118 changed files with 5793 additions and 866 deletions

View File

@ -7,16 +7,16 @@ use app_tauri_lib::dto::{
LayoutOperationDto, OpenTerminalRequestDto, ReattachResultDto, ResizeTerminalRequestDto,
TerminalClosedDto, WriteTerminalRequestDto,
};
use app_tauri_lib::events::{DomainEventDto, DOMAIN_EVENT};
use application::{CloseTerminalOutput, LayoutOperation, LoadLayoutOutput, OpenTerminalInput};
use domain::{Direction, LayoutNode, LayoutTree, LeafCell, NodeId};
use app_tauri_lib::events::{DomainEventDto, DOMAIN_EVENT};
use application::{AppError, HealthInput};
use domain::events::DomainEvent;
use domain::ids::{AgentId, SessionId};
use domain::ProjectId;
use domain::TemplateVersion;
use domain::TemplateId;
use domain::TemplateVersion;
use serde_json::json;
use uuid::Uuid;
@ -188,7 +188,10 @@ fn terminal_closed_dto_serialises_code_camel_case() {
// Signalled (None) round-trips as null.
let none = TerminalClosedDto::from(CloseTerminalOutput { code: None });
assert_eq!(serde_json::to_value(&none).unwrap(), json!({ "code": null }));
assert_eq!(
serde_json::to_value(&none).unwrap(),
json!({ "code": null })
);
}
#[test]
@ -198,7 +201,10 @@ fn reattach_result_dto_serialises_camel_case() {
scrollback: vec![104, 105],
};
let v = serde_json::to_value(&dto).unwrap();
assert_eq!(v, json!({ "sessionId": "sess-1", "scrollback": [104, 105] }));
assert_eq!(
v,
json!({ "sessionId": "sess-1", "scrollback": [104, 105] })
);
}
#[test]
@ -327,7 +333,18 @@ fn layout_dto_round_trips_a_split_tree_shape() {
conversation_id: None,
agent_was_running: false,
})
.split(nid(1), Direction::Column, LeafCell { id: nid(2), session: None, agent: None, conversation_id: None, agent_was_running: false }, nid(9))
.split(
nid(1),
Direction::Column,
LeafCell {
id: nid(2),
session: None,
agent: None,
conversation_id: None,
agent_was_running: false,
},
nid(9),
)
.unwrap();
let dto = LayoutDto::from(LoadLayoutOutput {
layout_id: domain::LayoutId::new_random(),