huskies: merge 759

This commit is contained in:
dave
2026-04-28 00:02:33 +00:00
parent 9b24c2e281
commit 63ce7b9ec3
3 changed files with 42 additions and 14 deletions
+13
View File
@@ -30,6 +30,19 @@ impl AgentPool {
.collect()) .collect())
} }
/// Return story IDs of merge jobs currently in the `Running` state.
///
/// Used by `list_agents` and `get_pipeline_status` to surface in-flight
/// deterministic merges that hold the merge lock but have no agent entry.
pub fn list_running_merges(&self) -> Result<Vec<String>, String> {
let jobs = self.merge_jobs.lock().map_err(|e| e.to_string())?;
Ok(jobs
.values()
.filter(|job| matches!(job.status, crate::agents::merge::MergeJobStatus::Running))
.map(|job| job.story_id.clone())
.collect())
}
/// List all agents with their status. /// List all agents with their status.
pub fn list_agents(&self) -> Result<Vec<AgentInfo>, String> { pub fn list_agents(&self) -> Result<Vec<AgentInfo>, String> {
let agents = self.agents.lock().map_err(|e| e.to_string())?; let agents = self.agents.lock().map_err(|e| e.to_string())?;
+20 -7
View File
@@ -87,8 +87,7 @@ pub(crate) async fn tool_stop_agent(args: &Value, ctx: &AppContext) -> Result<St
pub(crate) fn tool_list_agents(ctx: &AppContext) -> Result<String, String> { pub(crate) fn tool_list_agents(ctx: &AppContext) -> Result<String, String> {
let project_root = ctx.services.agents.get_project_root(&ctx.state).ok(); let project_root = ctx.services.agents.get_project_root(&ctx.state).ok();
let agents = ctx.services.agents.list_agents()?; let agents = ctx.services.agents.list_agents()?;
serde_json::to_string_pretty(&json!( let mut entries: Vec<serde_json::Value> = agents
agents
.iter() .iter()
.filter(|a| { .filter(|a| {
project_root project_root
@@ -96,16 +95,30 @@ pub(crate) fn tool_list_agents(ctx: &AppContext) -> Result<String, String> {
.map(|root| !crate::service::agents::is_archived(root, &a.story_id)) .map(|root| !crate::service::agents::is_archived(root, &a.story_id))
.unwrap_or(true) .unwrap_or(true)
}) })
.map(|a| json!({ .map(|a| {
json!({
"story_id": a.story_id, "story_id": a.story_id,
"agent_name": a.agent_name, "agent_name": a.agent_name,
"status": a.status.to_string(), "status": a.status.to_string(),
"session_id": a.session_id, "session_id": a.session_id,
"worktree_path": a.worktree_path, "worktree_path": a.worktree_path,
})) })
.collect::<Vec<_>>() })
)) .collect();
.map_err(|e| format!("Serialization error: {e}"))
// Append a synthetic entry for each deterministic merge holding the merge lock.
let running_merges = ctx.services.agents.list_running_merges()?;
for story_id in running_merges {
entries.push(json!({
"story_id": story_id,
"agent_name": "deterministic-merge",
"status": "Running",
"session_id": null,
"worktree_path": null,
}));
}
serde_json::to_string_pretty(&json!(entries)).map_err(|e| format!("Serialization error: {e}"))
} }
/// Read agent session logs from disk and return a human-readable timeline. /// Read agent session logs from disk and return a human-readable timeline.
+2
View File
@@ -156,6 +156,7 @@ pub(crate) fn tool_list_upcoming(ctx: &AppContext) -> Result<String, String> {
pub(crate) fn tool_get_pipeline_status(ctx: &AppContext) -> Result<String, String> { pub(crate) fn tool_get_pipeline_status(ctx: &AppContext) -> Result<String, String> {
let state = load_pipeline_state(ctx)?; let state = load_pipeline_state(ctx)?;
let running_merges = ctx.services.agents.list_running_merges()?;
fn map_items(items: &[crate::http::workflow::UpcomingStory], stage: &str) -> Vec<Value> { fn map_items(items: &[crate::http::workflow::UpcomingStory], stage: &str) -> Vec<Value> {
items items
@@ -203,6 +204,7 @@ pub(crate) fn tool_get_pipeline_status(ctx: &AppContext) -> Result<String, Strin
"active": active, "active": active,
"backlog": backlog, "backlog": backlog,
"backlog_count": backlog.len(), "backlog_count": backlog.len(),
"deterministic_merges_in_flight": running_merges,
})) }))
.map_err(|e| format!("Serialization error: {e}")) .map_err(|e| format!("Serialization error: {e}"))
} }