Commit Graph

368 Commits

Author SHA1 Message Date
Levi Neely 405095716a docs: add Send verbatim action and connection recovery to README 2026-08-06 16:15:33 +02:00
Levi Neely 48a5250194 kate: add 'Send verbatim' context action
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.
2026-08-06 14:56:13 +02:00
Levi Neely 4acda0f7de gui: rename 'Human' to 'You' in chat block headers 2026-08-06 14:15:58 +02:00
Levi Neely 2a6441cd53 kate: add connection state tracking and auto-recovery
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
2026-08-06 14:09:33 +02:00
Levi Neely 0ff1025bb9 docs: update README for simplified kate plugin and GUI sidebar toggle 2026-08-06 13:57:37 +02:00
Levi Neely d90a2e6f27 ghost: fix completion overlapping existing document text
- 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
2026-08-06 13:49:22 +02:00
Levi Neely 9887751690 gui: add sidebar toggle (Ctrl+B or click button in header)
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
2026-08-06 13:42:36 +02:00
Levi Neely 7aad74dafe kate: simplify plugin — remove chat panel, keep context actions + ghost text
Strip the plugin down to its core value:
- Context menu actions (explain, fix, refactor, document, test, review diff)
- Ghost text completion (unchanged)
- Git++ review diff integration
- Auto agent-per-project matching (silent, no UI)
- Status bar indicator showing current agent

Removed:
- Chat panel (side panel UI, streaming, renderer)
- Inline annotations
- Emoji processing
- Config page (use env vars: OLLIE_COMPLETE_BACKEND, etc)
- Apply code blocks
- libkateprivate/diff viewer integration

3109 LOC → 1192 LOC. Use the KDE GUI app tiled alongside Kate for chat.
2026-08-06 13:39:30 +02:00
Levi Neely 9a1704d728 Remove all D-Bus mentions from README 2026-08-05 19:01:33 +02:00
Levi Neely 00b904063e Fix close-to-tray comment - it is implemented 2026-08-05 18:59:47 +02:00
Levi Neely 8748c2e540 Fix architecture diagram: KDE programs use 9P clients to access olliesrv
- 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
2026-08-05 18:58:35 +02:00
Levi Neely e8ac6f445d Remove stale tray/daemon/plasmoid build files
- Delete CMakeLists-active.txt and CMakeLists-kf5.txt (legacy D-Bus build
  configs referencing deleted tray/, daemon/, plasmoid/ directories)
