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

@ -17,7 +17,7 @@ use async_trait::async_trait;
use domain::agent::{AgentManifest, ManifestEntry};
use domain::events::{DomainEvent, OrchestrationSource};
use domain::ids::SkillId;
use domain::ids::{AgentId, ProfileId, ProjectId};
use domain::ids::{AgentId, ProfileId, ProjectId, RuntimeAgentKey};
use domain::markdown::MarkdownDoc;
use domain::ports::{
AgentContextStore, AgentRuntime, AgentSession, AgentSessionError, AgentSessionFactory,
@ -840,12 +840,13 @@ async fn ask_request_surfaces_reply_alongside_detail() {
let svc = Arc::clone(&service);
let proj = project();
let runtime_key = RuntimeAgentKey::new(proj.id, agent_id);
let ask_req = req.clone();
let ask = tokio::spawn(async move { process_request_file(&ask_req, &proj, &svc).await });
// Wait until the ask has enqueued its ticket (blocked awaiting the reply).
tokio::time::timeout(std::time::Duration::from_secs(10), async {
while mailbox.pending(&agent_id) == 0 {
while mailbox.pending(&runtime_key) == 0 {
tokio::task::yield_now().await;
}
})
@ -918,11 +919,12 @@ async fn ask_request_no_reply_persists_failed_rendezvous_task() {
let svc = Arc::clone(&service);
let proj = project();
let runtime_key = RuntimeAgentKey::new(proj.id, agent_id);
let ask_req = req.clone();
let ask = tokio::spawn(async move { process_request_file(&ask_req, &proj, &svc).await });
tokio::time::timeout(std::time::Duration::from_secs(10), async {
while mailbox.pending(&agent_id) == 0 {
while mailbox.pending(&runtime_key) == 0 {
tokio::task::yield_now().await;
}
})
@ -951,7 +953,11 @@ async fn ask_request_no_reply_persists_failed_rendezvous_task() {
}
other => panic!("expected failure result, got {other:?}"),
}
assert_eq!(mailbox.pending(&agent_id), 0, "turn-lock mailbox is freed");
assert_eq!(
mailbox.pending(&runtime_key),
0,
"turn-lock mailbox is freed"
);
}
/// Point 2 — a non-`ask` command (here `spawn_agent`, reply `None`) ⇒ the `reply`