Commit Graph

47 Commits

Author SHA1 Message Date
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 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 6e7245dd99 connection statuses 2026-08-02 23:35:14 +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 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 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
Levi Neely 5fc4ebe64e gui: track agents by name, not by index
- setActiveSessionId: only auto-select agent when exactly ONE exists.
  With multiple agents, clear m_agentId and let user click on
  an agent node in the tree to activate it.
- startStateStream: verify current agent directory still exists
  instead of always re-discovering to the first agent. Only
  fall back to first agent if current agent is gone.
- Emit activeAgentIdChanged so QML reacts to agent changes.
2026-08-01 20:49:39 +02:00
Levi Neely e3fc0c49dd fix(gui): stop trimming lines before splitting by tab
The previous fix stopped trimming the whole output but still called
.trimmed() on each individual line, which stripped trailing tabs from
empty-session lines like "testsession2\t\t\t\t\t". Now we split
by tab directly without any trimming — empty fields stay as empty
strings and the parts count stays at 6.
2026-08-01 20:20:01 +02:00
Levi Neely f0a2185e2f fix(gui): trailing tabs stripped by trimmed() dropping empty sessions
refreshSessions() and onEventReadyRead() called .trimmed() on the full
session/idx output, which stripped trailing tab characters from the
last line. An empty session line ("testsession2\t\t\t\t\t")
became just "testsession2" — a single field that failed the size>=2
check and was silently dropped from the session list.
2026-08-01 20:18:26 +02:00
Levi Neely c8fb652785 debug: add qDebug output to session refresh and event handler 2026-08-01 20:16:47 +02:00
Levi Neely b68b72b31f fix(gui): use eventwait data directly, remove focus-stealing timer
- onEventReadyRead() now parses the eventwait data directly instead of
  discarding it and re-reading session/idx
- Removed the 2-second refresh timer that was stealing focus from the
  prompt text area on every tick
2026-08-01 20:13:53 +02:00
Levi Neely adb2760c91 fix(gui): load sessions on startup, show '(no sessions)' placeholder
- ensureRootDataLoaded() now calls refreshSessions() so the initial
  session list is populated immediately, not only on events
- Added '(no sessions)' placeholder label in the session tree ListView
  when no sessions exist
2026-08-01 20:05:31 +02:00
Levi Neely 6fcef9f60c feat(gui): handle empty sessions in ollie-gui
- getAgents(): filter out 'new' (agent creation file), return empty list
- setActiveSessionId(): skip agent init when no agents exist
- submit/interrupt/kill/streams: guard against empty m_agentId
- prompt input: disable when no agent, show 'No agent' placeholder
2026-08-01 19:51:07 +02:00
Levi Neely c4d6f2a1b9 gui: use rdwr (not write) for session/new and agent/new 2026-08-01 18:50:19 +02:00
Levi Neely 153b40cbc7 gui: write name= to session/new, then agent params to session/{name}/agent/new 2026-08-01 18:41:16 +02:00
Levi Neely 1b67305ef7 gui: group session/agent params in new session dialog; add agent alias field 2026-08-01 18:33:05 +02:00
Levi Neely 374349622e gui: stop state stream when session is killed; don't restart if session gone 2026-08-01 18:27:55 +02:00
Levi Neely 363b2e95ed gui: add double-click to rename agent nodes in tree view 2026-08-01 18:17:13 +02:00
Levi Neely 74d346418d gui: re-discover agent ID on state stream start to pick up renames 2026-08-01 18:14:29 +02:00
Levi Neely 3d4e50a386 kill: write to session-level ctl instead of agent ctl
Session-level kill (session/{id}/ctl) does the full cleanup:
removes from session map, cancels context, closes agent, kills
toolsrv process, removes persisted JSON. Agent-level kill just
cancels the session context without cleaning up the filesystem.

Also remove unused getAgents call.
2026-08-01 17:05:19 +02:00
Levi Neely 612adb03f8 gui: add right-click context menu with Kill action for sessions
- Add killSession(sessionId) Q_INVOKABLE to Ollie9pClient
- Accept right button in session tree MouseArea
- Context menu with Kill option on right-click
- MenuItem resolves session ID via parent chain
2026-08-01 17:02:53 +02:00
Levi Neely 76f97c42eb session rename: eventwait stream for live refresh, fix infinite loop 2026-08-01 16:14:43 +02:00
Levi Neely cd0f92e16d session rename: fix renameSession, use setProgram/setArguments 2026-08-01 15:53:03 +02:00
Levi Neely 4bd7a60e78 session rename: clean up comment, use ollie-9p mv 2026-08-01 15:50:31 +02:00
Levi Neely 63cf13b891 session rename: double-click to rename sessions in tree 2026-08-01 15:47:23 +02:00
Levi Neely 3255522144 gui: replace backend/model text fields with editable ComboBoxes, make chat blocks collapsible
- New session dialog: Backend and Model now use proper ComboBoxes with
  editable=true so users can type or select from dropdown
