feat(gui): handle empty sessions in ollie-gui

- getAgents(): filter out 'new' (agent creation file), return empty list
- setActiveSessionId(): skip agent init when no agents exist
- submit/interrupt/kill/streams: guard against empty m_agentId
- prompt input: disable when no agent, show 'No agent' placeholder
This commit is contained in:
Levi Neely 2026-08-01 19:51:07 +02:00
parent c4d6f2a1b9
commit 6fcef9f60c
2 changed files with 47 additions and 37 deletions

View File

@ -193,11 +193,13 @@ ApplicationWindow {
}
Component.onCompleted: {
if (ollie.activeAgentId) {
var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId)
if (log) {
chatModel.appendData(log)
chatModel.trimFront(20)
}
}
scrollTimer.start()
buildSessionTree()
}
@ -206,11 +208,13 @@ ApplicationWindow {
target: ollie
function onActiveSessionIdChanged() {
chatModel.reset()
if (ollie.activeAgentId) {
var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId)
if (log) {
chatModel.appendData(log)
chatModel.trimFront(20)
}
}
scrollTimer.start()
// Expand the active session in the tree
buildSessionTree()
@ -886,8 +890,8 @@ property bool blockExpanded: {
TextArea {
id: promptInput
Layout.fillWidth: true
placeholderText: ollie.activeSessionId ? "Message..." : "No session"
enabled: ollie.activeSessionId !== ""
placeholderText: !ollie.activeSessionId ? "No session" : !ollie.activeAgentId ? "No agent" : "Message..."
enabled: ollie.activeSessionId !== "" && ollie.activeAgentId !== ""
wrapMode: TextEdit.Wrap
property string _lastText: ""

View File

@ -62,13 +62,9 @@ void Ollie9pClient::setActiveSessionId(const QString &id)
}
// Discover agent ID BEFORE emitting signals
QByteArray out = run9p({"ls", "session/" + id + "/agent"});
QString agents = QString::fromUtf8(out).trimmed();
QStringList agents = getAgents(id);
if (!agents.isEmpty()) {
m_agentId = agents.split('\n').first().trimmed();
} else {
m_agentId = "0";
}
m_agentId = agents.first();
// Read initial state immediately (state, not statewait)
QByteArray stateOut = run9p({"read", agentPath() + "/state"});
@ -81,6 +77,11 @@ void Ollie9pClient::setActiveSessionId(const QString &id)
startChatStream();
startStateStream();
} else {
// Empty session — no agent yet
emit activeSessionIdChanged();
emit activeStateChanged();
}
}
void Ollie9pClient::refreshSessions()
@ -126,7 +127,7 @@ QString Ollie9pClient::readLogForSession(const QString &sessionId, const QString
bool Ollie9pClient::submit(const QString &prompt)
{
if (m_activeSessionId.isEmpty() || prompt.trimmed().isEmpty()) return false;
if (m_activeSessionId.isEmpty() || m_agentId.isEmpty() || prompt.trimmed().isEmpty()) return false;
QProcess proc;
proc.start(ninepBin(), {"-a", serverAddr(), "write", agentPath() + "/prompt"});
proc.waitForStarted(3000);
@ -138,7 +139,7 @@ bool Ollie9pClient::submit(const QString &prompt)
bool Ollie9pClient::interrupt()
{
if (m_activeSessionId.isEmpty()) return false;
if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return false;
QProcess proc;
proc.start(ninepBin(), {"-a", serverAddr(), "write", agentPath() + "/ctl"});
proc.waitForStarted(3000);
@ -150,7 +151,7 @@ bool Ollie9pClient::interrupt()
bool Ollie9pClient::kill()
{
if (m_activeSessionId.isEmpty()) return false;
if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return false;
QProcess proc;
proc.start(ninepBin(), {"-a", serverAddr(), "write", agentPath() + "/ctl"});
proc.waitForStarted(3000);
@ -191,10 +192,15 @@ QStringList Ollie9pClient::getAgents(const QString &sessionId)
if (sessionId.isEmpty()) return {};
QByteArray out = run9p({"ls", "session/" + sessionId + "/agent"});
QString raw = QString::fromUtf8(out).trimmed();
if (raw.isEmpty()) return {"0"}; // default fallback
QStringList agents = raw.split('\n', Qt::SkipEmptyParts);
for (int i = 0; i < agents.size(); ++i)
agents[i] = agents[i].trimmed();
if (raw.isEmpty()) return {};
QStringList all = raw.split('\n', Qt::SkipEmptyParts);
QStringList agents;
for (const QString &a : all) {
QString name = a.trimmed();
// "new" is the agent creation file, not an actual agent
if (name != "new")
agents.append(name);
}
return agents;
}
@ -425,6 +431,7 @@ 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<int, QProcess::ExitStatus>::of(&QProcess::finished),
@ -437,16 +444,15 @@ void Ollie9pClient::startStateStream()
if (m_stateProc) return;
// Check if session still exists before starting
if (m_activeSessionId.isEmpty()) return;
if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return;
// Re-discover agent ID — directory may have been renamed
QByteArray out = run9p({"ls", "session/" + m_activeSessionId + "/agent"});
QString agents = QString::fromUtf8(out).trimmed();
QStringList agents = getAgents(m_activeSessionId);
if (agents.isEmpty()) {
// Session or agent directory gone — don't start
return;
}
QString newId = agents.split('\n').first().trimmed();
QString newId = agents.first();
if (newId != m_agentId) {
m_agentId = newId;
emit activeAgentIdChanged();