From fe91ba2ab491396acb029a90fe8bd7d5e8cde2a0 Mon Sep 17 00:00:00 2001 From: Xing Ji <41811005+micsama@users.noreply.github.com> Date: Mon, 9 Mar 2026 04:40:56 +0800 Subject: [PATCH] fix(repl): skip /quit on EOF when stdin is not a TTY (#724) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running as a launchd/systemd daemon, stdin is /dev/null. rustyline reads EOF immediately and the REPL thread was sending a /quit message, causing the agent to shut down right after startup — making service mode non-functional on both macOS and Linux. Fix: check std::io::stdin().is_terminal() before sending /quit on EOF. In daemon mode (no TTY) the REPL thread exits silently, leaving other channels (gateway, telegram, …) running as expected. Fixes #723 Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Zaki Manian --- src/channels/repl.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/channels/repl.rs b/src/channels/repl.rs index f49cb7ee..b1f06ec2 100644 --- a/src/channels/repl.rs +++ b/src/channels/repl.rs @@ -18,7 +18,7 @@ //! - `Esc` - Interrupt current operation use std::borrow::Cow; -use std::io::{self, Write}; +use std::io::{self, IsTerminal, Write}; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -411,10 +411,15 @@ impl Channel for ReplChannel { } } Err(ReadlineError::Eof) => { - // Ctrl+D: send /quit so the agent loop runs graceful shutdown - let msg = - IncomingMessage::new("repl", "default", "/quit").with_timezone(&sys_tz); - let _ = tx.blocking_send(msg); + // Ctrl+D in interactive mode: graceful shutdown. + // In daemon mode (stdin = /dev/null, no TTY), EOF arrives + // immediately — just drop the REPL thread silently so other + // channels (gateway, telegram, …) keep running. + if std::io::stdin().is_terminal() { + let msg = IncomingMessage::new("repl", "default", "/quit") + .with_timezone(&sys_tz); + let _ = tx.blocking_send(msg); + } break; } Err(e) => {