feat: Vestige v2.0.0 "Cognitive Leap" — 3D dashboard, HyDE search, WebSocket events

The biggest release in Vestige history. Complete visual and cognitive overhaul.

Dashboard:
- SvelteKit 2 + Three.js 3D neural visualization at localhost:3927/dashboard
- 7 interactive pages: Graph, Memories, Timeline, Feed, Explore, Intentions, Stats
- WebSocket event bus with 16 event types, real-time 3D animations
- Bloom post-processing, GPU instanced rendering, force-directed layout
- Dream visualization mode, FSRS retention curves, command palette (Cmd+K)
- Keyboard shortcuts, responsive mobile layout, PWA installable
- Single binary deployment via include_dir! (22MB)

Engine:
- HyDE query expansion (intent classification + 3-5 semantic variants + centroid)
- fastembed 5.11 with optional Nomic v2 MoE + Qwen3 reranker + Metal GPU
- Emotional memory module (#29)
- Criterion benchmark suite

Backend:
- Axum WebSocket at /ws with heartbeat + event broadcast
- 7 new REST endpoints for cognitive operations
- Event emission from MCP tools via shared broadcast channel
- CORS for SvelteKit dev mode

Distribution:
- GitHub issue templates (bug report, feature request)
- CHANGELOG with comprehensive v2.0 release notes
- README updated with dashboard docs, architecture diagram, comparison table

734 tests passing, zero warnings, 22MB release binary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam Valladares 2026-02-22 03:07:25 -06:00
parent 26cee040a5
commit c2d28f3433
321 changed files with 32695 additions and 4727 deletions

View file

@ -1,13 +1,22 @@
//! stdio Transport for MCP
//!
//! Handles JSON-RPC communication over stdin/stdout.
//! v1.9.2: Async tokio I/O with heartbeat and error resilience.
use std::io::{self, BufRead, BufReader, Write};
use tracing::{debug, error, warn};
use std::io;
use std::time::Duration;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tracing::{debug, error, info, warn};
use super::types::{JsonRpcError, JsonRpcRequest, JsonRpcResponse};
use crate::server::McpServer;
/// Maximum consecutive I/O errors before giving up
const MAX_CONSECUTIVE_ERRORS: u32 = 5;
/// Heartbeat interval — sends a ping notification to keep the connection alive
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(30);
/// stdio Transport for MCP server
pub struct StdioTransport;
@ -16,66 +25,109 @@ impl StdioTransport {
Self
}
/// Run the MCP server over stdio
/// Run the MCP server over stdio with heartbeat and error resilience
pub async fn run(self, mut server: McpServer) -> Result<(), io::Error> {
let stdin = io::stdin();
let stdout = io::stdout();
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
let reader = BufReader::new(stdin.lock());
let mut stdout = stdout.lock();
let mut reader = BufReader::new(stdin);
let mut stdout = stdout;
let mut consecutive_errors: u32 = 0;
let mut line_buf = String::new();
for line in reader.lines() {
let line = match line {
Ok(l) => l,
Err(e) => {
error!("Failed to read line: {}", e);
break;
}
};
loop {
line_buf.clear();
if line.is_empty() {
continue;
}
tokio::select! {
result = reader.read_line(&mut line_buf) => {
match result {
Ok(0) => {
// Clean EOF — stdin closed
info!("stdin closed (EOF), shutting down");
break;
}
Ok(_) => {
consecutive_errors = 0;
let line = line_buf.trim();
debug!("Received: {} bytes", line.len());
if line.is_empty() {
continue;
}
// Parse JSON-RPC request
let request: JsonRpcRequest = match serde_json::from_str(&line) {
Ok(r) => r,
Err(e) => {
warn!("Failed to parse request: {}", e);
let error_response = JsonRpcResponse::error(None, JsonRpcError::parse_error());
match serde_json::to_string(&error_response) {
Ok(response_json) => {
writeln!(stdout, "{}", response_json)?;
stdout.flush()?;
debug!("Received: {} bytes", line.len());
// Parse JSON-RPC request
let request: JsonRpcRequest = match serde_json::from_str(line) {
Ok(r) => r,
Err(e) => {
warn!("Failed to parse request: {}", e);
let error_response = JsonRpcResponse::error(None, JsonRpcError::parse_error());
match serde_json::to_string(&error_response) {
Ok(response_json) => {
let out = format!("{}\n", response_json);
stdout.write_all(out.as_bytes()).await?;
stdout.flush().await?;
}
Err(e) => {
error!("Failed to serialize error response: {}", e);
let fallback = "{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{\"code\":-32603,\"message\":\"Internal error\"}}\n";
let _ = stdout.write_all(fallback.as_bytes()).await;
let _ = stdout.flush().await;
}
}
continue;
}
};
// Handle the request
if let Some(response) = server.handle_request(request).await {
match serde_json::to_string(&response) {
Ok(response_json) => {
debug!("Sending: {} bytes", response_json.len());
let out = format!("{}\n", response_json);
stdout.write_all(out.as_bytes()).await?;
stdout.flush().await?;
}
Err(e) => {
error!("Failed to serialize response: {}", e);
let fallback = "{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{\"code\":-32603,\"message\":\"Internal error\"}}\n";
let _ = stdout.write_all(fallback.as_bytes()).await;
let _ = stdout.flush().await;
}
}
}
}
Err(e) => {
error!("Failed to serialize error response: {}", e);
// Send a minimal error response so client doesn't hang
let fallback = r#"{"jsonrpc":"2.0","id":null,"error":{"code":-32603,"message":"Internal error"}}"#;
let _ = writeln!(stdout, "{}", fallback);
let _ = stdout.flush();
consecutive_errors += 1;
warn!(
"I/O error reading stdin ({}/{}): {}",
consecutive_errors, MAX_CONSECUTIVE_ERRORS, e
);
if consecutive_errors >= MAX_CONSECUTIVE_ERRORS {
error!(
"Too many consecutive I/O errors ({}), shutting down",
consecutive_errors
);
break;
}
// Brief pause before retrying
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
continue;
}
};
// Handle the request
if let Some(response) = server.handle_request(request).await {
match serde_json::to_string(&response) {
Ok(response_json) => {
debug!("Sending: {} bytes", response_json.len());
writeln!(stdout, "{}", response_json)?;
stdout.flush()?;
}
Err(e) => {
error!("Failed to serialize response: {}", e);
// Send a minimal error response so client doesn't hang
let fallback = r#"{"jsonrpc":"2.0","id":null,"error":{"code":-32603,"message":"Internal error"}}"#;
let _ = writeln!(stdout, "{}", fallback);
let _ = stdout.flush();
_ = tokio::time::sleep(HEARTBEAT_INTERVAL) => {
// Send a heartbeat ping notification to keep the connection alive
let ping = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/ping\"}\n";
if let Err(e) = stdout.write_all(ping.as_bytes()).await {
warn!("Failed to send heartbeat ping: {}", e);
consecutive_errors += 1;
if consecutive_errors >= MAX_CONSECUTIVE_ERRORS {
error!("Too many consecutive errors, shutting down");
break;
}
} else {
let _ = stdout.flush().await;
debug!("Heartbeat ping sent");
}
}
}