fix(session-limit): brancher le handle de limite sur le chemin direct (#30)

Le handle de limite de session ne se déclenchait pas quand un agent
directement adressé (dont Main) touchait sa propre limite : le ReplyEvent
::RateLimited du chemin structuré direct n'était pas relayé au service de
limite.

On tap désormais ReplyEvent::RateLimited dans le registre terminal vers
SessionLimitService::on_rate_limited, qui émet AgentRateLimited puis
AgentResumeScheduled et arme la reprise auto annulable, exactement comme le
chemin délégué.

Couverture QA (sortie réelle) : structured_registry_d1 12 passed,
session_limit_wiring 6 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 13:19:11 +02:00
parent 631dd4eccf
commit 9430c65050
4 changed files with 200 additions and 20 deletions

View File

@ -41,6 +41,7 @@ fn nid(n: u128) -> NodeId {
/// `send`/`shutdown` ne sont pas exercés ici.
struct FakeSession {
id: SessionId,
conversation_id: Option<String>,
}
#[async_trait]
@ -49,7 +50,7 @@ impl AgentSession for FakeSession {
self.id
}
fn conversation_id(&self) -> Option<String> {
None
self.conversation_id.clone()
}
async fn send(&self, _prompt: &str) -> Result<ReplyStream, AgentSessionError> {
let stream: ReplyStream = Box::new(std::iter::empty());
@ -61,7 +62,17 @@ impl AgentSession for FakeSession {
}
fn fake(id: SessionId) -> Arc<dyn AgentSession> {
Arc::new(FakeSession { id })
Arc::new(FakeSession {
id,
conversation_id: None,
})
}
fn fake_with_conversation(id: SessionId, conversation_id: &str) -> Arc<dyn AgentSession> {
Arc::new(FakeSession {
id,
conversation_id: Some(conversation_id.to_owned()),
})
}
// ===========================================================================
@ -107,16 +118,19 @@ fn structured_insert_resolve_and_remove() {
}
#[test]
fn structured_meta_for_session_resolves_agent_and_node() {
fn structured_meta_for_session_resolves_agent_node_and_conversation() {
// LS7 (§21.10) : le tap niveau 1 n'a que le `SessionId` du tour et doit retrouver
// l'agent + la cellule hôte à passer au service de limite. Lookup direct par id.
let reg = StructuredSessions::new();
let s = sid(1);
let a = aid(10);
let n = nid(100);
reg.insert(fake(s), a, n);
reg.insert(fake_with_conversation(s, "conv-live"), a, n);
assert_eq!(reg.meta_for_session(&s), Some((a, n)));
assert_eq!(
reg.meta_for_session(&s),
Some((a, n, Some("conv-live".to_owned())))
);
// Id inconnu (ou retiré) ⇒ None (jamais de panique sur une session morte).
assert_eq!(reg.meta_for_session(&sid(999)), None);