diff --git a/Cargo.toml b/Cargo.toml index ade2fef..bffb424 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ edition = "2024" [dependencies] reqwest = { version = "0.13.3", default-features = false, features = ["json", "rustls", "multipart"] } -dotenv = "0.15" serde = { version = "1.0", features = ["derive"] } regex = "1.12" serde_json = "1.0" @@ -35,7 +34,6 @@ crossterm = { version = "0.29", features = ["event-stream"] } termimad = "0.34" textwrap = "0.16" chrono = "0.4" -hostname = "0.4" sqlx = { version = "0.8", features = ["sqlite", "macros", "chrono", "runtime-tokio"] } jieba-rs = "0.9" which = "8" @@ -52,6 +50,9 @@ tar = "0.4" fantoccini = { version = "0.22", default-features = false, features = ["rustls-tls"] } portable-pty = "0.9" +[dev-dependencies] +dotenv = "0.15" + [build-dependencies] zstd = "0.13" tar = "0.4" diff --git a/src/agent/sub_agent.rs b/src/agent/sub_agent.rs index 31bd492..0404179 100644 --- a/src/agent/sub_agent.rs +++ b/src/agent/sub_agent.rs @@ -391,16 +391,15 @@ impl SubAgentManager { // Update DB: running if let Some(ref s) = storage { let _ = s - .update_background_task_status( - &tid, - "running", - None, - None, - Some(started_at), - None, - None, - None, - ) + .update_background_task_status(&tid, crate::storage::background_task::BackgroundTaskUpdate { + status: "running", + result: None, + error: None, + started_at: Some(started_at), + finished_at: None, + tool_calls_count: None, + iterations: None, + }) .await; } @@ -513,16 +512,15 @@ impl SubAgentManager { if let Some(ref s) = storage { let _ = s - .update_background_task_status( - &tid, - &status_str, - Some(&result.content), - error_val.as_deref(), - Some(started_at), - Some(finished_at), - Some(result.tool_calls_count as i64), - Some(result.iterations as i64), - ) + .update_background_task_status(&tid, crate::storage::background_task::BackgroundTaskUpdate { + status: &status_str, + result: Some(&result.content), + error: error_val.as_deref(), + started_at: Some(started_at), + finished_at: Some(finished_at), + tool_calls_count: Some(result.tool_calls_count as i64), + iterations: Some(result.iterations as i64), + }) .await; } @@ -544,13 +542,15 @@ impl SubAgentManager { let _ = storage .update_background_task_status( &task_id, - "cancelled", - None, - Some("gateway shutdown"), - None, - Some(chrono::Utc::now().timestamp_millis()), - None, - None, + crate::storage::background_task::BackgroundTaskUpdate { + status: "cancelled", + result: None, + error: Some("gateway shutdown"), + started_at: None, + finished_at: Some(chrono::Utc::now().timestamp_millis()), + tool_calls_count: None, + iterations: None, + }, ) .await; } @@ -568,13 +568,15 @@ impl SubAgentManager { if let Some(ref s) = self.storage { s.update_background_task_status( task_id, - "cancelled", - None, - None, - None, - Some(chrono::Utc::now().timestamp_millis()), - None, - None, + crate::storage::background_task::BackgroundTaskUpdate { + status: "cancelled", + result: None, + error: None, + started_at: None, + finished_at: Some(chrono::Utc::now().timestamp_millis()), + tool_calls_count: None, + iterations: None, + }, ) .await .map_err(|e| SubAgentError::Storage(e.to_string()))?; @@ -662,6 +664,16 @@ fn truncate_sub_agent_result(content: &str) -> (String, bool) { } } +fn summarize_for_notification(content: &str, _duration_ms: u64) -> String { + const MAX_SUMMARY_BYTES: usize = 500; + if content.len() <= MAX_SUMMARY_BYTES { + content.to_string() + } else { + let truncate_at = content.floor_char_boundary(MAX_SUMMARY_BYTES); + format!("{}...", &content[..truncate_at]) + } +} + #[cfg(test)] mod tests { use super::*; @@ -723,13 +735,3 @@ mod tests { assert!(matches!(error, SubAgentError::TooManyTasks(1))); } } - -fn summarize_for_notification(content: &str, _duration_ms: u64) -> String { - const MAX_SUMMARY_BYTES: usize = 500; - if content.len() <= MAX_SUMMARY_BYTES { - content.to_string() - } else { - let truncate_at = content.floor_char_boundary(MAX_SUMMARY_BYTES); - format!("{}...", &content[..truncate_at]) - } -} diff --git a/src/bus/message.rs b/src/bus/message.rs index b297eb9..6a60c46 100644 --- a/src/bus/message.rs +++ b/src/bus/message.rs @@ -264,12 +264,6 @@ pub struct InboundMessage { pub forwarded_metadata: HashMap, } -impl InboundMessage { - pub fn session_key(&self) -> String { - format!("{}:{}", self.channel, self.chat_id) - } -} - // ============================================================================ // OutboundMessage - Message from Agent to Channel (bot response) // ============================================================================ @@ -284,12 +278,6 @@ pub struct OutboundMessage { pub metadata: HashMap, } -impl OutboundMessage { - pub fn is_stream_delta(&self) -> bool { - self.metadata.contains_key("_stream_delta") - } -} - // ============================================================================ // ControlMessage - Message for control channel (session management) // Uses SessionCommand from session module diff --git a/src/channels/feishu.rs b/src/channels/feishu.rs index cb656e0..43a8e90 100644 --- a/src/channels/feishu.rs +++ b/src/channels/feishu.rs @@ -90,7 +90,6 @@ struct LarkEvent { #[derive(Deserialize)] struct LarkEventHeader { event_type: String, - #[allow(dead_code)] event_id: String, } @@ -122,19 +121,13 @@ struct LarkSenderId { } #[derive(Deserialize)] -#[allow(dead_code)] struct LarkMessage { message_id: String, chat_id: String, - chat_type: String, message_type: String, #[serde(default)] content: String, #[serde(default)] - mentions: Vec, - #[serde(default)] - root_id: Option, - #[serde(default)] parent_id: Option, } diff --git a/src/client/mod.rs b/src/client/mod.rs index 30a9f87..685b791 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -21,7 +21,7 @@ pub async fn run(gateway_url: &str) -> Result<(), Box> { let (ws_sender, ws_receiver) = ws_stream.split(); - let mut app = App::new(gateway_url.to_string()); + let mut app = App::new(); app.ws_sender = Some(ws_sender); app.ws_receiver = Some(ws_receiver); diff --git a/src/client/tui/app.rs b/src/client/tui/app.rs index 53e1356..7999e81 100644 --- a/src/client/tui/app.rs +++ b/src/client/tui/app.rs @@ -1,16 +1,7 @@ -#![allow(dead_code)] use crate::protocol::{SessionSummary, SlashCommandInfo}; use std::collections::VecDeque; use tokio_tungstenite::tungstenite::Message; -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum FocusArea { - TitleBar, - SessionList, - ChatHistory, - InputArea, -} - #[derive(Debug, Clone)] pub enum MessageRole { User, @@ -25,7 +16,6 @@ pub struct ChatMessage { } pub struct App { - pub gateway_url: String, pub ws_sender: Option< futures_util::stream::SplitSink< tokio_tungstenite::WebSocketStream< @@ -47,12 +37,10 @@ pub struct App { pub messages: VecDeque, - pub focus: FocusArea, pub input: String, pub input_cursor_pos: usize, pub show_help: bool, pub chat_scroll_offset: u16, - pub session_scroll_offset: u16, pub should_quit: bool, // Quit confirmation state (double Ctrl+C to exit) @@ -66,20 +54,17 @@ pub struct App { } impl App { - pub fn new(gateway_url: String) -> Self { + pub fn new() -> Self { Self { - gateway_url, ws_sender: None, ws_receiver: None, current_session_id: None, sessions: Vec::new(), messages: VecDeque::new(), - focus: FocusArea::InputArea, input: String::new(), input_cursor_pos: 0, show_help: false, chat_scroll_offset: 0, - session_scroll_offset: 0, should_quit: false, ctrl_c_count: 0, pending_quit: false, @@ -111,14 +96,6 @@ impl App { self.chat_scroll_offset = self.chat_scroll_offset.saturating_sub(1); } - pub fn scroll_session_up(&mut self) { - self.session_scroll_offset = self.session_scroll_offset.saturating_add(1); - } - - pub fn scroll_session_down(&mut self) { - self.session_scroll_offset = self.session_scroll_offset.saturating_sub(1); - } - pub fn input_insert_char(&mut self, c: char) { self.input.insert(self.input_cursor_pos, c); self.input_cursor_pos += 1; @@ -149,11 +126,6 @@ impl App { self.input_cursor_pos = self.input.len(); } - pub fn input_clear(&mut self) { - self.input.clear(); - self.input_cursor_pos = 0; - } - pub fn take_input(&mut self) -> String { let input = std::mem::take(&mut self.input); self.input_cursor_pos = 0; diff --git a/src/client/tui/markdown.rs b/src/client/tui/markdown.rs deleted file mode 100644 index 747d802..0000000 --- a/src/client/tui/markdown.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(dead_code)] - -pub fn render_markdown(content: &str) -> String { - content.to_string() -} diff --git a/src/client/tui/mod.rs b/src/client/tui/mod.rs index 1eda6d2..ff73966 100644 --- a/src/client/tui/mod.rs +++ b/src/client/tui/mod.rs @@ -1,5 +1,4 @@ pub mod app; pub mod components; pub mod event; -pub mod markdown; pub mod ui; diff --git a/src/logging.rs b/src/logging.rs index 2cdf0cd..2551d88 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -59,20 +59,3 @@ pub fn init_logging() { tracing::info!("Logging initialized. Log directory: {}", log_dir.display()); } - -/// Initialize logging without file output (console only) -pub fn init_logging_console_only() { - let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); - - let console_layer = fmt::layer() - .with_timer(LocalTime::rfc_3339()) - .with_target(true) - .with_level(true); - - tracing_subscriber::registry() - .with(env_filter) - .with(console_layer) - .init(); - - tracing::info!("Logging initialized (console only)"); -} diff --git a/src/mcp/mod.rs b/src/mcp/mod.rs index d23cdff..cfd5630 100644 --- a/src/mcp/mod.rs +++ b/src/mcp/mod.rs @@ -47,8 +47,6 @@ fn update_mcp_status(servers: Vec) { /// A connected MCP server. Holds a clonable Peer handle for tool calls, /// and keeps the underlying service alive via a background task. pub struct McpConnection { - #[allow(dead_code)] - pub name: String, peer: Peer, /// Keep the service alive. When dropped, the MCP connection is closed. _service: Option>, @@ -230,7 +228,6 @@ async fn connect_server(config: &McpServerConfig) -> anyhow::Result anyhow::Result Self { - Self { - output, - success: true, - error_reason: None, - duration, - } - } - /// Create a failed outcome with zero duration. pub fn failure(output: String, error_reason: Option) -> Self { Self { @@ -89,20 +79,6 @@ impl ToolExecutionOutcome { duration: Duration::ZERO, } } - - /// Create a failed outcome with duration. - pub fn failure_with_duration( - output: String, - error_reason: Option, - duration: Duration, - ) -> Self { - Self { - output, - success: false, - error_reason, - duration, - } - } } /// MultiObserver broadcasts events to multiple observers. @@ -204,16 +180,6 @@ mod tests { assert_eq!(outcome.duration, Duration::ZERO); } - #[test] - fn test_tool_execution_outcome_success_with_duration() { - let outcome = ToolExecutionOutcome::success_with_duration( - "output content".to_string(), - Duration::from_millis(100), - ); - assert!(outcome.success); - assert_eq!(outcome.duration, Duration::from_millis(100)); - } - #[test] fn test_tool_execution_outcome_failure() { let outcome = ToolExecutionOutcome::failure( diff --git a/src/providers/anthropic.rs b/src/providers/anthropic.rs index 67a389d..5a1f771 100644 --- a/src/providers/anthropic.rs +++ b/src/providers/anthropic.rs @@ -167,7 +167,6 @@ enum AnthropicContent { }, Thinking { #[serde(alias = "content")] - #[allow(dead_code)] thinking: String, }, #[serde(rename = "tool_use")] diff --git a/src/providers/traits.rs b/src/providers/traits.rs index d34ceac..84ce6ab 100644 --- a/src/providers/traits.rs +++ b/src/providers/traits.rs @@ -28,17 +28,6 @@ impl Message { } } - pub fn user_with_blocks(content: Vec) -> Self { - Self { - role: "user".to_string(), - content, - reasoning_content: None, - tool_call_id: None, - name: None, - tool_calls: None, - } - } - pub fn assistant(content: impl Into) -> Self { Self { role: "assistant".to_string(), diff --git a/src/session/session.rs b/src/session/session.rs index 7104eee..c97d934 100644 --- a/src/session/session.rs +++ b/src/session/session.rs @@ -82,7 +82,6 @@ pub struct Session { /// Messages before this time have been compressed into memory. pub last_consolidated_at: Option, pub last_compressed_message_at: Option, - #[allow(dead_code)] memory_manager: Arc, /// Task queue for per-session serial agent processing @@ -2681,28 +2680,3 @@ fn format_task_notification( crate::agent::TaskStatus::TimedOut => format!("📋 后台任务超时\n\n任务 ID: {}", task_id), } } - -#[cfg(test)] -mod tests { - use super::*; - use std::collections::HashMap; - - #[allow(dead_code)] - fn test_provider_config() -> LLMProviderConfig { - LLMProviderConfig { - provider_type: "openai".to_string(), - name: "test".to_string(), - base_url: "http://localhost".to_string(), - api_key: "test-key".to_string(), - extra_headers: HashMap::new(), - model_id: "test-model".to_string(), - temperature: Some(0.0), - max_tokens: Some(32), - model_extra: HashMap::new(), - max_tool_iterations: 1, - token_limit: 4096, - workspace_dir: std::path::PathBuf::from("/tmp/test-workspace"), - input_types: vec!["text".to_string()], - } - } -} diff --git a/src/storage/background_task.rs b/src/storage/background_task.rs index a01d1ed..669ee04 100644 --- a/src/storage/background_task.rs +++ b/src/storage/background_task.rs @@ -17,3 +17,13 @@ pub struct BackgroundTask { pub finished_at: Option, pub created_at: i64, } + +pub(crate) struct BackgroundTaskUpdate<'a> { + pub status: &'a str, + pub result: Option<&'a str>, + pub error: Option<&'a str>, + pub started_at: Option, + pub finished_at: Option, + pub tool_calls_count: Option, + pub iterations: Option, +} diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 811b66e..bc1d6bf 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1056,16 +1056,10 @@ impl Storage { Ok(()) } - pub async fn update_background_task_status( + pub(crate) async fn update_background_task_status( &self, id: &str, - status: &str, - result: Option<&str>, - error: Option<&str>, - started_at: Option, - finished_at: Option, - tool_calls_count: Option, - iterations: Option, + update: crate::storage::background_task::BackgroundTaskUpdate<'_>, ) -> Result<(), StorageError> { sqlx::query( r#" @@ -1077,13 +1071,13 @@ impl Storage { WHERE id = ? "#, ) - .bind(status) - .bind(result) - .bind(error) - .bind(started_at) - .bind(finished_at) - .bind(tool_calls_count) - .bind(iterations) + .bind(update.status) + .bind(update.result) + .bind(update.error) + .bind(update.started_at) + .bind(update.finished_at) + .bind(update.tool_calls_count) + .bind(update.iterations) .bind(id) .execute(self.pool()) .await?; @@ -1270,13 +1264,15 @@ mod tests { storage .update_background_task_status( &task.id, - "completed", - Some("done"), - None, - Some(2), - Some(3), - Some(4), - Some(5), + crate::storage::background_task::BackgroundTaskUpdate { + status: "completed", + result: Some("done"), + error: None, + started_at: Some(2), + finished_at: Some(3), + tool_calls_count: Some(4), + iterations: Some(5), + }, ) .await .unwrap(); diff --git a/src/tools/pty.rs b/src/tools/pty.rs index 8ac8641..e4b1951 100644 --- a/src/tools/pty.rs +++ b/src/tools/pty.rs @@ -96,11 +96,7 @@ enum SessionStatus { } struct PtySession { - #[allow(dead_code)] - id: String, - #[allow(dead_code)] command: String, - #[allow(dead_code)] started_at: Instant, status: SessionStatus, child: Arc>>>, @@ -111,13 +107,11 @@ struct PtySession { impl PtySession { fn new( - id: String, command: String, child: Box, writer: Box, ) -> Self { Self { - id, command, started_at: Instant::now(), status: SessionStatus::Running, @@ -225,8 +219,7 @@ impl PtyManager { .try_clone_reader() .map_err(|e| format!("Failed to clone reader: {}", e))?; - let session_id_clone = session_id.clone(); - let session = PtySession::new(session_id_clone, command.to_string(), child, writer); + let session = PtySession::new(command.to_string(), child, writer); let session = Arc::new(Mutex::new(session)); sessions.insert(session_id.clone(), session.clone());