Commit Graph

425 Commits

Author SHA1 Message Date
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 5e22545b2e cmake: disable kate plugin build (needs rework)
The kate plugin code remains in the tree but is not built or
installed until it's updated for the current 9P namespace.
2026-08-03 10:59:00 +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
Levi Neely 0f51c83f24 gui: rename 'Alias' to 'Name' in new agent dialog 2026-08-03 09:22:08 +02:00
Levi Neely 84fb53332c gui: separate session and agent creation, add agent context menu
- '+' button now creates an empty session directly (no dialog)
- Dialog becomes 'New Agent' (session name field removed)
- Session context menu: 'New Agent...' + 'Kill Session'
- Agent context menu: 'Kill' (writes kill to agent ctl)
- New C++ methods: createQuickSession(), createAgent(), killAgent()
2026-08-03 09:16:00 +02:00
Levi Neely 042f287796 gui: resolve session/agent names from IDs before building 9P paths
killSession, renameSession, readLogForSession, getAgents, and
renameAgent were using immutable IDs directly in paths like
session/{id}/ctl. The 9P namespace uses the mutable display name
as the directory key, not the ID. Use sessionNameForId() and
agentNameForId() consistently (matching agentPath()).
2026-08-03 09:09:49 +02:00
Levi Neely 34d4a97b3d gui: complete KF5 build with full feature parity
- Add chatblockmodel.cpp and thememanager.cpp to KF5 GUI target
- Link KF5::ConfigCore and KF5::SyntaxHighlighting to GUI
- Strip Qt6-only grouped palette syntax from QML at configure time
  (ThemeManager::applyPalette handles palette via QGuiApplication)
- Fix QStringList brace-init ambiguity in Qt5
- Fix QList::remove(pos,count) not available in Qt5 (use erase)
2026-08-03 09:03:12 +02:00
Levi Neely 7cb809fad2 revert all system-user-interruption changes 2026-08-03 00:41:49 +02:00
Levi Neely 64883cf9b8 Revert "render system-user-interruption as styled yellow box in prose"
This reverts commit 4e455e2dd3.
2026-08-03 00:39:34 +02:00
Levi Neely 4e455e2dd3 render system-user-interruption as styled yellow box in prose 2026-08-03 00:38:01 +02:00
Levi Neely e08b69c152 fix build: replace-with-lambda requires Qt 6.5+, use manual global-match loop 2026-08-03 00:32:26 +02:00
Levi Neely 1fdae741c3 render system-user-interruption as a styled yellow box with warning icon 2026-08-03 00:31:08 +02:00
Levi Neely cf4313165b fix fence close regex: only match backtick on its own line 2026-08-03 00:27:25 +02:00
Levi Neely b841f556d4 gui: preserve newlines in plaintext blocks 2026-08-03 00:24:52 +02:00
Levi Neely 4dabab1f71 fix connection dot indicators 2026-08-02 23:59:28 +02:00
Levi Neely 41f95e84c2 session ended as disconnect signal 2026-08-02 23:51:40 +02:00
Levi Neely b50a2e01b2 fix source block rendering 2026-08-02 23:48:00 +02:00
Levi Neely 6e7245dd99 connection statuses 2026-08-02 23:35:14 +02:00
Levi Neely c983e2bb6a simplify rendering 2026-08-02 22:43:11 +02:00
Levi Neely 220adf9b05 Fix session and agent rename handling 2026-08-02 22:34:39 +02:00
Levi Neely bd78b2b703 duplicate param 2026-08-02 22:10:31 +02:00
Levi Neely 753467db5b fix: preserve pre-fence text and post-fence content in rendered blocks
When a fence () closes inside a block, prepend any content
that came before the fence to renderedHtml so both text and the
highlighted fence show up. When content is appended after the fence,
keep renderedHtml in sync by appending the escaped text. This prevents
the fence from 'eating' surrounding content.
2026-08-02 11:13:22 +02:00
Levi Neely 67b33f95d2 refactor: StreamFsm lifecycle + chatblock model FSM fix
- Introduce StreamFsm class wrapping QProcess lifecycle with
  Oneshot/Looping/Guarded restart policies
- Replace raw QProcess management in Ollie9pClient with StreamFsm
- Fix chatblockmodel FSM to properly handle [[[end]]] and fence
  transitions with a combined State enum (Outside/InBlock/InFence)
