remove per-agent connection polling
Agent connection state is now derived from session.connected in session/idx, updated via refreshSessions(). No longer spawns N processes for N agents. - Remove m_agentConnections hash and per-agent NinePConnection instances - Remove readAgentRecords() (dead code - agents come from session/idx) - reconcileAgentConnections() now just updates m_agentConnected from m_sessions This eliminates one ollie-9p process per agent, leaving only: - 1 daemon eventwait stream (for all events) - 1 chat stream (for active agent only) - 1 state stream (for active agent only)
This commit is contained in:
parent
0e9a7016a3
commit
8a3d52965e
|
|
@ -142,24 +142,6 @@ QString Ollie9pClient::agentKey(const QString &sessionId, const QString &agentId
|
|||
return sessionId + "\n" + agentId;
|
||||
}
|
||||
|
||||
QList<Ollie9pClient::AgentRecord> Ollie9pClient::readAgentRecords(const QString &sessionId)
|
||||
{
|
||||
QList<AgentRecord> result;
|
||||
if (sessionId.isEmpty()) return result;
|
||||
// Use immutable ID — the 9P namespace resolves it via alias.
|
||||
const QString raw = QString::fromUtf8(run9p({"ls", "session/" + sessionId + "/agent"})).trimmed();
|
||||
for (const QString &name : raw.split('\n', Qt::SkipEmptyParts)) {
|
||||
if (name == "new") continue;
|
||||
const QString path = "session/" + sessionId + "/agent/" + name;
|
||||
const QString id = QString::fromUtf8(run9p({"read", path + "/id"})).trimmed();
|
||||
AgentRecord r;
|
||||
r.id = id.isEmpty() ? name : id;
|
||||
r.name = name;
|
||||
result.append(r);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Ollie9pClient::setDaemonConnected(bool connected)
|
||||
{
|
||||
if (m_daemonConnected == connected) return;
|
||||
|
|
@ -229,71 +211,49 @@ void Ollie9pClient::setAgentConnected(const QString &key, bool connected)
|
|||
|
||||
void Ollie9pClient::startAgentConnections()
|
||||
{
|
||||
reconcileAgentConnections();
|
||||
// Agent connection state now comes from session/idx (refreshSessions)
|
||||
// and eventwait events. No per-agent processes needed.
|
||||
// Just start streams for the active agent if we have one.
|
||||
if (!m_activeSessionId.isEmpty() && !m_agentId.isEmpty()) {
|
||||
startActiveAgentStreams();
|
||||
}
|
||||
}
|
||||
|
||||
// reconcileAgentConnections ensures one NinePConnection per known agent.
|
||||
// Connections use immutable session/agent IDs in their 9P paths. The server
|
||||
// namespace lists directories by mutable display name, but accepts the
|
||||
// immutable UUID as an alias during path walks. This means connection paths
|
||||
// remain valid across session/agent renames without reconnection.
|
||||
// reconcileAgentConnections updates agent connected state from m_sessions.
|
||||
// Connection state is derived from session.connected in session/idx.
|
||||
void Ollie9pClient::reconcileAgentConnections()
|
||||
{
|
||||
if (!m_daemonConnected) return;
|
||||
QSet<QString> wanted;
|
||||
|
||||
// Update connected state for all agents based on session data
|
||||
for (const QVariant &value : std::as_const(m_sessions)) {
|
||||
const QVariantMap session = value.toMap();
|
||||
const QString sid = session.value("id").toString();
|
||||
const bool isConnected = session.value("connected").toBool();
|
||||
|
||||
for (const QVariant &agentValue : session.value("agents").toList()) {
|
||||
const QVariantMap agent = agentValue.toMap();
|
||||
const QString aid = agent.value("id").toString();
|
||||
const QString key = agentKey(sid, aid);
|
||||
wanted.insert(key);
|
||||
if (m_agentConnections.contains(key)) continue;
|
||||
auto *connection = new NinePConnection(this);
|
||||
m_agentConnections.insert(key, connection);
|
||||
connect(connection, &NinePConnection::connected, this, [this, key]() {
|
||||
const bool reconnected = m_seenAgentConnections.contains(key);
|
||||
m_seenAgentConnections.insert(key);
|
||||
setAgentConnected(key, true);
|
||||
if (key == agentKey(m_activeSessionId, m_agentId)) {
|
||||
if (reconnected)
|
||||
emit sessionConnected();
|
||||
|
||||
bool wasConnected = m_agentConnected.value(key, false);
|
||||
m_agentConnected[key] = isConnected;
|
||||
|
||||
// Emit signal if this is the active agent and connection state changed
|
||||
if (key == agentKey(m_activeSessionId, m_agentId)) {
|
||||
if (!wasConnected && isConnected) {
|
||||
emit sessionConnected();
|
||||
startActiveAgentStreams();
|
||||
}
|
||||
});
|
||||
connect(connection, &NinePConnection::disconnected, this, [this, key]() {
|
||||
// Agent may have been killed — refresh to get authoritative state
|
||||
// rather than showing a transient red indicator.
|
||||
refreshSessions();
|
||||
});
|
||||
// Retry with backoff (5s, max 3 retries) to avoid hammering the server
|
||||
// when the connection file exists but returns an error (e.g., toolsConn nil).
|
||||
connection->start(ollie9pBin(), {"-a", serverAddr(), "read", "--open-marker",
|
||||
"session/" + sid + "/agent/" + aid + "/connection"}, 5000, true, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = m_agentConnections.begin(); it != m_agentConnections.end();) {
|
||||
if (wanted.contains(it.key())) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
it.value()->disconnect(this); // prevent stale disconnect signals
|
||||
it.value()->stop();
|
||||
it.value()->deleteLater();
|
||||
m_agentConnected.remove(it.key());
|
||||
it = m_agentConnections.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void Ollie9pClient::stopAgentConnections()
|
||||
{
|
||||
for (NinePConnection *connection : std::as_const(m_agentConnections)) {
|
||||
connection->disconnect(this);
|
||||
connection->stop();
|
||||
}
|
||||
qDeleteAll(m_agentConnections);
|
||||
m_agentConnections.clear();
|
||||
// No per-agent connections to stop anymore
|
||||
m_agentConnected.clear();
|
||||
}
|
||||
|
||||
// handleEvent processes a single event from eventwait.
|
||||
|
|
|
|||
|
|
@ -74,13 +74,6 @@ public:
|
|||
Q_INVOKABLE QString getConfig();
|
||||
Q_INVOKABLE QStringList getAgents(const QString &sessionId);
|
||||
|
||||
struct AgentRecord {
|
||||
QString id;
|
||||
QString name;
|
||||
bool connected = false;
|
||||
};
|
||||
|
||||
QList<AgentRecord> readAgentRecords(const QString &sessionId);
|
||||
void setAgentConnected(const QString &key, bool connected);
|
||||
QString agentKey(const QString &sessionId, const QString &agentId) const;
|
||||
Q_INVOKABLE void setActiveAgentId(const QString &agentId);
|
||||
|
|
@ -141,9 +134,7 @@ private:
|
|||
QVariantMap m_rootModels;
|
||||
QString m_currentBackend;
|
||||
bool m_daemonConnected = false;
|
||||
QHash<QString, NinePConnection *> m_agentConnections;
|
||||
QHash<QString, bool> m_agentConnected;
|
||||
QSet<QString> m_seenAgentConnections;
|
||||
QHash<QString, bool> m_agentConnected; // Derived from session.connected in session/idx
|
||||
NinePConnection *m_daemon = nullptr;
|
||||
|
||||
// Agent state cache — updated via delta events
|
||||
|
|
|
|||
Reference in New Issue