Show human-readable descriptions in /resume session list
- Add description field to SessionContinuation struct - Extract first user message (truncated to ~60 chars at word boundary) - Display as quoted text instead of session ID hash - Fall back to session ID if no description available Example: [2 hours ago] 'when I call /resume it only shows me 2 sessions...'
This commit is contained in:
@@ -1852,16 +1852,18 @@ async fn run_interactive<W: UiWriter>(
|
|||||||
};
|
};
|
||||||
let todo_marker = if session.has_incomplete_todos() { " 📝" } else { "" };
|
let todo_marker = if session.has_incomplete_todos() { " 📝" } else { "" };
|
||||||
|
|
||||||
// Truncate session ID for display
|
// Use description if available, otherwise fall back to session ID
|
||||||
let display_id = if session.session_id.len() > 40 {
|
let display_name = match &session.description {
|
||||||
|
Some(desc) => format!("'{}'", desc),
|
||||||
|
None => if session.session_id.len() > 40 {
|
||||||
format!("{}...", &session.session_id[..40])
|
format!("{}...", &session.session_id[..40])
|
||||||
} else {
|
} else {
|
||||||
session.session_id.clone()
|
session.session_id.clone()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
output.print(&format!(
|
output.print(&format!(
|
||||||
" {}. [{}] {} ({}){}{}",
|
" {}. [{}] {} ({}){}{}",
|
||||||
i + 1, time_str, display_id, context_str, todo_marker, current_marker
|
i + 1, time_str, display_name, context_str, todo_marker, current_marker
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
output.print("");
|
output.print("");
|
||||||
|
|||||||
@@ -1405,10 +1405,20 @@ impl<W: UiWriter> Agent<W> {
|
|||||||
.map(|p| p.to_string_lossy().to_string())
|
.map(|p| p.to_string_lossy().to_string())
|
||||||
.unwrap_or_else(|_| ".".to_string());
|
.unwrap_or_else(|_| ".".to_string());
|
||||||
|
|
||||||
|
// Get description from first user message (strip "Task: " prefix if present)
|
||||||
|
let description = self.context_window.conversation_history.iter()
|
||||||
|
.find(|m| matches!(m.role, g3_providers::MessageRole::User))
|
||||||
|
.map(|m| {
|
||||||
|
let content = m.content.strip_prefix("Task: ").unwrap_or(&m.content);
|
||||||
|
// Truncate to ~60 chars for display, ending at word boundary
|
||||||
|
truncate_to_word_boundary(content, 60)
|
||||||
|
});
|
||||||
|
|
||||||
let continuation = SessionContinuation::new(
|
let continuation = SessionContinuation::new(
|
||||||
self.is_agent_mode,
|
self.is_agent_mode,
|
||||||
self.agent_name.clone(),
|
self.agent_name.clone(),
|
||||||
session_id,
|
session_id,
|
||||||
|
description,
|
||||||
summary,
|
summary,
|
||||||
session_log_path.to_string_lossy().to_string(),
|
session_log_path.to_string_lossy().to_string(),
|
||||||
self.context_window.percentage_used(),
|
self.context_window.percentage_used(),
|
||||||
@@ -2831,6 +2841,24 @@ impl<W: UiWriter> Agent<W> {
|
|||||||
// Re-export utility functions
|
// Re-export utility functions
|
||||||
pub use utils::apply_unified_diff_to_string;
|
pub use utils::apply_unified_diff_to_string;
|
||||||
|
|
||||||
|
/// Truncate a string to approximately max_len characters, ending at a word boundary
|
||||||
|
fn truncate_to_word_boundary(s: &str, max_len: usize) -> String {
|
||||||
|
if s.len() <= max_len {
|
||||||
|
return s.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the last space before max_len
|
||||||
|
let truncated = &s[..max_len];
|
||||||
|
if let Some(last_space) = truncated.rfind(' ') {
|
||||||
|
if last_space > max_len / 2 {
|
||||||
|
// Only use word boundary if it's not too short
|
||||||
|
return format!("{}...", &s[..last_space]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall back to character truncation
|
||||||
|
format!("{}...", truncated)
|
||||||
|
}
|
||||||
|
|
||||||
// Implement Drop to clean up safaridriver process
|
// Implement Drop to clean up safaridriver process
|
||||||
impl<W: UiWriter> Drop for Agent<W> {
|
impl<W: UiWriter> Drop for Agent<W> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ pub struct SessionContinuation {
|
|||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
/// Original session ID
|
/// Original session ID
|
||||||
pub session_id: String,
|
pub session_id: String,
|
||||||
|
/// Human-readable description (first user message, truncated)
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: Option<String>,
|
||||||
/// Session summary (last assistant response)
|
/// Session summary (last assistant response)
|
||||||
pub summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
/// Path to the full session log (g3_session_*.json)
|
/// Path to the full session log (g3_session_*.json)
|
||||||
@@ -50,6 +53,7 @@ impl SessionContinuation {
|
|||||||
is_agent_mode: bool,
|
is_agent_mode: bool,
|
||||||
agent_name: Option<String>,
|
agent_name: Option<String>,
|
||||||
session_id: String,
|
session_id: String,
|
||||||
|
description: Option<String>,
|
||||||
summary: Option<String>,
|
summary: Option<String>,
|
||||||
session_log_path: String,
|
session_log_path: String,
|
||||||
context_percentage: f32,
|
context_percentage: f32,
|
||||||
@@ -62,6 +66,7 @@ impl SessionContinuation {
|
|||||||
agent_name,
|
agent_name,
|
||||||
created_at: chrono::Utc::now().to_rfc3339(),
|
created_at: chrono::Utc::now().to_rfc3339(),
|
||||||
session_id,
|
session_id,
|
||||||
|
description,
|
||||||
summary,
|
summary,
|
||||||
session_log_path,
|
session_log_path,
|
||||||
context_percentage,
|
context_percentage,
|
||||||
|
|||||||
Reference in New Issue
Block a user