From dbbcc07934e7526c264b665123b9bf15f0bcd901 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Sat, 1 Aug 2026 20:56:40 +0200 Subject: [PATCH] gui: verify agent directory before starting statewait/chat streams Add a run9p stat check before opening blocking reads on statewait and chat. If the agent directory doesn't exist, skip starting (prevents tight restart loops when a session is killed while streams are active). Only one statewait reader runs at a time. --- gui/ollie9pclient.cpp | 45 +++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/gui/ollie9pclient.cpp b/gui/ollie9pclient.cpp index 73ce6ab..46e143c 100644 --- a/gui/ollie9pclient.cpp +++ b/gui/ollie9pclient.cpp @@ -437,13 +437,19 @@ void Ollie9pClient::ensureRootDataLoaded() void Ollie9pClient::startChatStream() { - if (m_chatProc) return; - if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return; - m_chatProc = new QProcess(this); - connect(m_chatProc, &QProcess::readyReadStandardOutput, this, &Ollie9pClient::onChatReadyRead); - connect(m_chatProc, QOverload::of(&QProcess::finished), - this, &Ollie9pClient::onChatFinished); - m_chatProc->start(ninepBin(), {"-a", serverAddr(), "read", agentPath() + "/chat"}); + if (m_chatProc) return; + if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return; + + // Verify the agent directory exists before opening a blocking read + QString ap = agentPath(); + QByteArray verify = run9p({"stat", ap}); + if (verify.isEmpty()) return; // directory gone + + m_chatProc = new QProcess(this); + connect(m_chatProc, &QProcess::readyReadStandardOutput, this, &Ollie9pClient::onChatReadyRead); + connect(m_chatProc, QOverload::of(&QProcess::finished), + this, &Ollie9pClient::onChatFinished); + m_chatProc->start(ninepBin(), {"-a", serverAddr(), "read", ap + "/chat"}); } void Ollie9pClient::startStateStream() @@ -453,6 +459,16 @@ void Ollie9pClient::startStateStream() // Check if session still exists before starting if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return; + // Verify the agent directory still exists before opening a blocking read + QString ap = agentPath(); + QByteArray verify = run9p({"stat", ap}); + if (verify.isEmpty()) { + // Directory gone — don't start spamming + m_agentId.clear(); + emit activeAgentIdChanged(); + return; + } + // Verify the current agent directory still exists QStringList agents = getAgents(m_activeSessionId); if (agents.isEmpty()) { @@ -465,11 +481,11 @@ void Ollie9pClient::startStateStream() emit activeAgentIdChanged(); } - m_stateProc = new QProcess(this); - connect(m_stateProc, &QProcess::readyReadStandardOutput, this, &Ollie9pClient::onStateReadyRead); - connect(m_stateProc, QOverload::of(&QProcess::finished), - this, &Ollie9pClient::onStateFinished); - m_stateProc->start(ninepBin(), {"-a", serverAddr(), "read", agentPath() + "/statewait"}); + m_stateProc = new QProcess(this); + connect(m_stateProc, &QProcess::readyReadStandardOutput, this, &Ollie9pClient::onStateReadyRead); + connect(m_stateProc, QOverload::of(&QProcess::finished), + this, &Ollie9pClient::onStateFinished); + m_stateProc->start(ninepBin(), {"-a", serverAddr(), "read", ap + "/statewait"}); } void Ollie9pClient::stopStreams() @@ -529,8 +545,9 @@ void Ollie9pClient::onStateFinished(int, QProcess::ExitStatus) { m_stateProc->deleteLater(); m_stateProc = nullptr; - // Do NOT restart — statewait is only started when the user - // explicitly selects an agent via setActiveSessionId or switchAgent. + // Restart to pick up the next state change — statewait is a blocking + // read that returns once and closes the FD. + startStateStream(); } void Ollie9pClient::startEventStream()