修复: run_command 空路径报错 + 隧道日志洪水 + 启动残留清理
- tool_registry: run_command working_dir 空字符串 "" → None,修 Windows os error 123 - lib: tunnel subscriber 从频率压制(suppress_until)改为连接状态感知(is_connected), 断开时静默丢弃事件,重连后恢复透传,记 INFO 状态变迁(治本) - conversation_repo: 新增 cleanup_stale_pending(),超 24h 残留 pending 标记 interrupted - restore: 启动时先清理过期 pending 再恢复审批 - state: 实现 cleanup_orphan_pending_messages(),启动时清理对应已决/超时 tool 的 __PENDING__ 占位消息
This commit is contained in:
@@ -24,6 +24,11 @@ use super::risk_from_str;
|
||||
/// 语义而非路由键)。conversation_id=None 的无主审批(R-9)不建 per_conv(无 conv_id 可挂),
|
||||
/// 仍进 pending_approvals 单层表,后续审批按 tool_call_id 路由,不影响正确性。
|
||||
pub async fn restore_pending_approvals(state: &AppState) {
|
||||
// 清理超 24 小时的残留 pending(旧会话遗留,不再有意义)
|
||||
if let Err(e) = state.ai_tool_executions.cleanup_stale_pending(86400).await {
|
||||
tracing::warn!("清理过期 pending 审批失败(非阻断): {}", e);
|
||||
}
|
||||
|
||||
let pending = match state.ai_tool_executions.list_pending().await {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
|
||||
@@ -2680,7 +2680,8 @@ fn register_file_tools(
|
||||
|
||||
let request = ShellRequest {
|
||||
command: command.clone(),
|
||||
working_dir: Some(working_dir.clone()),
|
||||
// 空字符串 → None(空路径是非法 current_dir,Windows 报 os error 123)
|
||||
working_dir: if working_dir.is_empty() { None } else { Some(working_dir.clone()) },
|
||||
env: HashMap::new(),
|
||||
timeout_secs: Some(timeout_secs),
|
||||
shell_type: Default::default(),
|
||||
|
||||
@@ -186,13 +186,29 @@ pub fn run() {
|
||||
let token = "devflow-relay-default-token".to_string();
|
||||
|
||||
// subscriber task:subscribe ai_event_bus → tunnel.send_raw_event 透传 miniapp
|
||||
// 治本:先查 is_connected()(AtomicBool 无锁读),未连接时静默丢弃事件,
|
||||
// 避免每次 send_raw_event 都失败并记 WARN(旧实现用 suppress_until 降频,是治标)。
|
||||
let mut rx = state.ai_event_bus.subscribe();
|
||||
let tunnel_for_sub = state.tunnel.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
tracing::info!("[tunnel-sub] subscriber task 启动,透传 ai_event_bus → relay");
|
||||
let mut was_connected = false;
|
||||
while let Ok(value) = rx.recv().await {
|
||||
if !tunnel_for_sub.is_connected() {
|
||||
if was_connected {
|
||||
tracing::info!("[tunnel-sub] tunnel 已断开,暂停透传");
|
||||
was_connected = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if !was_connected {
|
||||
tracing::info!("[tunnel-sub] tunnel 已重连,恢复透传");
|
||||
was_connected = true;
|
||||
}
|
||||
if let Err(e) = tunnel_for_sub.send_raw_event(value).await {
|
||||
tracing::warn!("[tunnel-sub] send_raw_event 失败(可能未连接): {}", e);
|
||||
// 连接刚断(查询与发送间窗口),记一条 DEBUG 而非 WARN
|
||||
tracing::debug!("[tunnel-sub] send_raw_event 失败(连接瞬断): {}", e);
|
||||
was_connected = false;
|
||||
}
|
||||
}
|
||||
tracing::info!("[tunnel-sub] subscriber task 退出(rx 关闭)");
|
||||
|
||||
@@ -264,6 +264,11 @@ impl AppState {
|
||||
// 从 Settings KV 恢复审批超时配置(默认 15 分钟,0=禁用超时)。
|
||||
state.reload_approval_timeout().await;
|
||||
|
||||
// 启动时清理残留 __PENDING__ 占位消息(对应 tool_execution 已非 pending 状态)
|
||||
if let Err(e) = cleanup_orphan_pending_messages(&state.db).await {
|
||||
tracing::warn!("清理孤儿 __PENDING__ 消息失败(非阻断): {}", e);
|
||||
}
|
||||
|
||||
// 迁移旧 .trash(编译期 workspace_root → 运行期 data_dir),仅一次,幂等。
|
||||
let old_trash = workspace_root_path().join(".trash");
|
||||
let new_trash = data_dir.join(".trash");
|
||||
@@ -554,6 +559,26 @@ fn build_registry(db: Arc<Database>) -> NodeRegistry {
|
||||
registry
|
||||
}
|
||||
|
||||
/// 启动时清理孤儿 `__PENDING__` 占位消息。
|
||||
///
|
||||
/// 对应 `tool_execution` 已非 pending(已审批/已超时/已中断)的占位消息是残留垃圾,
|
||||
/// 不删会导致前端展示冗余的 pending 态工具卡。
|
||||
pub async fn cleanup_orphan_pending_messages(db: &Database) -> anyhow::Result<u64> {
|
||||
let conn_arc = db.conn();
|
||||
let guard = conn_arc.lock().await;
|
||||
let deleted = guard.execute(
|
||||
"DELETE FROM ai_messages \
|
||||
WHERE (content = '__PENDING__' OR content LIKE '%__PENDING__%') \
|
||||
AND tool_call_id IS NOT NULL \
|
||||
AND tool_call_id NOT IN (\
|
||||
SELECT tool_call_id FROM ai_tool_executions WHERE status = 'pending'\
|
||||
)",
|
||||
[],
|
||||
)?;
|
||||
tracing::info!("清理孤儿 __PENDING__ 消息: {} 条", deleted);
|
||||
Ok(deleted as u64)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user