feat: event-driven parity, Windows shell detection, dirty build timestamps, and gatekeeper integration

This commit is contained in:
Riz Ashraf committed 2026-10-08 10:52:19 +01:00
1 parent 64857f9d5e
commit f4dcf3acbf
33 files changed
+3501 -143

No files matched your search

+99 -10
View File
@@ -96,7 +96,62 @@ pub struct AppState {
pub async fn ttl_sweeper_worker(state: Arc<MemoryState>) {
loop {
state.ttl_notify.notified().await;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let mut next_expiry: Option<u64> = None;
state.project.tasks.read_with(|tasks| {
for t in tasks.iter() {
if let Some(exp) = t.expires_at {
if t.is_active() {
next_expiry = Some(next_expiry.map_or(exp, |curr| curr.min(exp)));
}
}
}
});
state.telemetry.handoff_memos.read_with(|memos| {
for m in memos.iter() {
if let Some(exp) = m.expires_at {
next_expiry = Some(next_expiry.map_or(exp, |curr| curr.min(exp)));
}
}
});
state.telemetry.session_summaries.read_with(|summaries| {
for s in summaries.iter() {
if let Some(exp) = s.expires_at {
next_expiry = Some(next_expiry.map_or(exp, |curr| curr.min(exp)));
}
}
});
state.env.gates.read_with(|gates| {
for g in gates.iter() {
if let Some(exp) = g.expires_at {
next_expiry = Some(next_expiry.map_or(exp, |curr| curr.min(exp)));
}
}
});
let sleep_duration = match next_expiry {
Some(exp) if exp > now => {
let diff = exp - now;
std::time::Duration::from_secs(diff.min(60).max(1))
}
Some(_) => std::time::Duration::from_millis(50),
None => std::time::Duration::from_secs(60),
};
tokio::select! {
_ = state.shutdown_notify.notified() => break,
_ = state.ttl_notify.notified() => {},
_ = tokio::time::sleep(sleep_duration) => {},
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
@@ -139,12 +194,18 @@ pub async fn ttl_sweeper_worker(state: Arc<MemoryState>) {
state.telemetry.session_summaries.modify(|summaries| {
summaries.retain(|s| s.expires_at.is_none_or(|exp| exp > now));
});
state.env.gates.modify(|gates| {
gates.retain(|g| g.expires_at.is_none_or(|exp| exp > now));
});
}
}
pub async fn index_committer_worker(state: Arc<MemoryState>) {
loop {
state.index_commit_notify.notified().await;
tokio::select! {
_ = state.shutdown_notify.notified() => break,
_ = state.index_commit_notify.notified() => {},
}
let idx = state.search_index.read().await.clone();
let _ = idx.commit().await;
}
@@ -152,7 +213,10 @@ pub async fn index_committer_worker(state: Arc<MemoryState>) {
pub async fn condense_graph_worker(state: Arc<MemoryState>) {
loop {
state.condense_notify.notified().await;
tokio::select! {
_ = state.shutdown_notify.notified() => break,
_ = state.condense_notify.notified() => {},
}
let threshold: usize = std::env::var("MCP_MEMORY_CONDENSE_THRESHOLD")
.unwrap_or_else(|_| "100".to_string())
@@ -224,6 +288,7 @@ pub async fn run_server(state: Arc<MemoryState>) -> Result<(), Box<dyn std::erro
tokio::spawn(ttl_sweeper_worker(Arc::clone(&state)));
tokio::spawn(condense_graph_worker(Arc::clone(&state)));
crate::watcher::spawn_watcher(Arc::clone(&state));
crate::handlers::vision::spawn_clipboard_listener(Arc::clone(&state));
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel();
let app_state = Arc::new(AppState {
handler: Arc::new(MemoryHandler::new(Arc::clone(&state))),
@@ -372,14 +437,38 @@ pub async fn run_server(state: Arc<MemoryState>) -> Result<(), Box<dyn std::erro
let _ = nvim_udp_state.handler.state.event_bus_tx.send(event);
}
if payload.event.starts_with("agent_") {
let topic = format!("nvim:ui:{}", payload.event);
let event = crate::state::GenericEvent {
topic,
if payload.event.starts_with("agent_") || payload.event.starts_with("diff_") {
let payload_val = serde_json::json!(&payload);
// 1. General event topic (e.g. nvim:ui:agent_prompt_response, nvim:ui:agent_diff_accepted)
let _ = nvim_udp_state.handler.state.event_bus_tx.send(crate::state::GenericEvent {
topic: format!("nvim:ui:{}", payload.event),
session_id: Some(payload.session_id.clone()),
payload: serde_json::json!(&payload),
};
let _ = nvim_udp_state.handler.state.event_bus_tx.send(event);
payload: payload_val.clone(),
});
// 2. Correlated request_id topic (e.g. nvim:ui:agent_prompt_response:REQ_ID)
if let Some(ref req_id) = payload.request_id {
let _ = nvim_udp_state.handler.state.event_bus_tx.send(crate::state::GenericEvent {
topic: format!("nvim:ui:{}:{}", payload.event, req_id),
session_id: Some(payload.session_id.clone()),
payload: payload_val.clone(),
});
}
// 3. Correlated diff_id topics
if let Some(ref diff_id) = payload.diff_id {
let _ = nvim_udp_state.handler.state.event_bus_tx.send(crate::state::GenericEvent {
topic: format!("nvim:ui:{}:{}", payload.event, diff_id),
session_id: Some(payload.session_id.clone()),
payload: payload_val.clone(),
});
// General diff decision topic
let _ = nvim_udp_state.handler.state.event_bus_tx.send(crate::state::GenericEvent {
topic: format!("nvim:ui:diff_decision:{}", diff_id),
session_id: Some(payload.session_id.clone()),
payload: payload_val.clone(),
});
}
}
}
}