-
Notifications
You must be signed in to change notification settings - Fork 0
[codex] fix routing and stale mission-help cleanup #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -533,6 +533,49 @@ impl Channel { | |
| Ok(agents.into_iter().find(|a| a.name == name)) | ||
| } | ||
|
|
||
| /// Resolve a user-supplied agent reference. | ||
| /// | ||
| /// Accepts a full UUID, short UUID, canonical name, nickname/display name, | ||
| /// or full display label. Returns `Ok(None)` when there is no match. | ||
| pub async fn resolve_agent_ref(&self, raw: &str) -> Result<Option<crate::agent::Agent>> { | ||
| let raw = raw.trim(); | ||
| if raw.is_empty() { | ||
| return Ok(None); | ||
| } | ||
|
|
||
| if let Ok(agent_id) = raw.parse::<AgentId>() { | ||
| return self.get_agent_state(agent_id).await; | ||
| } | ||
|
|
||
| let raw_lower = raw.to_ascii_lowercase(); | ||
| let agents = self.list_agents().await?; | ||
| let matches: Vec<_> = agents | ||
| .into_iter() | ||
| .filter(|agent| { | ||
| agent.name.eq_ignore_ascii_case(raw) | ||
| || agent.display_name().eq_ignore_ascii_case(raw) | ||
| || agent.display_label().eq_ignore_ascii_case(raw) | ||
| || agent.id.short_id().eq_ignore_ascii_case(&raw_lower) | ||
| }) | ||
| .collect(); | ||
|
|
||
| match matches.as_slice() { | ||
| [] => Ok(None), | ||
| [agent] => Ok(Some(agent.clone())), | ||
| _ => { | ||
| let labels = matches | ||
| .iter() | ||
| .map(|agent| format!("{} ({})", agent.display_label(), agent.id.short_id())) | ||
| .collect::<Vec<_>>() | ||
| .join(", "); | ||
| Err(crate::Error::Config(format!( | ||
| "Agent reference '{}' is ambiguous: {}", | ||
| raw, labels | ||
| ))) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Delete an agent from Redis. | ||
| pub async fn delete_agent(&self, agent_id: AgentId) -> Result<()> { | ||
| let mut conn = self.conn.clone(); | ||
|
|
@@ -948,6 +991,43 @@ impl Channel { | |
| Ok(messages) | ||
| } | ||
|
|
||
| /// Remove inbox messages matching a predicate while preserving order. | ||
| pub async fn remove_inbox_messages_matching<F>( | ||
| &self, | ||
| agent_id: AgentId, | ||
| mut predicate: F, | ||
| ) -> Result<usize> | ||
| where | ||
| F: FnMut(&Message) -> bool, | ||
| { | ||
| let mut conn = self.conn.clone(); | ||
| let inbox_key = self.inbox_key(agent_id); | ||
| let items: Vec<String> = conn.lrange(&inbox_key, 0, -1).await?; | ||
| if items.is_empty() { | ||
| return Ok(0); | ||
| } | ||
|
|
||
| let mut kept = Vec::with_capacity(items.len()); | ||
| let mut removed = 0usize; | ||
| for item in items { | ||
| match serde_json::from_str::<Message>(&item) { | ||
| Ok(message) if predicate(&message) => removed += 1, | ||
| _ => kept.push(item), | ||
| } | ||
| } | ||
|
|
||
| if removed == 0 { | ||
| return Ok(0); | ||
| } | ||
|
|
||
| let _: () = conn.del(&inbox_key).await?; | ||
| if !kept.is_empty() { | ||
| let _: () = conn.rpush(&inbox_key, kept).await?; | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-atomic inbox read-delete-write loses concurrent messagesMedium Severity
Reviewed by Cursor Bugbot for commit 10ae7f5. Configure here. |
||
|
|
||
| Ok(removed) | ||
| } | ||
|
|
||
| /// Move a message to another agent's inbox. | ||
| pub async fn move_message_to_inbox(&self, message: &Message, to_agent: AgentId) -> Result<()> { | ||
| // Create a new message with updated recipient | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MCP note handler omits
clear_help_request_stateunlike other pathsMedium Severity
The MCP
mission.notehandler discards the loaded mission withOk(Some(_)) => {}and never callsclear_help_request_state(), while every other operator acknowledgment path (CLI note, MCP pause/resume/stop, API resume/stop, and MCP reject) does call it and saves the mission. This means operator notes sent via MCP won't reset the dispatcher's remembered help-request state, so the dispatcher may re-send stale help requests to the conductor inbox even though the operator already responded.Additional Locations (1)
src/main.rs#L5004-L5012Reviewed by Cursor Bugbot for commit 10ae7f5. Configure here.