fix(audit): 修复深度审计 P1/P2 问题 — 记忆统一、持久化、前端适配
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

H3: 重写 memory_commands.rs 统一到 VikingStorage 单一存储,移除双写
H4: 心跳引擎 record_interaction() 持久化到 VikingStorage,启动时恢复
M4: 反思结果/状态持久化到 VikingStorage metadata,重启后自动恢复
- HandApprovalModal import 修正 (handStore 替代 gatewayStore)
- kernel-client.ts 幽灵调用替换为 kernel_status
- PersistentMemoryStore dead_code warnings 清理
- 审计报告和 README 更新至 v0.6.3,完成度 58%→62%
This commit is contained in:
iven
2026-03-27 09:59:55 +08:00
parent a71c4138cc
commit b7bc9ddcb1
11 changed files with 477 additions and 238 deletions

View File

@@ -376,10 +376,23 @@ fn get_last_interaction_map() -> &'static RwLock<StdHashMap<String, String>> {
/// Record an interaction for an agent (call from frontend when user sends message)
pub fn record_interaction(agent_id: &str) {
let now = chrono::Utc::now().to_rfc3339();
// Store in-memory map (fast path)
let map = get_last_interaction_map();
if let Ok(mut map) = map.write() {
map.insert(agent_id.to_string(), chrono::Utc::now().to_rfc3339());
map.insert(agent_id.to_string(), now.clone());
}
// Persist to VikingStorage metadata (survives restarts)
let key = format!("heartbeat:last_interaction:{}", agent_id);
tokio::spawn(async move {
if let Ok(storage) = crate::viking_commands::get_storage().await {
if let Err(e) = zclaw_growth::VikingStorage::store_metadata_json(&*storage, &key, &now).await {
tracing::warn!("[heartbeat] Failed to persist interaction time: {}", e);
}
}
});
}
/// Update memory stats cache for an agent
@@ -621,6 +634,9 @@ fn check_learning_opportunities(agent_id: &str) -> Option<HeartbeatAlert> {
pub type HeartbeatEngineState = Arc<Mutex<HashMap<String, HeartbeatEngine>>>;
/// Initialize heartbeat engine for an agent
///
/// Restores persisted interaction time from VikingStorage so idle-greeting
/// check works correctly across app restarts.
#[tauri::command]
pub async fn heartbeat_init(
agent_id: String,
@@ -628,11 +644,43 @@ pub async fn heartbeat_init(
state: tauri::State<'_, HeartbeatEngineState>,
) -> Result<(), String> {
let engine = HeartbeatEngine::new(agent_id.clone(), config);
// Restore last interaction time from VikingStorage metadata
restore_last_interaction(&agent_id).await;
let mut engines = state.lock().await;
engines.insert(agent_id, engine);
Ok(())
}
/// Restore the last interaction timestamp for an agent from VikingStorage.
/// Called during heartbeat_init so the idle-greeting check works after restart.
async fn restore_last_interaction(agent_id: &str) {
let key = format!("heartbeat:last_interaction:{}", agent_id);
match crate::viking_commands::get_storage().await {
Ok(storage) => {
match zclaw_growth::VikingStorage::get_metadata_json(&*storage, &key).await {
Ok(Some(timestamp)) => {
let map = get_last_interaction_map();
if let Ok(mut map) = map.write() {
map.insert(agent_id.to_string(), timestamp);
}
tracing::info!("[heartbeat] Restored last interaction for {}", agent_id);
}
Ok(None) => {
tracing::debug!("[heartbeat] No persisted interaction for {}", agent_id);
}
Err(e) => {
tracing::warn!("[heartbeat] Failed to restore interaction: {}", e);
}
}
}
Err(e) => {
tracing::warn!("[heartbeat] Storage unavailable during init: {}", e);
}
}
}
/// Start heartbeat engine for an agent
#[tauri::command]
pub async fn heartbeat_start(