fix: fetch descriptions from graph agent configs as well when listing agents

This commit is contained in:
2026-07-22 12:59:30 -06:00
parent 420db4bb88
commit ab0b89dd67
3 changed files with 62 additions and 18 deletions
-2
View File
@@ -8,8 +8,6 @@ description: |
sisyphus alongside explore when unfamiliar libraries/APIs/frameworks are sisyphus alongside explore when unfamiliar libraries/APIs/frameworks are
involved. involved.
Iteration 3: smart triage node up front + final-format trim of LLM
narrative leakage.
version: "1.0" version: "1.0"
global_tools: global_tools:
+53 -4
View File
@@ -1020,15 +1020,30 @@ pub fn list_agents_with_descriptions() -> Vec<(String, String)> {
list_agents() list_agents()
.into_iter() .into_iter()
.map(|name| { .map(|name| {
let description = AgentConfig::load(&paths::agent_config_file(&name)) let description = load_agent_description(&name);
.ok()
.map(|c| c.description)
.unwrap_or_default();
(name, description) (name, description)
}) })
.collect() .collect()
} }
#[derive(Deserialize)]
struct AgentMetadataStub {
#[serde(default)]
description: String,
}
fn load_agent_description(name: &str) -> String {
if let Ok(config) = AgentConfig::load(&paths::agent_config_file(name)) {
return config.description;
}
if let Ok(contents) = read_to_string(paths::agent_graph_file(name))
&& let Ok(meta) = serde_yaml::from_str::<AgentMetadataStub>(&contents)
{
return meta.description;
}
String::new()
}
pub fn complete_agent_variables(agent_name: &str) -> Vec<(String, Option<String>)> { pub fn complete_agent_variables(agent_name: &str) -> Vec<(String, Option<String>)> {
let config_path = paths::agent_config_file(agent_name); let config_path = paths::agent_config_file(agent_name);
if !config_path.exists() { if !config_path.exists() {
@@ -1218,4 +1233,38 @@ variables:
assert_eq!(config.max_agent_depth, default_max_agent_depth()); assert_eq!(config.max_agent_depth, default_max_agent_depth());
assert_eq!(config.escalation_timeout, default_escalation_timeout()); assert_eq!(config.escalation_timeout, default_escalation_timeout());
} }
#[test]
fn agent_metadata_stub_extracts_description_from_graph_yaml() {
let yaml = r#"
name: librarian
description: External-reference research agent.
version: "1.0"
start: triage
nodes: {}
"#;
let meta: AgentMetadataStub = serde_yaml::from_str(yaml).unwrap();
assert_eq!(meta.description, "External-reference research agent.");
}
#[test]
fn agent_metadata_stub_extracts_multiline_description() {
let yaml = r#"
name: coder
description: |
Implementation agent. Plans, implements, and runs build + tests in a
bounded fix-loop until verified.
version: "1.0"
"#;
let meta: AgentMetadataStub = serde_yaml::from_str(yaml).unwrap();
assert!(meta.description.starts_with("Implementation agent."));
assert!(meta.description.contains("bounded fix-loop"));
}
#[test]
fn agent_metadata_stub_defaults_when_description_missing() {
let yaml = "name: nameless\nversion: \"1.0\"\n";
let meta: AgentMetadataStub = serde_yaml::from_str(yaml).unwrap();
assert_eq!(meta.description, "");
}
} }
+9 -12
View File
@@ -10,8 +10,8 @@ use super::{
AGENTS_DIR_NAME, Agent, AgentVariables, AppConfig, AppState, AssetCategory, CREATE_TITLE_ROLE, AGENTS_DIR_NAME, Agent, AgentVariables, AppConfig, AppState, AssetCategory, CREATE_TITLE_ROLE,
Input, InstallFilter, LEFT_PROMPT, LastMessage, MESSAGES_FILE_NAME, RIGHT_PROMPT, Role, Input, InstallFilter, LEFT_PROMPT, LastMessage, MESSAGES_FILE_NAME, RIGHT_PROMPT, Role,
RoleLike, SESSIONS_DIR_NAME, SUMMARIZATION_PROMPT, SUMMARY_CONTEXT_PROMPT, StateFlags, RoleLike, SESSIONS_DIR_NAME, SUMMARIZATION_PROMPT, SUMMARY_CONTEXT_PROMPT, StateFlags,
TEMP_ROLE_NAME, TEMP_SESSION_NAME, WorkingMode, ensure_parent_exists, list_agents, memory, TEMP_ROLE_NAME, TEMP_SESSION_NAME, WorkingMode, ensure_parent_exists, list_agents,
paths, list_agents_with_descriptions, memory, paths,
}; };
use super::{MessageContentToolCalls, prompts}; use super::{MessageContentToolCalls, prompts};
use crate::client::{Model, ModelType, list_models}; use crate::client::{Model, ModelType, list_models};
@@ -2338,21 +2338,18 @@ impl RequestContext {
"rags" => print_asset_names("RAGs", &paths::list_rags()), "rags" => print_asset_names("RAGs", &paths::list_rags()),
"macros" => print_asset_names("macros", &paths::list_macros()), "macros" => print_asset_names("macros", &paths::list_macros()),
"agents" => { "agents" => {
let names = list_agents(); let entries = list_agents_with_descriptions();
if names.is_empty() { if entries.is_empty() {
println!("No agents found."); println!("No agents found.");
return Ok(()); return Ok(());
} }
println!("Agents:"); println!("Agents:");
for name in names { for (name, description) in entries {
let description = AgentConfig::load(&paths::agent_config_file(&name)) if description.is_empty() {
.ok() println!("{name}");
.map(|c| c.description) } else {
.filter(|d| !d.is_empty()); println!("{name}{description}");
match description {
Some(description) => println!("{name}{description}"),
None => println!("{name}"),
} }
} }