Skip to content

Commit d9be99f

Browse files
authored
Merge pull request #231 from iOfficeAI/fix/team-rename-agent-dedup
fix(team): add name dedup check to rename_agent
2 parents c8e584b + 845e212 commit d9be99f

4 files changed

Lines changed: 80 additions & 2 deletions

File tree

crates/aionui-team/src/scheduler/agent_lifecycle.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,27 @@ impl TeammateManager {
4949
}
5050

5151
pub async fn rename_agent(&self, slot_id: &str, new_name: &str) -> Result<(), TeamError> {
52+
let normalized = super::normalize_name(new_name);
53+
if normalized.is_empty() {
54+
return Err(TeamError::InvalidRequest(
55+
"rename_agent.new_name is empty after normalization".into(),
56+
));
57+
}
58+
5259
let mut slots = self.slots.lock().await;
53-
let slot = slots
54-
.get_mut(slot_id)
60+
let _target = slots
61+
.get(slot_id)
5562
.ok_or_else(|| TeamError::AgentNotFound(slot_id.to_owned()))?;
63+
64+
// Check all other agents for name collision (exclude self).
65+
let conflict = slots
66+
.iter()
67+
.any(|(id, s)| id != slot_id && super::normalize_name(&s.agent.name) == normalized);
68+
if conflict {
69+
return Err(TeamError::DuplicateAgentName(new_name.to_owned()));
70+
}
71+
72+
let slot = slots.get_mut(slot_id).unwrap();
5673
slot.agent.name = new_name.to_owned();
5774
drop(slots);
5875
self.events.broadcast_agent_renamed(slot_id, new_name);

crates/aionui-team/src/scheduler/tests.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,38 @@ async fn rename_agent_broadcasts_renamed_event() {
426426
assert_eq!(renamed_events[0].data["name"], "Renamed Worker");
427427
}
428428

429+
#[tokio::test]
430+
async fn rename_agent_rejects_duplicate_name() {
431+
let agents = make_team_agents();
432+
let (mgr, _) = make_manager(&agents);
433+
434+
// "Worker2" already exists (from make_team_agents). Renaming worker-1
435+
// to " Worker2 " should collide after normalization.
436+
let err = mgr
437+
.rename_agent("worker-1", " Worker2 ")
438+
.await
439+
.expect_err("duplicate name must be rejected");
440+
assert!(
441+
matches!(&err, TeamError::DuplicateAgentName(_)),
442+
"expected DuplicateAgentName, got {err:?}"
443+
);
444+
445+
// Original name must be preserved.
446+
let agent = mgr.get_agent("worker-1").await.unwrap();
447+
assert_eq!(agent.name, "Worker1");
448+
}
449+
450+
#[tokio::test]
451+
async fn rename_agent_allows_same_agent_own_name() {
452+
let agents = make_team_agents();
453+
let (mgr, _) = make_manager(&agents);
454+
455+
// Renaming worker-1 to its own name (different casing) is not a conflict.
456+
mgr.rename_agent("worker-1", "WORKER1").await.unwrap();
457+
let agent = mgr.get_agent("worker-1").await.unwrap();
458+
assert_eq!(agent.name, "WORKER1");
459+
}
460+
429461
// -- execute_action: SendMessage -----------------------------------------
430462

431463
#[tokio::test]

crates/aionui-team/src/service.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,22 @@ impl TeamSessionService {
473473
.ok_or_else(|| TeamError::TeamNotFound(team_id.into()))?;
474474
let mut team = Team::from_row(&row)?;
475475

476+
let normalized = crate::scheduler::normalize_name(name);
477+
if normalized.is_empty() {
478+
return Err(TeamError::InvalidRequest(
479+
"rename_agent.name is empty after normalization".into(),
480+
));
481+
}
482+
483+
// Uniqueness check against all other agents in the team.
484+
let has_conflict = team
485+
.agents
486+
.iter()
487+
.any(|a| a.slot_id != slot_id && crate::scheduler::normalize_name(&a.name) == normalized);
488+
if has_conflict {
489+
return Err(TeamError::DuplicateAgentName(name.to_owned()));
490+
}
491+
476492
let agent = team
477493
.agents
478494
.iter_mut()

crates/aionui-team/src/session.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,19 @@ mod tests {
13671367
session.stop();
13681368
}
13691369

1370+
#[tokio::test]
1371+
async fn rename_agent_rejects_duplicate_in_session() {
1372+
let session = start_session().await;
1373+
let agents = session.scheduler.list_agents().await;
1374+
let lead_name = agents.iter().find(|a| a.slot_id == "lead-1").unwrap().name.clone();
1375+
1376+
// Rename worker-1 to the lead's name — should collide.
1377+
let result = session.rename_agent("worker-1", &lead_name).await;
1378+
assert!(result.is_err());
1379+
1380+
session.stop();
1381+
}
1382+
13701383
// -- spawn_agent helpers + guard tests -----------------------------------
13711384

13721385
fn sample_spawn_req() -> SpawnAgentRequest {

0 commit comments

Comments
 (0)