- Add Q_INVOKABLE to setActiveSessionId for direct QML access
- Add streamfsm.cpp to CMakeLists.txt for both KF5 and KF6 builds
2026-08-02 11:11:05 +02:00
Levi Neely bfac41b2ae gui: update eventwait to use new global /eventwait with structured events
- Changed path from session/eventwait to /eventwait (global, not per-session)
- Use ollie-9p instead of plan9port 9p for the read
- Changed from parsing full session index to just calling refreshSessions()
  on any event (the event format is now structured deltas, but the GUI
  is kept simple with a full index refresh)
2026-08-02 10:25:10 +02:00
Levi Neely cd7bb9e272 gui: stop validating statewait via agent directory listing
startStateStream now trusts the explicit session/agent selection and
opens session/<sid>/agent/<aid>/statewait directly. It no longer calls
getAgents(), which was repeatedly listing session/<sid>/agent on every
statewait reopen.
2026-08-01 21:53:01 +02:00
Levi Neely 3bb75b6c20 gui: stop rebuilding session tree on selection changes
The dominant 9P spam came from buildSessionTree() re-listing
session/<sid>/agent on every activeSessionIdChanged and
activeAgentIdChanged signal. Tree rebuilds are now limited to
sessionsChanged and startup refresh; selection changes only
refresh the right pane transcript.
2026-08-01 21:48:04 +02:00
Levi Neely 50bca0a770 gui: add debug logs for session/agent selection and statewait startup 2026-08-01 21:24:38 +02:00
Levi Neely b8fc64fb18 gui: clicking a session clears explicit agent selection
- Session row click now calls setActiveSessionId(id)
- setActiveSessionId no longer returns early for same-session clicks;
  it always clears m_agentId and stops per-agent streams
- This enforces a clean 'session selected, no agent selected' state
2026-08-01 21:22:33 +02:00
Levi Neely 38cd1313ec gui: make switchAgent the only stream-start path
- setActiveSessionId remains session-only and never starts streams
- setActiveAgentId now clears any existing per-agent streams and only
  updates selection/state
- switchAgent is explicitly the only place that starts chat/statewait
- onStateFinished only reopens for the current explicit selection
2026-08-01 21:20:52 +02:00
Levi Neely 22e58b348f gui: require explicit agent selection for statewait/chat
- setActiveSessionId now selects only the session; it clears m_agentId
  and never auto-selects a lone agent.
- startStateStream no longer re-resolves or falls back to another agent;
  it only runs when the explicit active agent still exists.
- QML now handles activeAgentId changes explicitly and keeps the right
  pane blank/disabled when no session or no agent is selected.
2026-08-01 21:18:09 +02:00
Levi Neely 56b08d8f8f Revert "gui: never auto-restart statewait/chat streams on finish"
This reverts commit 608127e4ac.
2026-08-01 20:59:08 +02:00
Levi Neely 9f51a2181b Revert "gui: verify agent directory before starting statewait/chat streams"
This reverts commit dbbcc07934.
2026-08-01 20:59:08 +02:00
Levi Neely dbbcc07934 gui: verify agent directory before starting statewait/chat streams
Add a run9p stat check before opening blocking reads on
statewait and chat. If the agent directory doesn't exist,
skip starting (prevents tight restart loops when a session
is killed while streams are active). Only one statewait
reader runs at a time.
2026-08-01 20:56:40 +02:00
Levi Neely 608127e4ac gui: never auto-restart statewait/chat streams on finish
onStateFinished and onChatFinished no longer restart their
streams. Streams are only started when the user explicitly
selects an agent (setActiveSessionId for single-agent,
switchAgent for multi-agent). This prevents tight restart
loops when pointing to a nonexistent directory.
2026-08-01 20:55:41 +02:00
Levi Neely ef03cdc7f3 gui: deduplicate sessions from multi-agent index, fix field mapping
- Deduplicate session entries in refreshSessions and onEventReadyRead
  using QSet to skip duplicate session IDs from multi-agent index.
- Fix field mapping: index format is name\tstate\tcwd\tbackend\tmodel\tagent
- Added QSet include
2026-08-01 20:53:07 +02:00