Sends selected text wrapped in a code fence with the appropriate language
(derived from Kate's highlighting mode). Useful for pasting code snippets
directly to the agent without any surrounding prompt text.
Adds a heartbeat timer (5s interval) that pings the 9P server with a
cheap read. If the server goes down, the status bar turns red showing
'disconnected'. When it comes back, the plugin reconnects and
re-selects the agent for the current project.
Status bar now shows connection state:
- Red '✗ disconnected' — server unreachable
- Gray '○ no agent' — connected but no agent selected
- Green '● kate/ID' — connected with active agent
- Don't show inline ghost note when cursor is mid-line (text exists
after cursor) — the ghost would visually conflict with real content
- Only show multi-line overlay when cursor is near EOF, so continuation
lines don't paint on top of existing document lines
- Tab-accept still works regardless of visual display
Adds ability to hide/show the session list sidebar:
- Ctrl+B keyboard shortcut
- Toggle button (◀/▶) in the chat header
- Sidebar collapses to zero width when hidden
- olliesrv is the single 9P server, not parallel to ollie-9p
- KDE programs connect via libollie9p, ollie-9p subprocess, or plan9port's 9p
- Remove remaining Plasmoid reference from intro
- Rewrite README.md with accurate 9P-based architecture
- Remove all D-Bus dependencies:
- Update dolphin/ollie-ask to use ollie-9p rdwr generate
- Update kate/ollie_ghost.cpp to use ollie-9p rdwr complete
- Remove QDBusInterface from ollie_ghost.h
- Fix OllieGhostProvider constructor in ollie_kate.cpp
- Remove D-Bus comments from gui/main.cpp and kate/ollie_kate.cpp
- Remove QtDBus from CMakeLists.txt (KF5 and KF6)
- Delete D-Bus test scripts test-e2e.sh and test-peers.sh
- Clarify no system tray component exists
- Update build/install instructions with just commands
- newAgent() is idempotent: selects existing agent by cwd if one exists
- Use rdwr() to get new agent ID from server instead of ls() ordering
- selectAgentForProject() returns bool for caller branching
- Connect to Kate project plugin projectChanged signal
- Update chat block parser to [[[type]]] / [[[end]]] format
- Guard pluginViewCreated for KF5 < 5.78
- Add 2.5 second heartbeat timer using Lib9pClient to read 'help'
- Heartbeat detects daemon death reliably (vs unreliable process exit codes)
- On disconnect: clear sessions, stop streams, reset active selection
- On reconnect: refresh sessions, restart agent streams
Replaces flaky NinePConnection::disconnected signal which depended on
eventwait process exit behavior.
- Add colored dot to session rows showing tool server RPC status:
green=connected, yellow=paused+connected, gray=paused, red=broken
- Remove vestigial agent.connected from model (was never displayed)
- Session-level connected status is the correct granularity
Non-selected agents show idle (green). This matches the working
top indicator approach until we find a proper solution for tracking
all agent states reactively in QML.
- Add agentStateRevision property that increments on any state change
- QML binds to revision to trigger re-evaluation of agentState() calls
- All agent dots now show correct live state, not just active agent
- Update parentPaused on agents during incremental session updates
- Set parentPaused and parentExpanded when inserting new agents
- Update session paused property during updates
- Use explicit === true check for parentPaused
- Agent dots in tree are gray when parent session is paused
- Top state indicator is gray when viewing agent in paused session
- Top state label shows 'inactive' instead of agent state when paused
Previously disconnected was only emitted when entering Disconnected state,
not when transitioning Connected->RetryWait. This caused Lib9pClient to
keep a stale handle when olliesrv restarted, preventing reconnection.
- Add createSession(name, remote) with New Session dialog
- Remove agent connectivity code (redundant with session connectivity)
- Refactor NinePConnection as formal state machine with explicit events
- Fix silent restart to not cycle through Connecting state
Removes the activeState !== 'idle' check before sending the stop
command. The server-side Interrupt() is safe to call on an idle
agent (returns false, no-op).
This fixes an intermittent issue where pressing ESC wouldn't stop
the agent because the GUI's cached activeState was stale/out-of-sync
with the server's actual state.
- onAgentStateChanged: update specific agent in model instead of version bump
- State dot: use model.state directly instead of ollie.agentState() callback
- Remove ollie.isSessionPaused() call (was O(n) iteration per agent)
- Remove unused agentStateVersion property
Before: 1 state change → re-evaluate ALL agent dots → N * ollie.agentState() calls
After: 1 state change → setProperty on 1 model item → 1 binding update
- Check if event is for active agent FIRST using QStringView comparison
- Active agent: compare payload directly against m_activeState (no hash lookup)
- Non-active: use iterator find() to avoid double hash lookup
For the most common case (active agent state change), this reduces
the hot path to: QStringView compare → QString convert → direct compare
- Use QStringView to avoid string allocations during parsing
- Use QLatin1String for comparisons (faster than QString)
- Manual indexOf parsing instead of split('.') allocation
- Early return for non-session events
Reduces allocations in the agent.state hot path from ~7 QStrings
to 4 (only when state actually changes and needs to be stored).
Consolidate refreshSessions() to do everything in one iteration:
- Parse session/idx data
- Update m_agentConnected cache
- Track active session/agent existence
- Emit connection state change
Eliminates separate reconcileAgentConnections() call and second loop
to check active session/agent. O(n) instead of O(3n).
- computeConnectionColor() in QML instead of ollie.sessionConnectionColor()
(avoids O(n²) iteration through sessions list)
- Use agent.state from session/idx data directly instead of ollie.agentState()
(avoids hash lookup per agent per update)
All data now flows from session/idx through m_sessions to QML with no
callbacks into C++ during the update loop.
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)
Replace subprocess spawning (run9p) with native 9P connection for hot paths:
- refreshSessions() - called on every sessionsChanged event
- loadRootBackends() - called on daemon connect
- loadRootAgents() - called on daemon connect
Lib9pClient wraps the Go shared library with a Qt-friendly C++ API.
Connection persists across operations, eliminating ~2-5ms per-call
process spawn overhead.
Requires libollie9p.so from parent repo's lib9p/ package.
Instead of clearing and rebuilding the entire session tree model on
every sessionsChanged signal, diff old vs new and:
- Update existing session/agent properties in place
- Remove deleted sessions/agents
- Append new sessions/agents
This avoids O(n) model rebuilds that cause ListView to re-render all
delegates, improving responsiveness with many sessions.
- Remove console.log statements from hot paths
- Add sessionExpandedCache for O(1) expanded state lookup
- Replace O(n) parentSessionExpanded() loop with cache lookup
- Preserve expanded state across session tree rebuilds
- Parse new session/idx format with all data in one read:
name, id, paused, connected, cwd, backend, model, agents
- Handle delta events from eventwait instead of full refresh:
- session.{sid}.agent.{aid}.state -> update cache, emit signal
- Structural changes (new/kill/pause/resume) -> full refresh
- Remove per-agent statewait streams (eliminated N processes)
- Single eventwait stream handles all events via pubsub
This eliminates O(sessions * agents) synchronous reads per refresh,
replacing them with one read + incremental delta updates.
- Add Pause/Resume toggle to session context menu
- Gray agent dots when parent session is paused
- Disable input field with 'Session paused' placeholder when paused
- Preserve focus on prompt input during tree rebuilds
- Add per-agent statewait streams to track execution state
- Agent dots show: green=idle, orange=calling, blue=thinking
- Session dots show RPC connection health (unchanged)
- Use version counter pattern to force QML binding re-evaluation
- Green: connected and not paused (healthy)
- Gray: paused (intentional)
- Red: not paused but disconnected (crashed/broken)
- Yellow: paused but still connected (zombie)
- Fetch paused status from session/{id}/paused file
- Gray dot indicator for paused sessions (green otherwise)
- Session names now show agent count: 'ollie (2)'
- Fixed rename to use baseName for sessions (without count suffix)
commitRename now checks the return value from renameSession/renameAgent.
On failure, resets displayName to the old value and shows an error dialog
explaining the rename failed (e.g., name already in use).
refreshSessions now checks if the active session/agent still exist after
updating the list. If the agent was killed, clears m_agentId and stops
streams. The statewait guard will then return false, stopping the loop.