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:
@ -11,8 +11,8 @@ use application::AppError;
|
||||
use application::{
|
||||
CreateAgentOutput, InspectConversationOutput, LaunchAgentOutput, ListAgentsOutput,
|
||||
};
|
||||
use domain::ports::ConversationDetails;
|
||||
use domain::ids::{AgentId, NodeId, ProfileId, SessionId};
|
||||
use domain::ports::ConversationDetails;
|
||||
use domain::terminal::{PtySize, SessionKind, SessionStatus, TerminalSession};
|
||||
use domain::{Agent, AgentOrigin, ProjectPath};
|
||||
use serde_json::json;
|
||||
@ -44,7 +44,10 @@ fn agent_dto_serialises_camelcase() {
|
||||
assert_eq!(v["id"], agent.id.to_string());
|
||||
assert_eq!(v["name"], "My Agent");
|
||||
assert_eq!(v["contextPath"], "agents/my-agent.md", "camelCase key");
|
||||
assert_eq!(v["profileId"], ProfileId::from_uuid(Uuid::from_u128(2)).to_string());
|
||||
assert_eq!(
|
||||
v["profileId"],
|
||||
ProfileId::from_uuid(Uuid::from_u128(2)).to_string()
|
||||
);
|
||||
assert_eq!(v["synchronized"], false);
|
||||
// origin: tagged `{ "type": "scratch" }`
|
||||
assert_eq!(v["origin"]["type"], "scratch");
|
||||
@ -68,7 +71,9 @@ fn agent_list_dto_is_transparent_array() {
|
||||
#[test]
|
||||
fn create_agent_output_maps_to_agent_dto() {
|
||||
let agent = make_agent(5, 6);
|
||||
let out = CreateAgentOutput { agent: agent.clone() };
|
||||
let out = CreateAgentOutput {
|
||||
agent: agent.clone(),
|
||||
};
|
||||
let dto = AgentDto::from(out);
|
||||
assert_eq!(dto.0.id, agent.id);
|
||||
}
|
||||
@ -168,19 +173,25 @@ fn agent_already_running_error_code_is_stable() {
|
||||
// Option A: a stable code + a text message (the node is NOT enriched into the
|
||||
// ErrorDto — the frontend branches on the code alone).
|
||||
assert_eq!(dto.code, "AGENT_ALREADY_RUNNING");
|
||||
assert!(dto.message.contains("already running"), "message: {}", dto.message);
|
||||
assert!(
|
||||
dto.message.contains("already running"),
|
||||
"message: {}",
|
||||
dto.message
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn live_agent_list_dto_serialises_camelcase_array() {
|
||||
let agent_a = AgentId::from_uuid(Uuid::from_u128(11));
|
||||
let node_a = NodeId::from_uuid(Uuid::from_u128(21));
|
||||
let dto = LiveAgentListDto::from_pairs(vec![(agent_a, node_a)]);
|
||||
let session_a = domain::SessionId::from_uuid(Uuid::from_u128(31));
|
||||
let dto = LiveAgentListDto::from_pairs(vec![(agent_a, node_a, session_a)]);
|
||||
let v = serde_json::to_value(&dto).unwrap();
|
||||
let arr = v.as_array().expect("transparent array");
|
||||
assert_eq!(arr.len(), 1);
|
||||
assert_eq!(arr[0]["agentId"], agent_a.to_string());
|
||||
assert_eq!(arr[0]["nodeId"], node_a.to_string());
|
||||
assert_eq!(arr[0]["sessionId"], session_a.to_string());
|
||||
// No snake_case leak.
|
||||
assert!(arr[0].get("agent_id").is_none());
|
||||
assert!(arr[0].get("node_id").is_none());
|
||||
@ -246,7 +257,10 @@ fn conversation_details_dto_omits_fields_when_none() {
|
||||
let v = serde_json::to_value(&dto).unwrap();
|
||||
// Both optional fields are omitted from the wire (absent, not null).
|
||||
assert!(v.get("lastTopic").is_none(), "absent topic ⇒ field omitted");
|
||||
assert!(v.get("tokenCount").is_none(), "absent tokens ⇒ field omitted");
|
||||
assert!(
|
||||
v.get("tokenCount").is_none(),
|
||||
"absent tokens ⇒ field omitted"
|
||||
);
|
||||
assert_eq!(v, json!({}), "fully degraded ⇒ empty object");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user