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:
43
crates/domain/src/custom_conversation.rs
Normal file
43
crates/domain/src/custom_conversation.rs
Normal file
@ -0,0 +1,43 @@
|
||||
//! Durable catalogue of user-facing custom CLI conversations.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ports::StoreError;
|
||||
use crate::{AgentId, ConversationId};
|
||||
|
||||
/// One non-empty custom CLI conversation owned by an agent.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CustomConversationEntry {
|
||||
/// Stable logical conversation id used by log, handoff and provider-session stores.
|
||||
pub conversation_id: ConversationId,
|
||||
/// Agent that owns the conversation.
|
||||
pub agent_id: AgentId,
|
||||
/// Title derived from the first accepted user prompt.
|
||||
pub title: String,
|
||||
/// Creation timestamp in epoch milliseconds.
|
||||
pub created_at_ms: u64,
|
||||
/// Most recent persisted human/final activity in epoch milliseconds.
|
||||
pub updated_at_ms: u64,
|
||||
}
|
||||
|
||||
/// Project-scoped durable index for non-empty custom CLI conversations.
|
||||
#[async_trait]
|
||||
pub trait CustomConversationIndex: Send + Sync {
|
||||
/// Lists conversations owned by `agent_id`, newest first and capped by `limit`.
|
||||
async fn list(
|
||||
&self,
|
||||
agent_id: AgentId,
|
||||
limit: usize,
|
||||
) -> Result<Vec<CustomConversationEntry>, StoreError>;
|
||||
|
||||
/// Finds an indexed conversation by logical id.
|
||||
async fn get(
|
||||
&self,
|
||||
conversation_id: ConversationId,
|
||||
) -> Result<Option<CustomConversationEntry>, StoreError>;
|
||||
|
||||
/// Inserts the first non-empty prompt or updates the last-activity timestamp.
|
||||
async fn upsert(&self, entry: CustomConversationEntry) -> Result<(), StoreError>;
|
||||
}
|
||||
@ -37,6 +37,7 @@ pub mod chat_attachment;
|
||||
pub mod chat_draft;
|
||||
pub mod conversation;
|
||||
pub mod conversation_log;
|
||||
pub mod custom_conversation;
|
||||
pub mod device;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
@ -177,6 +178,7 @@ pub use conversation_log::{
|
||||
TurnSlice, HANDOFF_SUMMARY_MAX_CHARS, MAX_ARCHIVE_SEGMENTS, PAGE_DEFAULT_LIMIT, PAGE_MAX_LIMIT,
|
||||
ROTATE_AFTER_BYTES, ROTATE_AFTER_TURNS,
|
||||
};
|
||||
pub use custom_conversation::{CustomConversationEntry, CustomConversationIndex};
|
||||
|
||||
pub use device::{
|
||||
AuthenticatedDevice, DeviceError, DeviceId, DeviceName, PairedDevice, SessionTokenHash,
|
||||
|
||||
Reference in New Issue
Block a user