- Mark close-to-tray property as unimplemented in thememanager
2026-08-05 18:57:35 +02:00
Levi Neely 7393e4a846 Remove remaining plasmoid/tray references
- Remove Plasmoid from README components, architecture diagram, usage, file layout
- Remove Plasmoid build outputs from Build section
- Remove Plasma dependency from Dependencies
- Remove stale plasmoid/tray paths from justfile uninstall targets
- Fix typo in justfile (q5 -> qt5)
- Remove test-peers.sh from file layout
2026-08-05 18:56:32 +02:00
Levi Neely ce36dcde52 Update KDE README and remove D-Bus dependencies
- 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
2026-08-05 18:53:30 +02:00
Levi Neely 73e262ffaf kate: fix agent selection, block rendering, project change detection
- 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
2026-08-05 17:51:27 +02:00
Levi Neely 2c7b5e6fc5 gui: add heartbeat-based daemon disconnect detection
- 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.
2026-08-05 09:02:51 +02:00
Levi Neely d01d7c8ad8 gui: add session connection status indicator
- 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
2026-08-05 08:35:43 +02:00
Levi Neely 9f3f962567 Remove sidebar state dot indicators 2026-08-04 16:33:21 +02:00
Levi Neely dbc1eabf55 gui: revert to working approach - activeState for selected agent only
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.
2026-08-04 16:26:32 +02:00
Levi Neely 9929abc73b gui: use Connections to update agent state on revision change 2026-08-04 16:24:57 +02:00
Levi Neely 670d759769 gui: use revision inside binding expression for proper dependency tracking 2026-08-04 16:24:10 +02:00
Levi Neely 22980e376a gui: track all agent states via revision counter for proper QML binding
- 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
2026-08-04 16:23:04 +02:00
Levi Neely 1d79d0ee89 gui: agent tree dot uses ollie.activeState directly for active agent 2026-08-04 16:19:33 +02:00
Levi Neely dbd2606ed2 gui: agent tree dot uses ollie.agentState() like top indicator 2026-08-04 16:18:42 +02:00
Levi Neely 457a6a55dc gui: use explicit properties for agent state dot binding
Create agentState and isPaused properties to ensure proper binding
updates when model roles change via setProperty
2026-08-04 16:17:11 +02:00
Levi Neely 9e5352f700 gui: fix agent state dot colors and parentPaused propagation
- 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
2026-08-04 16:15:40 +02:00
Levi Neely 3d739a9560 gui: gray indicators and 'inactive' label for paused sessions
- 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
2026-08-04 16:13:04 +02:00
Levi Neely 2b19cafc50 gui: show disconnected state visually, remove session connected/ended messages
- Session list shows '(not connected)' when daemon disconnected
- Chat area shows 'Not connected' placeholder when disconnected
- Message input disabled and shows 'Not connected' when disconnected
- Chat ListView hidden when disconnected
- Remove sessionConnected/streamingDone signals (visual feedback is sufficient)
2026-08-04 16:11:39 +02:00
Levi Neely b4ae887089 ninepconnection: emit disconnected when leaving Connected state
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.
2026-08-04 16:07:26 +02:00
Levi Neely 22302ce9f1 gui: session tree with Active/Paused groups, arbitrary-depth expand/collapse 2026-08-04 16:05:08 +02:00
Levi Neely 265a1fdda9 refactor: promote remote to session level, remove agent connectivity, formalize NinePConnection state machine
- 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
2026-08-04 15:22:08 +02:00
Levi Neely ec89eaae18 fix: always send stop on ESC regardless of cached 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.
2026-08-04 11:51:01 +02:00
Levi Neely 9d059943f2 cleanup: remove debug logging 2026-08-03 20:43:16 +02:00
Levi Neely 3fd13c6d38 fix: use state role directly for binding updates
model.state doesn't trigger binding re-evaluation in Qt5 delegates.
Using the implicit role 'state' directly does.
2026-08-03 20:31:17 +02:00
Levi Neely 1d0fb99501 targeted state dot updates in render path
- 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
2026-08-03 20:29:38 +02:00
Levi Neely 2d373b4bfb fast path for active agent state changes
- 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
2026-08-03 20:27:45 +02:00
Levi Neely 8792539bcc optimize handleEvent() hot path
- 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).
2026-08-03 20:22:18 +02:00
Levi Neely 249a19e261 single-pass session parsing
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).
2026-08-03 20:16:07 +02:00
Levi Neely 57b3ce04e3 eliminate C++ calls in QML update loop
- 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.
2026-08-03 20:13:32 +02:00
Levi Neely 8a3d52965e 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)
2026-08-03 20:10:49 +02:00
Levi Neely 0e9a7016a3 native 9P client via libollie9p
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.
2026-08-03 20:04:17 +02:00
Levi Neely 24921e7a2b incremental session tree updates
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.
2026-08-03 19:51:54 +02:00
Levi Neely 0c93414251 optimize QML rendering performance
- 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
2026-08-03 19:29:45 +02:00
Levi Neely 8e8466d4ef optimize session refresh with pubsub delta events
- 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.
2026-08-03 19:17:40 +02:00
Levi Neely eedee30332 session pause/resume UI
- 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
2026-08-03 18:36:18 +02:00
Levi Neely 453cef397f live agent state indicators in session tree
- 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
2026-08-03 18:30:14 +02:00
Levi Neely 6210fccf30 feat: session color reflects RPC connection health
- Green: connected and not paused (healthy)
- Gray: paused (intentional)
- Red: not paused but disconnected (crashed/broken)
- Yellow: paused but still connected (zombie)
2026-08-03 18:14:03 +02:00
Levi Neely 566aeddecc feat: show paused sessions as gray, add agent count to session name
- 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)
2026-08-03 18:12:01 +02:00
Levi Neely 22a2208526 fix: show error dialog and reset name when rename fails
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).
2026-08-03 17:53:38 +02:00
Levi Neely 0c47e1163a fix: clear active agent when killed, stop statewait loop
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.
2026-08-03 17:51:15 +02:00