Commit Graph

264 Commits

Author SHA1 Message Date
Levi Neely 4acda0f7de gui: rename 'Human' to 'You' in chat block headers 2026-08-06 14:15:58 +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 00b904063e Fix close-to-tray comment - it is implemented 2026-08-05 18:59:47 +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 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 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
Levi Neely 414143955f fix(gui): improve multi-agent-per-session handling
Session selection now auto-selects first agent:
- Clicking a session row selects its first agent (not just the session)
- Auto-select at startup now picks first agent of first session
- Fixes issue where clicking session would clear agent and show empty chat

Improved UI feedback:
- Header shows 'Session / Agent' name instead of just state
- State indicator gray when no agent selected
- Placeholder message when no agent selected: 'Select an agent to view chat'
- Chat ListView hidden when no agent (shows placeholder instead)
- Input already disabled with 'No agent' placeholder (existing behavior)

Cleanup:
- Remove dead declaration reconcileAgentConnections(const QVariantList&)
2026-08-03 17:42:05 +02:00
Levi Neely 28de16dbf1 fix(gui): rewrite chat block rendering for proper syntax highlighting
Reworked ChatBlockModel to fix syntax highlighting issues:

- Replace regex-based renderMarkdown() with line-by-line fence parsing
- Code fences detected by ```lang (open) and ``` alone (close)
- Prose wrapped in <div> with explicit palette text color
- Code blocks syntax highlighted with KSyntaxHighlighting

HtmlHighlighter improvements:
- Add defaultColor for unhighlighted text spans
- Track offset to emit gaps between highlighted tokens
- Add endLine() to flush trailing unhighlighted content

Remove TextEdit color property that was overriding inline styles in Qt5.
2026-08-03 16:16:43 +02:00
Levi Neely 7c347ff6c1 fix(gui): remove TextEdit color property for syntax highlighting
Qt5's TextEdit 'color' property can override inline styles from RichText,
breaking syntax highlighting colors. Remove the property so inline color
spans from KSyntaxHighlighting are respected.

Default text color should come from the application palette.
2026-08-03 16:07:43 +02:00
Levi Neely 21baea8691 fix(gui): add retry backoff for agent connections
Agent connections were using retryMs=0, causing a tight loop when the
/connection file read failed (e.g., toolsConn nil). The loop was:
  connection fails → disconnected signal → refreshSessions →
  reconcileAgentConnections → new connection → fails immediately → repeat

Fix by:
- Add maxRetries parameter to NinePConnection::start()
- Use 5s retry interval with max 3 retries for agent connections
- Reset retry count on successful connection
2026-08-03 16:02:59 +02:00
Levi Neely 8a0a5e6ff9 gui: fix Tread/Tclunk spam after session kill
Three fixes:
1. Disconnect signals before stopping removed connections in
   reconcileAgentConnections — prevents disconnect->refresh loop.
2. Clear m_activeSessionId/m_agentId when killing the active
   session — guard function returns false, statewait stream stops.
3. Same disconnect-before-stop in stopAgentConnections.
2026-08-03 12:28:40 +02:00
Levi Neely 0352d3a046 gui: disable retry on agent connections, let reconcile manage lifecycle
Agent connections no longer retry on their own. When an agent is
killed, the connection goes to Disconnected and stays there.
reconcileAgentConnections (triggered by refreshSessions) is the
sole authority for creating/removing connections.

For the happy path (probe returns normally), the connection
immediately re-probes without a timer delay.
2026-08-03 11:33:41 +02:00
Levi Neely 379afd81bf gui: refresh on agent disconnect instead of showing red
When an agent connection drops (killed or server restart), refresh
the session model from idx rather than immediately marking the
session as disconnected. This avoids a transient red indicator
when an agent is killed but the session is still alive.

The daemon disconnect handler (server down) still correctly marks
everything as disconnected.
2026-08-03 11:31:02 +02:00
Levi Neely c25bb2870c gui: remove dead createSession method 2026-08-03 10:43:09 +02:00
Levi Neely 3fd7907359 gui: show error and keep dialog open on agent creation failure
Change createAgent to return error string (empty=success).
Replace standardButtons with custom footer so dialog stays open
on error, showing the message and letting the user adjust values.
2026-08-03 10:41:32 +02:00
Levi Neely e4c702a691 gui: use immutable ID aliases in 9P paths
Connection monitoring and all 9P operations now use immutable
session/agent UUIDs in paths instead of mutable display names.
The server resolves these via the new Aliases mechanism in findChild.

This fixes the disconnected-indicator bug after session rename:
connections no longer go stale when names change.

Removed dead sessionNameForId/agentNameForId helpers and the
m_agentConnectionPaths tracking (unnecessary with stable paths).
Rename operations no longer restart streams.
2026-08-03 09:40:44 +02:00