Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/reliary-agent/pi/gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { execFileSync, spawnSync } = require("child_process");
const { existsSync, readFileSync, readdirSync, statSync, unlinkSync } = require("fs");
const { createHash } = require("crypto");

const GATE_VERSION = "2.6.0";
const GATE_VERSION = "0.6.5";

// ── Log levels (matching RELIARY_LOG convention) ──
const LOG_LEVELS = { error: 1, warn: 2, info: 3, debug: 4, trace: 5 };
Expand Down
2 changes: 1 addition & 1 deletion crates/reliary-agent/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ pub fn restore_opencode_proxy_routes() -> bool {
where
F: FnOnce(PathBuf),
{
let _lock = INIT_TEST_LOCK.lock().unwrap();
let _lock = INIT_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
let dir = std::env::temp_dir().join(format!("reliary_init_test_{}_{}", std::process::id(), COUNTER.fetch_add(1, Ordering::SeqCst)));
Expand Down
16 changes: 14 additions & 2 deletions crates/reliary-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,10 @@ fn main() {
}
}
Commands::Serve { port } => {
rayon::ThreadPoolBuilder::new()
.num_threads(4)
.build_global()
.unwrap_or(()); // ok if already initialized
ux::write_pid_file();
eprintln!("{} Reliary Agent v{}", color::bold(""), VERSION);
eprintln!(
Expand All @@ -978,7 +982,11 @@ fn main() {
let state = Arc::new(SessionState::new(
std::env::current_dir().unwrap_or_default().to_string_lossy().as_ref()
));
let rt = tokio::runtime::Runtime::new().unwrap();
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_all()
.build()
.unwrap();
rt.block_on(async { crate::proxy::start(*port, Some(state)).await })
.unwrap_or_else(|e| error!("Server error: {}", e));
}
Expand Down Expand Up @@ -1220,7 +1228,11 @@ fn main() {
let state = Arc::new(SessionState::new(
std::env::current_dir().unwrap_or_default().to_string_lossy().as_ref()
));
let rt = tokio::runtime::Runtime::new().unwrap();
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_all()
.build()
.unwrap();
rt.block_on(async { crate::proxy::start(9799, Some(state)).await })
.unwrap_or_else(|e| error!("Server error: {}", e));
}
Expand Down
5 changes: 2 additions & 3 deletions crates/reliary-agent/src/novel_compress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
//! 1. Cache-hit feedback loop: adapt compression aggressiveness from API cache metrics.
//! 2. Stream-aware prefetch: parse SSE chunks for file paths, pre-load reads.
//! 3. Maxwell's Demon: information-theoretic token erasure (cost = -log2(freq)).
//! 4. Asymmetric tool-call compression: preserve results, compress requests.
//! 5. Invariant hoisting: JSON arrays → header + delta rows.
//! 6. Dialogue State Ledger: extract [STATE] from old reasoning.
//! 4. Invariant hoisting: JSON arrays → header + delta rows.
//! 5. Dialogue State Ledger: extract [STATE] from old reasoning.

use rustc_hash::FxHashMap;
use serde_json::Value;
Expand Down
19 changes: 17 additions & 2 deletions crates/reliary-agent/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,17 @@ async fn proxy_post(
let result = crate::guard::check_diff(&index_path, rp, &new_text);
let status = result.get("status").and_then(|s| s.as_str()).unwrap_or("error").to_string();
if let Ok(mut c) = GUARD_CACHE.lock() {
// Evict stale entries (elapsed > 60s) to bound memory.
c.retain(|_, e| e.inserted_at.elapsed() < std::time::Duration::from_secs(60));
// Hard cap to prevent unbounded growth.
if c.len() >= 500 {
if let Some(&oldest_key) = c.iter()
.min_by_key(|(_, e)| e.inserted_at)
.map(|(k, _)| k)
{
c.remove(&oldest_key);
}
}
c.insert(gk, GuardCacheEntry { status: status.clone(), inserted_at: std::time::Instant::now() });
}
status
Expand Down Expand Up @@ -758,9 +769,13 @@ async fn proxy_post(
Ok(Some(chunk)) => {
// Track for usage parsing and response cache
let chunk_str = String::from_utf8_lossy(&chunk);
// Stream-aware prefetch: extract file paths from live chunks
// Stream-aware prefetch: extract file paths from live chunks.
// Run in spawn_blocking to avoid sync fs I/O stalling the async runtime.
if !std::env::var("RELIARY_PROXY_PREFETCH").is_ok_and(|v| v == "0") {
crate::novel_compress::try_prefetch(&chunk_str);
let pf_chunk = chunk_str.to_string();
tokio::task::spawn_blocking(move || {
crate::novel_compress::try_prefetch(&pf_chunk);
});
}
if chunk_str.contains("\"usage\"") || chunk_str.contains("\"prompt_tokens\"") {
last_chunk_with_usage = chunk_str.to_string();
Expand Down
4 changes: 3 additions & 1 deletion crates/reliary-agent/src/reindex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ fn file_modified(p: &Path) -> u64 {
fn walkdir(dir: &str) -> Result<Vec<std::path::PathBuf>, String> {
let mut files = Vec::new();
let mut stack = vec![std::path::PathBuf::from(dir)];
let skip_dirs = [".git", ".reliary", "node_modules", "target", "__pycache__", ".venv"];
let skip_dirs = [".git", ".reliary", "node_modules", "target", "__pycache__",
".venv", ".cargo", ".rustup", ".npm", ".cache", ".local", "venv",
".next", "dist", "build", "vendor", "bundle", ".bundle"];

while let Some(path) = stack.pop() {
if let Ok(entries) = std::fs::read_dir(&path) {
Expand Down
38 changes: 35 additions & 3 deletions crates/reliary-agent/src/scavenger.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
use std::sync::Arc;
use std::time::Duration;
use std::time::{Duration, Instant};
use rayon::prelude::*;
use crate::session_state::SessionState;
use crate::chronicle;

pub fn scavenger_loop(state: Arc<SessionState>) {
let mut sleep_secs: u64 = 120;
loop {
std::thread::sleep(Duration::from_secs(120));
std::thread::sleep(Duration::from_secs(sleep_secs));

if !state.is_scavenger_allowed() { continue; }

let workdir = state.workdir.to_string_lossy().to_string();
let db_path = state.chronicle_path.to_string_lossy().to_string();
let cycle_start = Instant::now();

// 1. Parallel incremental re-index for changed files
crate::reindex::incremental_reindex(&workdir);

// 2. Collect file paths then scan for dead code in parallel
let file_tasks: Vec<_> = walkdir::WalkDir::new(&workdir)
.follow_links(false)
.max_depth(20)
.into_iter()
.filter_entry(|e| {
if e.file_type().is_dir() {
let name = e.file_name().to_string_lossy();
!matches!(name.as_ref(),
".git" | ".reliary" | "node_modules" | "target" | "__pycache__"
| ".venv" | ".cargo" | ".rustup" | ".npm" | ".cache"
| ".local" | "venv" | ".next" | "dist" | "build"
| "vendor" | "bundle" | ".bundle")
} else {
true
}
})
.filter_map(|e| e.ok())
.map(|e| e.path().to_path_buf())
.filter(|fp| {
Expand Down Expand Up @@ -53,6 +69,9 @@ pub fn scavenger_loop(state: Arc<SessionState>) {
rusqlite::params![cutoff],
);

// WSL2 drvfs detection: skip heal subprocess (cargo test) on /mnt/ paths
let on_drvfs = workdir.starts_with("/mnt/");

// Process candidates sequentially (heal calls need serial DB)
for c in candidates.iter() {
if c.confidence != reliary_dead::Confidence::High { continue; }
Expand All @@ -62,7 +81,12 @@ pub fn scavenger_loop(state: Arc<SessionState>) {
let fixes = vec![(c.name.clone(), String::new())];
let (modified, count) = reliary_fix::apply_fixes(&content, &fixes);
if count > 0 && modified != content {
match crate::heal::heal_edit(&c.file, &modified, &workdir) {
let result = if on_drvfs {
Err("WSL2 drvfs: heal skipped".into())
} else {
crate::heal::heal_edit(&c.file, &modified, &workdir)
};
match result {
Ok(()) => {
let _ = std::fs::write(&c.file, &modified);
chronicle::append(&chronicle_db, "scavenge", &c.file, &c.name, "removed");
Expand All @@ -75,5 +99,13 @@ pub fn scavenger_loop(state: Arc<SessionState>) {
}
}
}

// Adaptive backoff: if cycle took longer than 60s, double sleep interval
let elapsed = cycle_start.elapsed().as_secs();
if elapsed > 60 {
sleep_secs = (sleep_secs * 2).min(1800); // max 30 min
} else {
sleep_secs = 120;
}
}
}
2 changes: 1 addition & 1 deletion crates/reliary-agent/src/session_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl SessionState {
warn!("session_dir create_dir_all: {}", e);
}
Self {
scavenger_muzzled: AtomicBool::new(false),
scavenger_muzzled: AtomicBool::new(true),
muzzle_time: Mutex::new(Instant::now()),
chronicle_path,
workdir: PathBuf::from(workdir),
Expand Down
1 change: 1 addition & 0 deletions crates/reliary-agent/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fn start_daemon_inner() -> DaemonGuard {
let bin = binary_path();
let mut child = Command::new(&bin)
.arg("serve")
.env_remove("RELIARY_UPSTREAM_URL") // Test isolation: don't accept unknown keys
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
Expand Down
2 changes: 2 additions & 0 deletions crates/reliary-agent/tests/e2e_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ fn e2e_auth_routing() {
let client = common::http_client();

// Unknown key -> 403
// NOTE: The daemon is spawned with RELIARY_UPSTREAM_URL removed (see common/mod.rs)
// so unknown keys are rejected even if the developer has the env var set.
let resp = client
.post("http://127.0.0.1:9090/v1/chat/completions")
.header("Authorization", "Bearer sk-fake-key-xxxxx")
Expand Down
2 changes: 1 addition & 1 deletion pi/gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { execFileSync, spawnSync } = require("child_process");
const { existsSync, readFileSync, readdirSync, statSync, unlinkSync } = require("fs");
const { createHash } = require("crypto");

const GATE_VERSION = "2.6.0";
const GATE_VERSION = "0.6.5";

// ── Log levels (matching RELIARY_LOG convention) ──
const LOG_LEVELS = { error: 1, warn: 2, info: 3, debug: 4, trace: 5 };
Expand Down
Loading