delete ProcessTracker — replaced by ActiveToolCall + KillOnDrop

All process management now goes through active_tools:
- TUI reads metadata (name, elapsed time)
- Ctrl+K aborts handles (KillOnDrop sends SIGTERM)
- Running count from active_tools.len()

No more separate PID tracking, register/unregister, or
ProcessInfo. One data structure for everything.

Co-Developed-By: Kent Overstreet <kent.overstreet@linux.dev>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
ProofOfConcept 2026-04-03 23:56:56 -04:00 committed by Kent Overstreet
parent 310bbe9fce
commit 021eafe6da
5 changed files with 37 additions and 109 deletions

View file

@ -33,7 +33,7 @@ use clap::Parser;
use poc_memory::dbglog;
use poc_memory::user::*;
use poc_memory::agent::{tools, Agent, TurnResult};
use poc_memory::agent::{Agent, TurnResult};
use poc_memory::user::api::ApiClient;
use poc_memory::user::tui::HotkeyAction;
use poc_memory::config::{self, AppConfig, SessionConfig};
@ -114,7 +114,6 @@ enum Command {
struct Session {
agent: Arc<Mutex<Agent>>,
config: SessionConfig,
process_tracker: tools::ProcessTracker,
ui_tx: ui_channel::UiSender,
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
// DMN state
@ -140,7 +139,6 @@ impl Session {
fn new(
agent: Arc<Mutex<Agent>>,
config: SessionConfig,
process_tracker: tools::ProcessTracker,
ui_tx: ui_channel::UiSender,
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
) -> Self {
@ -149,7 +147,6 @@ impl Session {
Self {
agent,
config,
process_tracker,
ui_tx,
turn_tx,
dmn: if dmn::is_off() {
@ -581,13 +578,17 @@ impl Session {
/// Interrupt: kill processes, abort current turn, clear pending queue.
async fn interrupt(&mut self) {
let procs = self.process_tracker.list().await;
for p in &procs {
self.process_tracker.kill(p.pid).await;
}
// Only abort the turn if no processes are running — let SIGTERM'd
// processes exit normally so run_bash can unregister them.
if procs.is_empty() {
// Abort all active tool calls (KillOnDrop sends SIGTERM)
let count = {
let agent = self.agent.lock().await;
let mut tools = agent.active_tools.lock().unwrap();
let count = tools.len();
for entry in tools.drain(..) {
entry.handle.abort();
}
count
};
if count == 0 {
if let Some(handle) = self.turn_handle.take() {
handle.abort();
self.turn_in_progress = false;
@ -599,7 +600,7 @@ impl Session {
}
}
self.pending_input = None;
let killed = procs.len();
let killed = count;
if killed > 0 || self.turn_in_progress {
let _ = self.ui_tx.send(UiMessage::Info(format!(
"(interrupted — killed {} process(es), turn aborted)",
@ -639,28 +640,25 @@ impl Session {
}
}
/// Show and kill running processes (Ctrl+K).
/// Show and kill running tool calls (Ctrl+K).
async fn kill_processes(&mut self) {
let procs = self.process_tracker.list().await;
if procs.is_empty() {
let active_tools = self.agent.lock().await.active_tools.clone();
let mut tools = active_tools.lock().unwrap();
if tools.is_empty() {
let _ = self
.ui_tx
.send(UiMessage::Info("(no running processes)".into()));
.send(UiMessage::Info("(no running tool calls)".into()));
} else {
for p in &procs {
let elapsed = p.started.elapsed();
for entry in tools.drain(..) {
let elapsed = entry.started.elapsed();
let _ = self.ui_tx.send(UiMessage::Info(format!(
" killing pid {} ({:.0}s): {}",
p.pid,
" killing {} ({:.0}s): {}",
entry.name,
elapsed.as_secs_f64(),
p.command
entry.detail
)));
self.process_tracker.kill(p.pid).await;
entry.handle.abort();
}
let _ = self.ui_tx.send(UiMessage::Info(format!(
"Killed {} process(es)",
procs.len()
)));
}
}
@ -877,7 +875,6 @@ async fn run(cli: cli::CliArgs) -> Result<()> {
// Keep a reference to the process tracker outside the agent lock
// so Ctrl+K can kill processes even when the agent is busy.
let process_tracker = agent.lock().await.process_tracker.clone();
// Restore conversation from the append-only log
{
@ -912,7 +909,6 @@ async fn run(cli: cli::CliArgs) -> Result<()> {
let mut session = Session::new(
agent,
config,
process_tracker,
ui_tx.clone(),
turn_tx,
);
@ -994,7 +990,7 @@ async fn run(cli: cli::CliArgs) -> Result<()> {
// Render tick — update periodic state
_ = render_interval.tick() => {
let new_count = session.process_tracker.list().await.len() as u32;
let new_count = session.agent.lock().await.active_tools.lock().unwrap().len() as u32;
if new_count != app.running_processes {
app.running_processes = new_count;
dirty = true;