gui: fix deadlock — demand-driven chat streaming

The chat stream (ollie-9p read .../chat) blocks forever waiting for
data. Starting it on agent switch meant stopStreams() would block the
main thread trying to kill a hung process.

Fix: stream chat only while the agent is active.
- submit() starts the chat stream (agent is about to become active)
- Event handler stops chat on idle transition, starts on non-idle
- switchAgent() only starts chat if agent is already non-idle
- NativeStreamer::stop() uses SIGTERM+200ms then SIGKILL+100ms
  instead of immediate SIGKILL+1000ms
This commit is contained in:
Levi Neely 2026-08-08 23:20:03 +02:00
parent 66aecc8315
commit 51b2013ac3
2 changed files with 23 additions and 3 deletions

View File

@ -63,8 +63,11 @@ void NativeStreamer::stop()
if (m_process) {
disconnect(m_process, nullptr, this, nullptr);
if (m_process->state() != QProcess::NotRunning) {
m_process->kill();
m_process->waitForFinished(1000);
m_process->terminate(); // SIGTERM first
if (!m_process->waitForFinished(200)) {
m_process->kill(); // SIGKILL if still alive
m_process->waitForFinished(100);
}
}
m_process->deleteLater();
m_process = nullptr;

View File

@ -223,6 +223,13 @@ void Ollie9pClient::handleEvent(const QString &eventLine)
m_agentStateValues[key] = payloadStr;
emit agentStateChanged(sidStr, aidStr, payloadStr);
emit activeStateChanged();
// Demand-driven chat: stop on idle, start on activity.
if (payloadStr == QLatin1String("idle")) {
m_chat->stop();
} else if (!m_chat->isRunning()) {
m_chat->start(agentPath() + "/chat");
}
} else {
// Non-active agent: need to check cache
const QString sidStr = sessionId.toString();
@ -378,6 +385,12 @@ bool Ollie9pClient::submit(const QString &prompt)
{
if (m_activeSessionId.isEmpty() || m_agentId.isEmpty() || prompt.trimmed().isEmpty()) return false;
if (!m_9p || !m_9p->isConnected()) return false;
// Start chat stream before submitting — agent will become active.
if (!m_chat->isRunning()) {
m_chat->start(agentPath() + "/chat");
}
return m_9p->write(agentPath() + "/prompt", prompt.toUtf8());
}
@ -673,7 +686,11 @@ void Ollie9pClient::startActiveAgentStreams()
if (!m_daemonConnected) return;
if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return;
// Chat stream is demand-driven: started on submit(), stopped on idle.
// If the agent is already active (e.g. switching to a busy agent), start chat now.
if (m_activeState != "idle") {
m_chat->start(agentPath() + "/chat");
}
m_state->start(agentPath() + "/statewait");
}