diff --git a/AGENTS.md b/AGENTS.md
index 980798a..35f6193 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -73,7 +73,7 @@ Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionM
### Key Constraints
- Gateway **changes working directory** to workspace on startup (`src/gateway/mod.rs:31`)
-- Session/message persistence uses SQLite via `sqlx`; DB stored in workspace as `.picobot_sessions.db` by default
+- Session/message persistence uses SQLite via `sqlx`; DB stored in workspace as `picobot.db` by default
- `ChannelManager` owns the `MessageBus` and all channel instances
- `OutboundDispatcher` routes outbound messages to the correct channel via `ChannelManager`
- Config `.env` loading uses `unsafe { env::set_var(...) }` — don't refactor to safer patterns without understanding side effects
diff --git a/README.md b/README.md
index 5c5048d..c0aada6 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ graph TB
end
subgraph Storage
- SQLite[("SQLite
.picobot_sessions.db")]
+ SQLite[("SQLite
picobot.db")]
end
subgraph AI["AI Providers"]
@@ -236,7 +236,7 @@ The `.env` file in the working directory is loaded manually (not via dotenv crat
| `port` | u16 | `19876` | Listen port |
| `session_ttl_hours` | number | `4` | Inactive session expiration (hours) |
| `cleanup_interval_minutes` | number | `60` | Session cleanup interval |
-| `session_db_path` | string | workspace `.picobot_sessions.db` | SQLite database path |
+| `session_db_path` | string | workspace `picobot.db` | SQLite database path |
| `scheduler.enabled` | bool | `false` | Enable cron scheduler |
### Agent Config
diff --git a/src/agent/context_compressor.rs b/src/agent/context_compressor.rs
index f0ab650..784d858 100644
--- a/src/agent/context_compressor.rs
+++ b/src/agent/context_compressor.rs
@@ -59,7 +59,7 @@ impl Default for ContextCompressionConfig {
pub struct ContextCompressor {
config: ContextCompressionConfig,
context_window: usize,
- /// Threshold ratio to trigger compression (50% of context window)
+ /// Threshold ratio to trigger compression (70% of context window)
threshold_ratio: f64,
/// Shared LLM provider for summarization
provider: Arc,
@@ -86,7 +86,7 @@ impl ContextCompressor {
Self {
config: ContextCompressionConfig::default(),
context_window,
- threshold_ratio: 0.5,
+ threshold_ratio: 0.7,
provider,
memory,
session_id: None,
@@ -103,7 +103,7 @@ impl ContextCompressor {
Self {
config,
context_window,
- threshold_ratio: 0.5,
+ threshold_ratio: 0.7,
provider,
memory,
session_id: None,
diff --git a/src/agent/system_prompt.rs b/src/agent/system_prompt.rs
index 116850a..4076312 100644
--- a/src/agent/system_prompt.rs
+++ b/src/agent/system_prompt.rs
@@ -259,7 +259,7 @@ impl PromptSection for CrossChannelSection {
### chat_manager 工具
管理会话和查看消息。参数:
-- action = "list_sessions" — 列出最近活跃的会话
+- action = "list_sessions" — 列出全部会话,支持通过 offset/count 翻页
- action = "list_channels" — 列出所有可用渠道
- action = "list_messages" — 查看指定 session 的历史消息,支持以下参数:
- session_id (必填): 会话 ID
diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs
index b6104f8..6e18770 100644
--- a/src/gateway/mod.rs
+++ b/src/gateway/mod.rs
@@ -41,14 +41,11 @@ impl GatewayState {
// Override workspace_dir with the ensured path
provider_config.workspace_dir = workspace_path.clone();
- // Session TTL from config (default 4 hours)
- let session_ttl_hours = config.gateway.session_ttl_hours.unwrap_or(4);
-
// Initialize Storage
let db_path = if let Some(ref path) = config.gateway.session_db_path {
std::path::PathBuf::from(path)
} else {
- workspace_path.join(".picobot_sessions.db")
+ workspace_path.join("picobot.db")
};
let storage = Arc::new(
crate::storage::Storage::new(&db_path).await
@@ -79,7 +76,6 @@ impl GatewayState {
// Create SessionManager with bus injection
let session_manager = SessionManager::new(
- session_ttl_hours,
provider_config.clone(),
storage.clone(),
bus.clone(),
@@ -87,11 +83,6 @@ impl GatewayState {
)?;
let session_manager = Arc::new(session_manager);
- // Start background cleanup task (default 60 minutes)
- let cleanup_interval = config.gateway.cleanup_interval_minutes.unwrap_or(60);
- session_manager.clone().start_cleanup_task(cleanup_interval);
- tracing::info!("Session cleanup task started (interval: {} min)", cleanup_interval);
-
// Create ChannelManager and init channels
let cli_chat_channel = Arc::new(CliChatChannel::new());
let channel_manager = ChannelManager::with_bus(cli_chat_channel, bus);
diff --git a/src/session/session.rs b/src/session/session.rs
index a07eace..f05a1e4 100644
--- a/src/session/session.rs
+++ b/src/session/session.rs
@@ -1,6 +1,5 @@
use std::collections::HashMap;
use std::sync::Arc;
-use std::time::{Duration, Instant};
use tokio::sync::Mutex;
use uuid::Uuid;
@@ -730,8 +729,6 @@ pub struct SessionManager {
struct SessionManagerInner {
/// Sessions keyed by UnifiedSessionId.to_string()
sessions: HashMap>>,
- session_timestamps: HashMap,
- session_ttl: Duration,
/// Current active session per channel:chat_id
current_sessions: HashMap,
}
@@ -808,7 +805,6 @@ pub static SLASH_COMMANDS: &[SlashCommand] = &[
impl SessionManager {
pub fn new(
- session_ttl_hours: u64,
provider_config: LLMProviderConfig,
storage: Arc,
bus: Arc,
@@ -823,8 +819,6 @@ impl SessionManager {
Ok(Self {
inner: Arc::new(Mutex::new(SessionManagerInner {
sessions: HashMap::new(),
- session_timestamps: HashMap::new(),
- session_ttl: Duration::from_secs(session_ttl_hours * 3600),
current_sessions: HashMap::new(),
})),
provider_config,
@@ -847,42 +841,6 @@ impl SessionManager {
self.tools.clone()
}
- /// 启动后台 TTL 清理任务
- pub fn start_cleanup_task(self: Arc, interval_mins: u64) {
- let cleanup_interval = Duration::from_secs(interval_mins * 60);
- tokio::spawn(async move {
- loop {
- tokio::time::sleep(cleanup_interval).await;
- self.run_cleanup().await;
- }
- });
- }
-
- /// 执行一次 TTL 清理:释放内存中过期的 session,Storage 记录保留
- async fn run_cleanup(&self) {
- let inner = self.inner.lock().await;
- let now = Instant::now();
- let ttl = inner.session_ttl;
-
- let expired: Vec = inner
- .session_timestamps
- .iter()
- .filter(|(_, last_touch)| now.duration_since(**last_touch) > ttl)
- .map(|(id, _)| id.clone())
- .collect();
-
- drop(inner);
-
- if !expired.is_empty() {
- let mut inner = self.inner.lock().await;
- for id in &expired {
- inner.sessions.remove(id);
- inner.session_timestamps.remove(id);
- }
- tracing::debug!(count = expired.len(), "Cleaned up expired sessions");
- }
- }
-
/// 获取所有可用的斜杠命令
pub fn get_slash_commands(&self) -> &[SlashCommand] {
SLASH_COMMANDS
@@ -1071,7 +1029,6 @@ impl SessionManager {
let arc = Arc::new(Mutex::new(session));
let inner = &mut *self.inner.lock().await;
inner.sessions.insert(session_id_str.clone(), arc.clone());
- inner.session_timestamps.insert(session_id_str.clone(), Instant::now());
// Set as current session for this channel:chat_id
let chat_scope = format!("{}:{}", channel, chat_id);
inner.current_sessions.insert(chat_scope, session_id_str);
@@ -1084,7 +1041,6 @@ impl SessionManager {
let inner = &mut *self.inner.lock().await;
if let Some(session) = inner.sessions.get(&session_id_str) {
- inner.session_timestamps.insert(session_id_str, Instant::now());
return Ok(session.clone());
}
@@ -1102,7 +1058,6 @@ impl SessionManager {
let arc = Arc::new(Mutex::new(session));
inner.sessions.insert(session_id_str.clone(), arc.clone());
- inner.session_timestamps.insert(session_id_str.clone(), Instant::now());
// Set as current session
let chat_scope = format!("{}:{}", unified_id.channel, unified_id.chat_id);
inner.current_sessions.insert(chat_scope, session_id_str);
@@ -1126,7 +1081,6 @@ impl SessionManager {
let arc = Arc::new(Mutex::new(session));
inner.sessions.insert(session_id_str.clone(), arc.clone());
- inner.session_timestamps.insert(session_id_str.clone(), Instant::now());
// Set as current session
let chat_scope = format!("{}:{}", unified_id.channel, unified_id.chat_id);
inner.current_sessions.insert(chat_scope, session_id_str);
@@ -1209,7 +1163,6 @@ impl SessionManager {
// Remove from memory and current sessions
let mut inner = self.inner.lock().await;
inner.sessions.remove(&session_id_str);
- inner.session_timestamps.remove(&session_id_str);
let chat_scope = format!("{}:{}", session_id.channel, session_id.chat_id);
inner.current_sessions.remove(&chat_scope);
@@ -1262,8 +1215,7 @@ impl SessionManager {
}
}
- let ttl_millis = self.inner.lock().await.session_ttl.as_millis() as i64;
- match self.storage.find_active_session(channel, chat_id, ttl_millis).await {
+ match self.storage.find_most_recent_session(channel, chat_id).await {
Ok(Some(meta)) => Ok(UnifiedSessionId::new(channel, chat_id, &meta.dialog_id)),
_ => {
let (new_id, _) = self.create_session(channel, chat_id, None, String::new()).await?;
diff --git a/src/storage/mod.rs b/src/storage/mod.rs
index 17d7fd8..d7cdac5 100644
--- a/src/storage/mod.rs
+++ b/src/storage/mod.rs
@@ -457,25 +457,22 @@ impl Storage {
Ok(())
}
- pub async fn find_active_session(
+ pub async fn find_most_recent_session(
&self,
channel: &str,
chat_id: &str,
- ttl_millis: i64,
) -> Result