fix(runtime): isolate agent state by project (#101)
This commit is contained in:
@ -22,7 +22,7 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ids::{AgentId, SessionId};
|
||||
use crate::ids::{AgentId, ProjectId, RuntimeAgentKey, SessionId};
|
||||
|
||||
/// Identifies one [`Conversation`].
|
||||
///
|
||||
@ -68,19 +68,38 @@ impl ConversationId {
|
||||
/// Pour une paire `Agent↔Agent`, l'id est dérivé des deux UUID agent de façon
|
||||
/// commutative (XOR), stable et déterministe.
|
||||
#[must_use]
|
||||
pub fn for_pair(a: ConversationParty, b: ConversationParty) -> Self {
|
||||
pub fn for_project_pair(
|
||||
project_id: ProjectId,
|
||||
a: ConversationParty,
|
||||
b: ConversationParty,
|
||||
) -> Self {
|
||||
match (a.as_agent(), b.as_agent()) {
|
||||
// User↔Agent (un seul agent) : aligné sur le repli de `resolve_conversation`.
|
||||
(Some(agent), None) | (None, Some(agent)) => Self::from_uuid(agent.as_uuid()),
|
||||
(Some(agent), None) | (None, Some(agent)) => {
|
||||
let key = RuntimeAgentKey::new(project_id, agent);
|
||||
Self::from_uuid(uuid::Uuid::from_u128(
|
||||
key.project_id.as_uuid().as_u128() ^ key.agent_id.as_uuid().as_u128(),
|
||||
))
|
||||
}
|
||||
// Agent↔Agent : combinaison commutative des deux ids (insensible à l'ordre).
|
||||
(Some(x), Some(y)) => {
|
||||
let xor = x.as_uuid().as_u128() ^ y.as_uuid().as_u128();
|
||||
let xor =
|
||||
project_id.as_uuid().as_u128() ^ x.as_uuid().as_u128() ^ y.as_uuid().as_u128();
|
||||
Self::from_uuid(uuid::Uuid::from_u128(xor))
|
||||
}
|
||||
// User↔User n'existe pas (invariant `Conversation`) : repli sûr non-panic.
|
||||
(None, None) => Self::from_uuid(uuid::Uuid::nil()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Legacy deterministic pair id without project scoping.
|
||||
///
|
||||
/// Runtime code must prefer [`Self::for_project_pair`]. Kept for persisted legacy
|
||||
/// data and tests that intentionally exercise project-less values.
|
||||
#[must_use]
|
||||
pub fn for_pair(a: ConversationParty, b: ConversationParty) -> Self {
|
||||
Self::for_project_pair(ProjectId::from_uuid(uuid::Uuid::nil()), a, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ConversationId {
|
||||
@ -322,7 +341,12 @@ pub trait ConversationRegistry: Send + Sync {
|
||||
/// Get-or-create: returns the thread for the pair `{a, b}`, opening it
|
||||
/// (`Dormant`) if it did not exist. Pure registry — opens **no** session.
|
||||
/// The same unordered pair always yields the same [`ConversationId`].
|
||||
fn resolve(&self, a: ConversationParty, b: ConversationParty) -> Conversation;
|
||||
fn resolve(
|
||||
&self,
|
||||
project_id: ProjectId,
|
||||
a: ConversationParty,
|
||||
b: ConversationParty,
|
||||
) -> Conversation;
|
||||
|
||||
/// Marks the conversation `id` `Live` with the given session reference.
|
||||
fn bind_session(&self, id: ConversationId, session: SessionRef);
|
||||
|
||||
Reference in New Issue
Block a user