fix(runtime): isolate agent state by project (#101)

This commit is contained in:
2026-07-25 22:14:02 +02:00
parent 6a87c4635f
commit 6e98fd89f7
52 changed files with 1894 additions and 805 deletions

View File

@ -188,6 +188,7 @@ fn live_agent_list_dto_serialises_camelcase_array() {
let node_a = NodeId::from_uuid(Uuid::from_u128(21));
let session_a = domain::SessionId::from_uuid(Uuid::from_u128(31));
let dto = LiveAgentListDto::from_snapshots(vec![LiveSessionSnapshot {
project_id: domain::ProjectId::from_uuid(Uuid::from_u128(1)),
agent_id: agent_a,
node_id: node_a,
session_id: session_a,

View File

@ -17,7 +17,7 @@ use std::time::Duration;
use app_tauri_lib::state::AppState;
use async_trait::async_trait;
use domain::events::DomainEvent;
use domain::ids::{NodeId, SessionId};
use domain::ids::{NodeId, ProjectId, SessionId};
use domain::ports::{AgentSession, AgentSessionError, IdGenerator, ReplyStream};
use domain::AgentId;
use infrastructure::UuidGenerator;
@ -35,6 +35,10 @@ fn node(n: u128) -> NodeId {
NodeId::from_uuid(uuid::Uuid::from_u128(n))
}
fn project_id(n: u128) -> ProjectId {
ProjectId::from_uuid(uuid::Uuid::from_u128(n))
}
fn session(n: u128) -> SessionId {
SessionId::from_uuid(uuid::Uuid::from_u128(n))
}
@ -85,9 +89,13 @@ async fn on_rate_limited_arms_a_cancellable_resume_over_the_real_bus() {
// Reset far in the future so the scheduler does NOT fire before we cancel.
let resets_at_ms = i64::MAX;
state
.session_limit_service
.on_rate_limited(agent(7), node(70), None, Some(resets_at_ms));
state.session_limit_service.on_rate_limited(
project_id(1),
agent(7),
node(70),
None,
Some(resets_at_ms),
);
// The two arming events, in order, on the bus.
let mut saw_rate_limited = false;
@ -145,7 +153,7 @@ async fn direct_structured_rate_limit_uses_live_meta_and_arms_resume() {
node(70),
);
let (agent_id, node_id, conversation_id) = state
let (project_id, agent_id, node_id, conversation_id) = state
.structured_sessions
.meta_for_session(&sid)
.expect("live structured session metadata");
@ -155,6 +163,7 @@ async fn direct_structured_rate_limit_uses_live_meta_and_arms_resume() {
let resets_at_ms = i64::MAX;
state.session_limit_service.on_rate_limited(
project_id,
agent_id,
node_id,
conversation_id,
@ -205,7 +214,7 @@ async fn direct_structured_rate_limit_without_conversation_uses_human_fallback()
node(70),
);
let (agent_id, node_id, conversation_id) = state
let (project_id, agent_id, node_id, conversation_id) = state
.structured_sessions
.meta_for_session(&sid)
.expect("live structured session metadata");
@ -217,9 +226,13 @@ async fn direct_structured_rate_limit_without_conversation_uses_human_fallback()
(None, None) => (None, None),
(None, Some(_)) => (None, None),
};
state
.session_limit_service
.on_rate_limited(agent_id, node_id, conversation_id, resets_at_ms);
state.session_limit_service.on_rate_limited(
project_id,
agent_id,
node_id,
conversation_id,
resets_at_ms,
);
let mut saw_rate_limited_none = false;
let mut saw_suspected = false;
@ -298,9 +311,13 @@ async fn confirm_human_resume_arms_a_cancellable_resume_over_the_real_bus() {
// Reset très loin dans le futur : le scheduler ne tire pas avant l'annulation.
let resets_at_ms = i64::MAX;
state
.session_limit_service
.confirm_human_resume(agent(8), node(80), None, resets_at_ms);
state.session_limit_service.confirm_human_resume(
project_id(1),
agent(8),
node(80),
None,
resets_at_ms,
);
let mut saw_rate_limited = false;
let mut saw_scheduled = false;