diff --git a/cmd/olliesrv/internal/agent/agent.go b/cmd/olliesrv/internal/agent/agent.go index a6078f7..e3588d4 100644 --- a/cmd/olliesrv/internal/agent/agent.go +++ b/cmd/olliesrv/internal/agent/agent.go @@ -66,6 +66,7 @@ type Agent struct { flush func() // immediately flush persistence onStateChange func(agentID, state string) // optional callback for state changes onProcStart func(agentID string, pid int, tool, cmd string) // optional callback for proc start + onClear func(agentID string) // optional callback for clear // Chat log — the rendered output stream, exposed via 9P. chatMu sync.RWMutex @@ -318,6 +319,9 @@ func (ag *Agent) Clear() error { ag.blockCounter = 0 ag.chatMu.Unlock() ag.chatCond.Broadcast() + if ag.onClear != nil { + ag.onClear(ag.id) + } return nil } diff --git a/cmd/olliesrv/internal/agent/state.go b/cmd/olliesrv/internal/agent/state.go index 41358b1..88ef9d1 100644 --- a/cmd/olliesrv/internal/agent/state.go +++ b/cmd/olliesrv/internal/agent/state.go @@ -87,6 +87,11 @@ func (ag *Agent) SetOnProcStart(fn func(agentID string, pid int, tool, cmd strin ag.onProcStart = fn } +// SetOnClear sets a callback invoked when the agent is cleared. +func (ag *Agent) SetOnClear(fn func(agentID string)) { + ag.onClear = fn +} + // setReply sets the agent's last response. func (ag *Agent) setReply(reply string) { ag.stateMu.Lock() diff --git a/cmd/olliesrv/internal/fs/support.go b/cmd/olliesrv/internal/fs/support.go index 808ad0e..6881d30 100644 --- a/cmd/olliesrv/internal/fs/support.go +++ b/cmd/olliesrv/internal/fs/support.go @@ -26,6 +26,10 @@ func wireAgentEvents(sessID string, ag *agent.Agent) { payload := fmt.Sprintf("%d\t%s\t%s", pid, tool, cmd) session.PublishEvent(topic, payload) }) + ag.SetOnClear(func(agentID string) { + topic := "session." + sessID + ".agent." + agentID + ".clear" + session.PublishEvent(topic, "") + }) } // ctlCmd is one control verb: its name, a one-line description for the diff --git a/kde/gui/ChatPane.qml b/kde/gui/ChatPane.qml index 25f6e18..4c38080 100644 --- a/kde/gui/ChatPane.qml +++ b/kde/gui/ChatPane.qml @@ -386,10 +386,7 @@ Item { font.pixelSize: theme.fontSize > 0 ? theme.fontSize * 0.9 : 11 implicitHeight: 28 enabled: ollie.activeState === "idle" - onClicked: { - ollie.submit("/clear") - chatPane.loadChat() // Refresh the view - } + onClicked: ollie.submit("/clear") ToolTip.visible: hovered ToolTip.text: "Clear history and start fresh" } @@ -676,6 +673,12 @@ Connections { chatPane.updateCurrentBypass() } } + function onAgentCleared(sessionId, agentId) { + // Refresh chat if the cleared agent is the active one + if (sessionId === ollie.activeSessionId && agentId === ollie.activeAgentId) { + chatPane.loadChat() + } + } // Public functions for keyboard shortcuts function approveBypass() { diff --git a/kde/gui/ollie9pclient.cpp b/kde/gui/ollie9pclient.cpp index 0a5ad60..ad5dee7 100644 --- a/kde/gui/ollie9pclient.cpp +++ b/kde/gui/ollie9pclient.cpp @@ -282,6 +282,9 @@ void Ollie9pClient::handleEvent(const QString &eventLine) } emit bypassResolved(sidStr, aidStr, id); } + } else if (agentAction == QLatin1String("clear")) { + // Agent was cleared — emit signal so GUI can refresh chat view + emit agentCleared(sessionId.toString(), agentId.toString()); } } else { // Session-level event diff --git a/kde/gui/ollie9pclient.h b/kde/gui/ollie9pclient.h index 9043832..010c12d 100644 --- a/kde/gui/ollie9pclient.h +++ b/kde/gui/ollie9pclient.h @@ -123,6 +123,7 @@ signals: 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, const QString &requestId); + void agentCleared(const QString &sessionId, const QString &agentId); void pendingBypassCountChanged(const QString &sessionId, const QString &agentId); void procStarted(const QString &sessionId, const QString &agentId, const QString &payload); void rootBackendsLoadedChanged();