feat(agent): implement agent execution engine and turn handling with background processing

This commit is contained in:
asepharyana
2026-07-20 16:29:39 +07:00
parent 2e8a4f2443
commit efcd191f96
15 changed files with 452 additions and 247 deletions
+36 -17
View File
@@ -104,36 +104,55 @@ impl SessionLock {
let _ = fs::remove_file(&self.path);
}
/// Check whether a process with the given PID is currently alive.
/// Check whether a process with the given PID is currently alive and
/// belongs to the same binary (mitigating PID-reuse races).
///
/// Strategy (Unix):
/// 1. Resolve `/proc/<pid>/exe` — if it doesn't match our own binary,
/// the PID either belongs to another process or is reused — return false.
/// 2. Send `kill(pid, 0)` to verify the process is still alive.
/// 3. Re-check `/proc/<pid>/exe` to close the TOCTOU window between
/// step 1 and step 2 (PID reuse after exe check, before kill).
///
/// Uses `kill(pid, 0)` on Unix via the `nix` or `libc` crate in production;
/// here we provide a best-effort check using the process table.
/// On non-Unix platforms this always returns `true` (conservative).
fn is_alive(pid: u32) -> bool {
// On Unix, signal 0 checks process existence without sending a signal.
#[cfg(unix)]
{
// SAFETY: `libc::kill(pid, 0)` does not send a signal; it only checks
// whether the process exists and the caller has permission to signal it.
// The integer argument is a PID validated by `try_lock`.
// Resolve our own executable path once.
let self_exe = match std::fs::read_link("/proc/self/exe") {
Ok(exe) => exe,
Err(_) => return false,
};
let pid_signed: i32 = match pid.try_into() {
Ok(p) => p,
Err(_) => return false,
};
let proc_exe = std::path::PathBuf::from(format!("/proc/{pid}/exe"));
// Phase 1: read /proc/<pid>/exe and compare with self_exe.
let target = match std::fs::read_link(&proc_exe) {
Ok(t) => t,
Err(_) => return false,
};
if target != self_exe {
return false;
}
// Phase 2: verify the process is still alive.
// SAFETY: `libc::kill(pid, 0)` does not send a signal; it only checks
// whether the process exists and the caller has permission to signal it.
if unsafe { libc::kill(pid_signed, 0) != 0 } {
return false;
}
// Extra check: verify the PID belongs to a zesdex process via
// /proc/<pid>/exe to mitigate the PID-reuse race.
let proc_exe = std::path::PathBuf::from(format!("/proc/{pid}/exe"));
if let Ok(target) = std::fs::read_link(&proc_exe) {
if let Ok(exe) = std::env::current_exe() {
if target != exe {
return false;
}
}
// Phase 3: re-check /proc/<pid>/exe to detect PID reuse between
// Phase 1 and Phase 2.
match std::fs::read_link(&proc_exe) {
Ok(recheck) if recheck == self_exe => true,
_ => false,
}
true
}
#[cfg(not(unix))]
+6 -2
View File
@@ -333,8 +333,9 @@ impl SseParser {
if let Some(tool_calls) =
d.get("tool_calls").and_then(|tc| tc.as_array())
{
const MAX_TOOL_CALLS: usize = 64;
for tc in tool_calls {
let index =
let raw_index =
tc.get("index").and_then(Value::as_u64).unwrap_or_else(
|| {
tracing::warn!(
@@ -344,7 +345,10 @@ impl SseParser {
0
},
);
let index = usize::try_from(index).unwrap_or(0);
// Clamp index to prevent out-of-bounds / memory exhaustion
let index = usize::try_from(raw_index)
.unwrap_or(0)
.min(MAX_TOOL_CALLS.saturating_sub(1));
let id = tc
.get("id")
.and_then(|i| i.as_str())