feat(domain,application,infrastructure,backend,web-server,app-tauri,frontend): persistance et reprise des conversations de CLI custom par agent — index durable <projet>/.ideai/conversations/custom-index.json borné aux conversations non vides par agent (conversation_id logique, titre dérivé de la première invite, created/updated_at_ms), port CustomConversationIndex + adaptateur FsCustomConversationIndex, persistence des messages utilisateur et finals agent dans le log de conversation existant, DTO/commandes app-tauri et routes web-server pour lister/rouvrir, CustomAgentChatView: ouverture par défaut sur une nouvelle conversation, menu reprise d'une conversation passée ou nouvelle conversation, replay de l'historique sauvegardé (user+finals) à la réouverture, affichage normal des nouveaux messages après reprise, /clean ouvre une nouvelle conversation, aucune entrée créée si la conversation courante est vide, injection de l'historique dans le contexte agent différée au premier nouveau message après reprise — #363 (QA verte: git diff --check, cargo fmt --all -- --check, cargo tests ciblés infrastructure/application/web-server/app-tauri/backend, npm run typecheck, tests frontend ciblés et CustomAgentChatView, npm run build)
This commit is contained in:
@ -74,6 +74,40 @@ fn id<T>(value: u128, make: impl FnOnce(Uuid) -> T) -> T {
|
||||
make(Uuid::from_u128(value))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_conversation_binding_keeps_logical_id_and_consumes_resume_once() {
|
||||
let session_id = id(1, SessionId::from_uuid);
|
||||
let project_id = id(2, ProjectId::from_uuid);
|
||||
let agent_id = id(3, AgentId::from_uuid);
|
||||
let node_id = id(4, NodeId::from_uuid);
|
||||
let conversation_id = domain::ConversationId::from_uuid(Uuid::from_u128(5));
|
||||
let sessions = StructuredSessions::new();
|
||||
sessions.insert_in_project(
|
||||
project_id,
|
||||
Arc::new(FakeSession::new(session_id, [])),
|
||||
agent_id,
|
||||
node_id,
|
||||
);
|
||||
|
||||
sessions.bind_custom_conversation(
|
||||
session_id,
|
||||
conversation_id,
|
||||
Some("bounded handoff".to_owned()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sessions.custom_conversation_id(&session_id),
|
||||
Some(conversation_id)
|
||||
);
|
||||
assert_eq!(
|
||||
sessions
|
||||
.take_pending_resume_preamble(&session_id)
|
||||
.as_deref(),
|
||||
Some("bounded handoff")
|
||||
);
|
||||
assert_eq!(sessions.take_pending_resume_preamble(&session_id), None);
|
||||
}
|
||||
|
||||
struct TurnAccounting {
|
||||
state: Mutex<AgentBusyState>,
|
||||
cancelled: Mutex<Vec<(RuntimeAgentKey, TicketId)>>,
|
||||
|
||||
@ -918,6 +918,44 @@ async fn global_idea_cli_preference_applies_to_a_new_human_launch() {
|
||||
assert_eq!(f.pty.spawn_count(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn explicit_custom_chat_gets_a_fresh_logical_conversation_without_persisting_it() {
|
||||
let f = launch_fixture(
|
||||
structured_profile(pid(9)),
|
||||
FakeFactory::new(500, Some("engine-conv")),
|
||||
);
|
||||
let mut input = launch_input(f.agent.id);
|
||||
input.require_structured = true;
|
||||
|
||||
let output = f.launch.execute(input).await.unwrap();
|
||||
let assigned = output
|
||||
.assigned_conversation_id
|
||||
.as_deref()
|
||||
.and_then(|raw| Uuid::parse_str(raw).ok())
|
||||
.map(ConversationId::from_uuid)
|
||||
.expect("fresh logical conversation id");
|
||||
|
||||
assert_eq!(assigned, ConversationId::from_uuid(Uuid::from_u128(1)));
|
||||
assert_ne!(
|
||||
assigned,
|
||||
ConversationId::for_project_pair(
|
||||
project_id(),
|
||||
ConversationParty::User,
|
||||
ConversationParty::agent(f.agent.id),
|
||||
),
|
||||
"each explicit custom chat is a distinct logical thread"
|
||||
);
|
||||
assert_eq!(
|
||||
f.structured.custom_conversation_id(&sid(500)),
|
||||
Some(assigned)
|
||||
);
|
||||
assert_eq!(
|
||||
f.structured.take_pending_resume_preamble(&sid(500)),
|
||||
None,
|
||||
"a fresh chat has no resume context"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn explicit_native_tui_overrides_the_global_idea_cli_preference() {
|
||||
let f = launch_fixture_with_preference(DefaultAgentTerminalInterface::IdeaCli);
|
||||
|
||||
Reference in New Issue
Block a user