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

@ -58,7 +58,10 @@ impl SkillStore for FakeSkills {
}
async fn save(&self, skill: &Skill, _root: &ProjectPath) -> Result<(), StoreError> {
let mut v = self.0.lock().unwrap();
if let Some(slot) = v.iter_mut().find(|s| s.scope == skill.scope && s.id == skill.id) {
if let Some(slot) = v
.iter_mut()
.find(|s| s.scope == skill.scope && s.id == skill.id)
{
*slot = skill.clone();
} else {
v.push(skill.clone());
@ -85,7 +88,10 @@ impl SkillStore for FakeSkills {
struct FakeContexts(Arc<Mutex<AgentManifest>>);
impl FakeContexts {
fn new(entries: Vec<ManifestEntry>) -> Self {
Self(Arc::new(Mutex::new(AgentManifest { version: 1, entries })))
Self(Arc::new(Mutex::new(AgentManifest {
version: 1,
entries,
})))
}
fn manifest(&self) -> AgentManifest {
self.0.lock().unwrap().clone()
@ -197,7 +203,11 @@ async fn create_skill_persists_in_its_scope() {
assert_eq!(out.skill.scope, SkillScope::Project);
assert_eq!(
store.list(SkillScope::Project, &root()).await.unwrap().len(),
store
.list(SkillScope::Project, &root())
.await
.unwrap()
.len(),
1
);
assert!(store