- Backend picker and model picker dialogs removed (no longer needed)
- Model list populated reactively via Connections on backend selection
- Fix model caching: m_rootModels[be].toStringList() returns a copy;
  extract to local QStringList, append, store back into map
- Session creation uses correct path 'session/new' instead of 'new'
- Chat blocks are now collapsible: user/assistant/tool expanded by default,
  reasoning/call/error/info/default collapsed
- Fix chat history on session switch: readLogForSession() reads from the
  explicit session/agent path instead of relying on active state
- Fix delegate layout: use plain ColumnLayout instead of Item-wrapped
  ColumnLayout so ListView calculates implicit heights correctly
2026-08-01 12:25:36 +02:00
Levi Neely 774c3d4d77 gui: new session dialog with directory, backend, model, agent, remote fields
- Add C++ methods: loadRootBackends(), loadRootAgents(), getAvailableModels(), createSession()
- Add Q_PROPERTYs for root data state (backends, agents, models)
- Add '+' button to sessions toolbar
- Create New Session Dialog with:
  - Directory field + Browse button (FolderDialog)
  - Name field (optional, auto-generated if empty)
  - Backend combo (populated from /backends)
  - Model combo (filtered by selected backend)
  - Agent combo (populated from /agents)
  - Remote field (user@host:port)
- Root data loaded lazily on startup
- OK creates session via 'write new' 9P call and refreshes list
2026-08-01 11:55:17 +02:00
Levi Neely 3cedf602be gui: session tree view with agent sub-elements
- Add getAgents() to Ollie9pClient for listing agents per session
- Add switchAgent() for switching sessions+agents atomically
- Add activeAgentId property and activeAgentIdChanged signal
- Replace flat ListView with tree-aware model (session → agents)
- Session nodes expandable/collapsible with ▶/▼ indicator
- Agent nodes indented, clickable to activate that agent
- Active agent highlighted, active session auto-expanded
- State dots on both session and agent rows
2026-08-01 11:43:55 +02:00
Levi Neely 1aa1455f33 state indicator: simple forever-loop via statewait subprocess 2026-08-01 11:16:19 +02:00
Levi Neely de835e9e6e fix QProcess pipe deadlock: drain stdout in run9p while waiting 2026-08-01 10:38:56 +02:00
Levi Neely 4f1c1f7222 gui: fix session switch — emit signals after agent ID discovery
Signals were emitted before m_agentId was set, causing QML's
onActiveSessionIdChanged handler to call readLog() with an empty
agent path. Now: discover agent → emit signals → start streams.
2026-07-31 20:30:17 +02:00
Levi Neely e89b723627 kill D-Bus: krunner+tray use 9P, remove plasmoid+tray, disable kate
- KRunner: rewritten to use plan9port 9p command for all operations
  (list sessions, kill, one-shot generate). No D-Bus dependency.

- Tray: removed. Was a thin D-Bus status wrapper, not useful enough
  to justify maintaining.

- Plasmoid: removed. GUI is standalone now.

- Kate: temporarily disabled in CMake. Header partially converted to
  9P (D-Bus includes removed, QProcess/9P helpers declared). Full
  conversion is next.

- All ninepBin() helpers use $PLAN9/bin/9p (not hardcoded path).

- CMake: removed Qt5::DBus, KF5::Notifications, KF5::Plasma deps.
  Only remaining link deps: Qt5::Core, Qt5::Widgets, Qt5::Quick,
  Qt5::QuickControls2, KF5::Runner.
2026-07-31 20:16:25 +02:00
Levi Neely f4161fcabc gui: replace D-Bus with native 9P streaming
Kill the entire D-Bus rendering pipeline. The GUI now uses plan9port's
9p command as a subprocess for all server communication:

- Chat streaming: persistent 'read chat' process, readyReadStandardOutput
  delivers tokens directly to QML (no polling, no signals, no offsets)
- State: 'read statewait' process, restarts on each state change
- Session list: 'read session/idx' one-shot
- Submit/stop/kill: 'write prompt/ctl' one-shot

The QML is stripped to fundamentals: session list + monospace streaming
text (last 8KB tail) + input field. No ChatBlockModel, no ThemeManager,
no OllieDBusClient, no syntax highlighting, no markdown rendering.

This eliminates the multi-second GUI freezes caused by:
- QTextDocument relayout on 200KB+ accumulated text
- loadEarlier cascade creating hundreds of delegates
- KSyntaxHighlighting::Repository blocking init
- Offset mismatch triggering synchronous D-Bus calls

Features will be re-added incrementally on this proven foundation.
D-Bus retained only for plasmoid/tray/KRunner (desktop integration).
2026-07-31 20:06:05 +02:00