#ifndef OLLIE9PCLIENT_H #define OLLIE9PCLIENT_H #include #include #include #include #include #include #include "lib9pclient.h" #include "lib9pstreamer.h" // 9P-based client for ollie. Uses native libollie9p for all operations. // Streaming files (chat, event) use Lib9pStreamer threads. class Ollie9pClient : public QObject { Q_OBJECT QML_ELEMENT QML_SINGLETON 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 activeSessionPaused READ activeSessionPaused NOTIFY activeSessionPausedChanged) 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 workflowsLoaded READ workflowsLoaded NOTIFY workflowsLoadedChanged) Q_PROPERTY(QStringList availableWorkflows READ availableWorkflows NOTIFY availableWorkflowsChanged) 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 activeSessionPaused() const { return m_activeSessionPaused; } 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 workflowsLoaded() const { return m_workflowsLoaded; } QStringList availableWorkflows() const { return m_availableWorkflows; } 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 int pendingBypassCount(const QString &sessionId, const QString &agentId) 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 void loadWorkflows(); Q_INVOKABLE QStringList variantsForWorkflow(const QString &workflow) const; Q_INVOKABLE QStringList getAvailableModels(const QString &backend) const; Q_INVOKABLE bool createQuickSession(); Q_INVOKABLE QString createSession(const QString &name, const QString &remote, const QString &cwd = QString(), const QString &workflow = QString(), const QString &variant = QString(), bool yolo = false); Q_INVOKABLE QVariantMap readSessionConfig(const QString &sessionName) const; Q_INVOKABLE QString updateSessionConfig(const QString &sessionName, const QString &key, const QString &value); Q_INVOKABLE QVariantMap readAgentConfig(const QString &sessionId, const QString &agentId) const; Q_INVOKABLE QString updateAgentConfig(const QString &sessionId, const QString &agentId, const QString &key, const QString &value); Q_INVOKABLE void setGoal(const QString &sessionName, const QString &goalText); Q_INVOKABLE QString createAgent(const QString &sessionId, 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 bool resolveBypass(const QString &sessionId, const QString &agentId, const QString &id, bool approve); Q_INVOKABLE QString lastError() const { return m_9p ? m_9p->lastError() : QString(); } signals: void sessionsChanged(); void activeSessionIdChanged(); void activeAgentIdChanged(); void activeStateChanged(); void activeSessionPausedChanged(); void agentStateChanged(const QString &sessionId, const QString &agentId, const QString &state); void chatReceived(const QString &text); void bypassRequested(const QString &sessionId, const QString &agentId, const QString &id, const QString &cmd, const QString &cwd); void bypassResolved(const QString &sessionId, const QString &agentId); void pendingBypassCountChanged(const QString &sessionId, const QString &agentId); void rootBackendsLoadedChanged(); void availableBackendsChanged(); void rootAgentsLoadedChanged(); void availableAgentsChanged(); void workflowsLoadedChanged(); void availableWorkflowsChanged(); 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(); void checkPendingBypass(); Lib9pClient *m_9p = nullptr; QTimer *m_heartbeatTimer = nullptr; QVariantList m_sessions; QString m_activeSessionId; QString m_activeState = "idle"; QString m_agentId; bool m_activeSessionPaused = false; // Root data cache bool m_rootBackendsLoaded = false; QStringList m_availableBackends; bool m_rootAgentsLoaded = false; QStringList m_availableAgents; bool m_workflowsLoaded = false; QStringList m_availableWorkflows; QHash m_workflowVariants; bool m_rootModelsLoaded = false; QVariantMap m_rootModels; QString m_currentBackend; bool m_daemonConnected = false; // Agent state cache — updated via delta events QHash m_agentStateValues; // Pending bypass requests per agent: agentKey -> set of bypass IDs QHash> m_pendingBypasses; // Native streaming readers Lib9pStreamer *m_chat = nullptr; // Guarded — reads agent chat log Lib9pStreamer *m_event = nullptr; // Looping — reads server event (includes state changes) }; #endif // OLLIE9PCLIENT_H