#ifndef OLLIE9PCLIENT_H #define OLLIE9PCLIENT_H #include #include #include #include #include #if QT_VERSION_MAJOR >= 6 #include #endif #include "lib9pclient.h" #include "nativestreamer.h" // 9P-based client for ollie. Uses native libollie9p for all operations. // Streaming files (chat, statewait, eventwait) use NativeStreamer threads. class Ollie9pClient : public QObject { Q_OBJECT #if QT_VERSION_MAJOR >= 6 QML_ELEMENT QML_SINGLETON #endif Q_PROPERTY(QVariantList sessions READ sessions NOTIFY sessionsChanged) Q_PROPERTY(QString activeSessionId READ activeSessionId WRITE setActiveSessionId NOTIFY activeSessionIdChanged) Q_PROPERTY(QString activeAgentId READ activeAgentId NOTIFY activeAgentIdChanged) Q_PROPERTY(QString activeState READ activeState NOTIFY activeStateChanged) Q_PROPERTY(bool rootBackendsLoaded READ rootBackendsLoaded NOTIFY rootBackendsLoadedChanged) Q_PROPERTY(QStringList availableBackends READ availableBackends NOTIFY availableBackendsChanged) Q_PROPERTY(bool rootAgentsLoaded READ rootAgentsLoaded NOTIFY rootAgentsLoadedChanged) Q_PROPERTY(QStringList availableAgents READ availableAgents NOTIFY availableAgentsChanged) Q_PROPERTY(bool rootModelsLoaded READ rootModelsLoaded NOTIFY rootModelsLoadedChanged) Q_PROPERTY(QString currentBackend READ currentBackend WRITE setCurrentBackend NOTIFY currentBackendChanged) Q_PROPERTY(bool daemonConnected READ daemonConnected NOTIFY daemonConnectedChanged) public: explicit Ollie9pClient(QObject *parent = nullptr); ~Ollie9pClient(); QVariantList sessions() const { return m_sessions; } QString activeSessionId() const { return m_activeSessionId; } Q_INVOKABLE void setActiveSessionId(const QString &id); QString activeAgentId() const { return m_agentId; } QString activeState() const { return m_activeState; } bool rootBackendsLoaded() const { return m_rootBackendsLoaded; } QStringList availableBackends() const { return m_availableBackends; } bool rootAgentsLoaded() const { return m_rootAgentsLoaded; } QStringList availableAgents() const { return m_availableAgents; } bool rootModelsLoaded() const { return m_rootModelsLoaded; } QString currentBackend() const { return m_currentBackend; } bool daemonConnected() const { return m_daemonConnected; } Q_INVOKABLE QString agentState(const QString &sessionId, const QString &agentId) const; Q_INVOKABLE QString sessionConnectionColor(const QString &sessionId) const; Q_INVOKABLE void setCurrentBackend(const QString &backend) { if (m_currentBackend == backend) return; m_currentBackend = backend; emit currentBackendChanged(); } Q_INVOKABLE void refreshSessions(); Q_INVOKABLE QString readLog(); Q_INVOKABLE QString readLogForSession(const QString &sessionId, const QString &agentId); Q_INVOKABLE bool submit(const QString &prompt); Q_INVOKABLE bool interrupt(); Q_INVOKABLE bool kill(); Q_INVOKABLE bool killSession(const QString &sessionId); Q_INVOKABLE bool pauseSession(const QString &sessionId); Q_INVOKABLE bool resumeSession(const QString &sessionId); Q_INVOKABLE bool isSessionPaused(const QString &sessionId) const; Q_INVOKABLE QString getConfig(); Q_INVOKABLE QStringList getAgents(const QString &sessionId); QString agentKey(const QString &sessionId, const QString &agentId) const; Q_INVOKABLE void setActiveAgentId(const QString &agentId); Q_INVOKABLE void switchAgent(const QString &sessionId, const QString &agentId); Q_INVOKABLE void loadRootBackends(); Q_INVOKABLE void loadRootAgents(); Q_INVOKABLE QStringList getAvailableModels(const QString &backend) const; Q_INVOKABLE bool createQuickSession(); Q_INVOKABLE QString createSession(const QString &name, const QString &remote); Q_INVOKABLE QString createAgent(const QString &sessionId, const QString &cwd, const QString &backend, const QString &model, const QString &agent, const QString &remote, const QString &agentAlias); Q_INVOKABLE bool killAgent(const QString &sessionId, const QString &agentId); Q_INVOKABLE bool renameSession(const QString &sessionId, const QString &newName); Q_INVOKABLE bool renameAgent(const QString &sessionId, const QString &agentId, const QString &newName); Q_INVOKABLE QString lastError() const { return m_9p ? m_9p->lastError() : QString(); } signals: void sessionsChanged(); void activeSessionIdChanged(); void activeAgentIdChanged(); void activeStateChanged(); void agentStateChanged(const QString &sessionId, const QString &agentId, const QString &state); void chatReceived(const QString &text); void rootBackendsLoadedChanged(); void availableBackendsChanged(); void rootAgentsLoadedChanged(); void availableAgentsChanged(); void rootModelsLoadedChanged(); void currentBackendChanged(); void daemonConnectedChanged(); private: void stopAgentStreams(); void stopStreams(); QString agentPath() const; void ensureRootDataLoaded(); void setDaemonConnected(bool connected); void startActiveAgentStreams(); void startEventStream(); void handleEvent(const QString &eventLine); void heartbeat(); // Native 9P client (persistent connection, no subprocess overhead) Lib9pClient *m_9p = nullptr; // Heartbeat timer — detects daemon death QTimer *m_heartbeatTimer = nullptr; QVariantList m_sessions; QString m_activeSessionId; QString m_activeState = "idle"; QString m_agentId; // Root data cache bool m_rootBackendsLoaded = false; QStringList m_availableBackends; bool m_rootAgentsLoaded = false; QStringList m_availableAgents; bool m_rootModelsLoaded = false; QVariantMap m_rootModels; QString m_currentBackend; bool m_daemonConnected = false; // Agent state cache — updated via delta events QHash m_agentStateValues; // Native streaming readers (replace StreamFsm/NinePConnection) NativeStreamer *m_chat = nullptr; // Oneshot — reads agent chat log NativeStreamer *m_state = nullptr; // Guarded — reads agent statewait NativeStreamer *m_event = nullptr; // Looping — reads server eventwait }; #endif // OLLIE9PCLIENT_H