From ef03cdc7f3cf40ddccd9ac94b0ecde0ec6ff2cce Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Sat, 1 Aug 2026 20:53:07 +0200 Subject: [PATCH] gui: deduplicate sessions from multi-agent index, fix field mapping - Deduplicate session entries in refreshSessions and onEventReadyRead using QSet to skip duplicate session IDs from multi-agent index. - Fix field mapping: index format is name\tstate\tcwd\tbackend\tmodel\tagent - Added QSet include --- gui/ollie9pclient.cpp | 103 ++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/gui/ollie9pclient.cpp b/gui/ollie9pclient.cpp index 0efdbf9..7df9d43 100644 --- a/gui/ollie9pclient.cpp +++ b/gui/ollie9pclient.cpp @@ -1,6 +1,7 @@ #include "ollie9pclient.h" #include +#include #include #include @@ -84,30 +85,36 @@ void Ollie9pClient::setActiveSessionId(const QString &id) void Ollie9pClient::refreshSessions() { - QByteArray out = run9p({"read", "session/idx"}); - // Don't trim — trailing tabs are significant fields - QString raw = QString::fromUtf8(out); + QByteArray out = run9p({"read", "session/idx"}); + // Don't trim — trailing tabs are significant fields + QString raw = QString::fromUtf8(out); - m_sessions.clear(); - if (!raw.isEmpty()) { - for (const QString &line : raw.split('\n', Qt::SkipEmptyParts)) { - QStringList parts = line.split('\t'); - // Skip lines where the first field (session name) is empty - if (parts.isEmpty() || parts[0].isEmpty()) continue; - QVariantMap session; - session["id"] = parts[0]; - session["state"] = parts.size() > 1 ? parts[1] : ""; - session["model"] = parts.size() > 2 ? parts[2] : ""; - session["agent"] = parts.size() > 3 ? parts[3] : ""; - m_sessions.append(session); - } - } - emit sessionsChanged(); + m_sessions.clear(); + QSet seen; + if (!raw.isEmpty()) { + for (const QString &line : raw.split('\n', Qt::SkipEmptyParts)) { + QStringList parts = line.split('\t'); + // Skip lines where the first field (session name) is empty + if (parts.isEmpty() || parts[0].isEmpty()) continue; + QString sid = parts[0]; + if (seen.contains(sid)) continue; // already added (multi-agent) + seen.insert(sid); + QVariantMap session; + session["id"] = sid; + session["state"] = parts.size() > 1 ? parts[1] : ""; + session["cwd"] = parts.size() > 2 ? parts[2] : ""; + session["backend"] = parts.size() > 3 ? parts[3] : ""; + session["model"] = parts.size() > 4 ? parts[4] : ""; + session["agent"] = parts.size() > 5 ? parts[5] : ""; + m_sessions.append(session); + } + } + emit sessionsChanged(); - // Auto-select first session if none active - if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) { - setActiveSessionId(m_sessions.first().toMap()["id"].toString()); - } + // Auto-select first session if none active + if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) { + setActiveSessionId(m_sessions.first().toMap()["id"].toString()); + } } QString Ollie9pClient::readLog() @@ -535,31 +542,37 @@ void Ollie9pClient::startEventStream() void Ollie9pClient::onEventReadyRead() { - // eventwait returned the full session index — use it directly - QByteArray data = m_eventProc->readAllStandardOutput(); - // Don't trim — trailing tabs are significant fields - QString raw = QString::fromUtf8(data); + // eventwait returned the full session index — use it directly + QByteArray data = m_eventProc->readAllStandardOutput(); + // Don't trim — trailing tabs are significant fields + QString raw = QString::fromUtf8(data); - m_sessions.clear(); - if (!raw.isEmpty()) { - for (const QString &line : raw.split('\n', Qt::SkipEmptyParts)) { - QStringList parts = line.split('\t'); - // Skip lines where the first field (session name) is empty - if (parts.isEmpty() || parts[0].isEmpty()) continue; - QVariantMap session; - session["id"] = parts[0]; - session["state"] = parts.size() > 1 ? parts[1] : ""; - session["model"] = parts.size() > 2 ? parts[2] : ""; - session["agent"] = parts.size() > 3 ? parts[3] : ""; - m_sessions.append(session); - } - } - emit sessionsChanged(); + m_sessions.clear(); + QSet seen; + if (!raw.isEmpty()) { + for (const QString &line : raw.split('\n', Qt::SkipEmptyParts)) { + QStringList parts = line.split('\t'); + // Skip lines where the first field (session name) is empty + if (parts.isEmpty() || parts[0].isEmpty()) continue; + QString sid = parts[0]; + if (seen.contains(sid)) continue; // already added (multi-agent) + seen.insert(sid); + QVariantMap session; + session["id"] = sid; + session["state"] = parts.size() > 1 ? parts[1] : ""; + session["cwd"] = parts.size() > 2 ? parts[2] : ""; + session["backend"] = parts.size() > 3 ? parts[3] : ""; + session["model"] = parts.size() > 4 ? parts[4] : ""; + session["agent"] = parts.size() > 5 ? parts[5] : ""; + m_sessions.append(session); + } + } + emit sessionsChanged(); - // Auto-select first session if none active - if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) { - setActiveSessionId(m_sessions.first().toMap()["id"].toString()); - } + // Auto-select first session if none active + if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) { + setActiveSessionId(m_sessions.first().toMap()["id"].toString()); + } } void Ollie9pClient::onEventFinished(int, QProcess::ExitStatus)