The ollie.protocol file was being installed to kservices5/, which is
the KF5 mechanism. KF6 KIO workers embed protocol metadata in the
plugin's JSON file (ollie.json), which we already have.
Remove:
- kio/ollie.protocol file
- CMake install rule for .protocol file
Manual cleanup needed: sudo rm -rf /usr/local/share/kservices5/
- Add procStarted signal to Ollie9pClient for proc.start events
- Add listProcs(), procTerm(), procKill(), procOutput() methods
- Create ProcManagerDialog.qml showing background processes for
the active session/agent with status, runtime, commands
- Add 'Procs' button to action toolbar to open the dialog
- Auto-refresh every 2 seconds when dialog is visible
- Support terminate, kill, and view output actions
The GitWidget delays initialization until showEvent. Find the
QHBoxLayout containing QToolButtons (the button bar) and insert
our button there, next to Commit/Push/Pull buttons.
The GitWidget creates local QMenu objects that can't be intercepted.
Instead, add a QToolButton with dropdown menu to the git tool view:
- Review Staged Changes
- Review Unstaged Changes
- Explain Selected Files
This approach doesn't require Kate source modification.
The previous approach using QMetaObject::invokeMethod on gitWidget()
failed because that method isn't Q_INVOKABLE. The new approach:
- Find git tool view by object name or 'git' in name
- Install event filter to catch QEvent::ChildAdded
- When a QMenu child is added, connect to aboutToShow
- Add Ollie actions to the menu before it's shown
Also add retry timer for setupProjectGitWidget since the plugin
view may not exist at construction time.
- setupProjectGitWidget(): Hook into kateprojectplugin's GitWidget
via gitWidget() method, install event filter on tree view
- addOllieActionsToGitContextMenu(): Add 'Review Staged Changes' and
'Ask About Selection' to the git widget's context menu
- reviewStagedChanges(): Get staged (or unstaged) diff via git process
- askAboutGitSelection(): Ask Ollie about selected files in git view
- onWidgetAdded(): Detect DiffWidget additions by shouldClose() method
- onDiffWidgetContextMenu(): Add 'Review This Diff' to diff widget
- reviewDiffFromWidget(): Extract diff from widget and send to Ollie
- Remove cut/copy/paste menu items from context menu
- Remove paste button (📋) from input row
- Remove B1+B2/B1+B3 chord handling for cut/paste
- Simplify mouse handling to just B2=exec, B3=plumb
- Standard Ctrl+X/C/V still work via TextArea defaults
- Agent cwd is optional; empty = inherit session cwd (the common case)
- Session cwd is required at creation and is the inheritance root
- toolsrv maintains agentCWD map, resolved per call from agent= field
- Override set via agent cfg (cwd=...) or ctl (cwd [<dir>|-])
- Agent.SyncCwdToToolServer re-pushes on every (re)connect
- GUI NewAgentDialog shows '(inherit: <sessionCwd>)' as placeholder
- proc_test.go covers per-agent cwd isolation
- Kate and acme scripts updated for new session/agent creation flow
- AGENTS.md documents the architecture
Agent names derived from cwd using parent:prefix format:
- /home/user/src/ollie -> src:ollie
- /home/user/proj/ABC-123_feature/repo -> abc-123:repo
Updates:
- Kate plugin: one agent per project, git root fallback if no project plugin
- acme-ollie-ensure: single 'acme' session, per-project agents
- ollie-session-here: single 'default' session, per-project agents
Also fixed write -> rdwr for session/agent creation endpoints
Backend:
- Add proc.start/proc.exit events for background process lifecycle
- Add proc idx ctl command for machine-readable process listing (TSV)
- Add event.pub file for external event publishing
- Add ListProcsIdx to toolclient and toolsrv
- Fix bypass commands to use Setpgid for process group isolation
GUI:
- Add configurable bypass approval shortcuts (Ctrl+Y/Ctrl+N default)
- Add Keyboard Shortcuts section to Settings dialog
- Store shortcuts in theme.conf
Kate:
- Fix 'Start Session Here' - use rdwr for session/new endpoint
The isSessionPaused() Q_INVOKABLE didn't trigger QML binding
re-evaluation on pause/resume. New property with proper signal
makes ChatPane bindings reactive.
- activeSessionPaused property tracks active session's paused state
- Updated on refreshSessions() and switchAgent()
- ChatPane.qml uses property instead of function call
Size and margins scale with row height:
- height: 30% of row height
- width: same as height (circular)
- left margin: 15% of row height
- right margin: 25% of row height
Small colored dot to the right of agent name shows execution state:
- Green: idle
- Blue: thinking
- Orange: calling tool
- Gray: paused
Updates reactively via agentStateChanged event signal.
Server emits session.{sid}.agent.{aid}.bypass.resolved with id and
action (approved/denied) when a bypass is resolved by any client.
GUI handles bypass.resolved events to clear the banner and pending
count when CLI or another client resolves a bypass request.
This allows CLI 'o sess approve' to clear the GUI banner automatically.
Track multiple concurrent pending bypasses per agent in C++ backend:
- Add m_pendingBypasses QHash<QString,QSet<QString>> keyed by session:agent
- Add pendingBypassCount(sessionId, agentId) Q_INVOKABLE
- Add pendingBypassCountChanged(sessionId, agentId) signal
- Update resolveBypass to take agentId and remove from tracking set
SessionTree.qml: show ⚠ indicator left of agent name when the agent
has pending bypass requests. Uses Connections to refresh on signal.
ChatPane.qml: pass agentId to resolveBypass calls.
- Changed event topic: session.{sid}.agent.{aid}.bypass.request
- Added agentId parameter to bypassRequested signal
- Filter bypass events to show only for the active agent
The event stream with filtering replaces statewait:
- echo filter | rdwrs event
Removed:
- statewait file from agent namespace
- All non-historical references in docs and code
The state file remains for simple polling reads.
The server event stream already delivers state change events for all
agents. Removed the redundant per-agent statewait streamer - now agent
switching has no teardown/startup overhead for state monitoring.
- Removed m_state streamer entirely
- State updates come via event stream's session.{sid}.agent.{aid}.state events
- Only chat stream needs per-agent setup/teardown
EventValue was storing only the latest event and using hash comparison,
which caused events to be overwritten if they arrived faster than the
client could read them.
Now eventwait uses Stream mode with EventStream which delivers each
event as it arrives. Events won't be lost due to rapid arrival.
Events can be missed if GUI wasn't connected when they were published.
Now checkPendingBypass() reads the bypass file directly when switching
agents/sessions to catch any pending requests.
Server changes:
- Session tracks pending bypass request and exposes methods
- New 9P files: session/{sid}/bypass (read/write), bypasswait (blocking)
- Publish bypass.request events for GUI listeners
GUI changes:
- Handle bypass.request events from eventwait
- Show inline amber banner with command and cwd
- Approve/Deny buttons resolve via 9P
Desktop notifications still work in parallel for non-GUI usage.
When switching agents, the old state/chat streams would block until
data arrived (up to 500ms for statewait, indefinitely for chat).
Now requestStop() closes the fid from the calling thread, which
unblocks the worker's blocking read immediately. This makes agent
switching instant instead of waiting for the read timeout.
QThread::~QThread() aborts if the thread is still running. The previous
code called delete unconditionally after a 100ms wait timeout. Now we
follow the same pattern as stopWorker(): check if wait() succeeded,
and if not, abandon the thread safely via deleteLater.
Switch streaming reads (chat, state, events) from subprocess-based
ollie-9p to native lib9p via worker threads. This eliminates:
- Process spawn/teardown overhead
- Subprocess failure modes
- Signal handling complexity
Lib9pStreamer uses QThread workers with blocking reads. On stop,
workers are given 200ms to exit cleanly, then abandoned (they'll
exit when the read completes or connection closes).
Also: make KRunner/Kate/KIO plugins optional in CMakeLists.txt
to fix build when KF6Runner etc aren't installed. Added
QT_DEFAULT_MAJOR_VERSION=6 to fix Qt6 detection with CMake 4.x.
Two bugs:
1. GUI sends 'agent=<profile>' but server only recognized 'profile='
- Added 'agent' as alias for 'profile' in parseAgentNewRequest
2. Model dropdown showed cost columns appended to model names
- /models format: backend<tab>model[<tab>in<tab>out...]
- Parser took everything after first tab as model name
- Now stops at second tab to extract just the model name
Drop the OLLIE_KF5 CMake option and the entire Qt5/KF5 build branch;
delete KF5-only assets (99-ollie-kf5.sh, ollie-actions-kf5.desktop);
collapse all QT_VERSION_MAJOR and KTEXTEDITOR_VERSION_MAJOR conditionals
to the KF6 path in the KRunner, Kate, KIO, and GUI sources; update
Makefile, README, and docs. Verified: KF6 configure + full build of
ollie-gui, krunner_ollie, ollie_kate, kio_ollie.
Implement proper Plan 9/Acme mouse chording in the input area:
- B1 = select (TextArea default)
- B2 = execute selection as prompt
- B3 = plumb selection or context menu
- B1+B2 = cut (chord while selecting)
- B1+B3 = paste (chord while selecting)
Uses MouseArea overlay that detects when B1 is held while B2/B3
is pressed to trigger chord actions. Context menu items now show
the chord shortcuts (Cut B1+B2, Paste B1+B3).
Placeholder text shows the mouse action reference.
- Remove chatPane property from delegate (was shadowing id, causing binding loop)
- Remove contentArea MouseArea from inside Column (invalid anchor)
- Simplify hovered property to use only headerArea
- Edit button now copies to clipboard instead of trying to access chatPane
Add table rendering support to ChatBlockModel:
- isTableLine(): detect pipe-delimited table rows
- renderTable(): convert table lines to HTML <table>
- renderProse(): split prose into text/table segments
- incrementalAppend: handle table state transitions during streaming
Tables render with header row detection (before |---|---| separator)
and proper incremental updates as content streams in.
Add a ▶ button next to the text area for mouse-based submission.
Extract shared doSubmit() on the input RowLayout. Button uses
focusPolicy: Qt.NoFocus so it doesn't steal focus from the TextArea.
Replace MouseArea overlay with TapHandler inside TextArea.
The MouseArea sat on top and intercepted left-button press events,
blocking click-and-drag text selection. TapHandler cooperates with
the TextArea's built-in selection handling.
Reduced from 18 targets to 10:
all, build, go, tools, kde, install, install-data, test, clean, uninstall
Key changes:
- Build to build/ dir first, install only after tests pass
- Merged ninep+core into 'go' (builds olliesrv, ollie-9p, toolsrv)
- Removed build-kf5/install-kf5/kde-kf5 (cmake auto-detects)
- Merged test-core+test-9p into 'test'
- Removed 'client' target (lib9p now built by cmake as custom command)
- Added 'uninstall' target
CMakeLists.txt changes:
- lib9p built via add_custom_command instead of requiring pre-built .so
- add_dependencies ensures lib9p built before KDE targets need it
QIcon::fromTheme("ollie") was finding a stale brain SVG at
/usr/share/icons/ instead of the octopus. Now the app uses the
embedded resource for both window icon and systray, and sets
app.setWindowIcon() explicitly.
Also fixed the .desktop file to use the theme name 'ollie' instead
of a hardcoded absolute path.
killTargetSessionName was set to item.name (displayName, e.g. 'ollie (1)')
instead of item.baseName (the actual 9P path component 'ollie').
This caused readSessionConfig() to read from a non-existent path.
Also fixes the Goal dialog which had the same latent bug.
- Workflow() no longer defaults to 'conductor' when empty
- runWorkflow() returns immediately for '' or 'none'
- 'none' listed first in the workflows file output
- QML dropdowns default to 'none' instead of 'conductor'
- C++ fallback uses 'none' when server unreachable
- Add session-level cfg file (read: name/cwd/remote/workflow/variant/yolo;
write: workflow, variant, cwd)
- Add readSessionConfig() and updateSessionConfig() to C++ client
- NewSessionDialog supports editMode: pre-populates fields from session cfg,
title becomes 'Session Settings', button becomes 'Save'
- Name and Remote fields disabled in edit mode (non-reconfigurable)
- YOLO checkbox hidden in edit mode
- Only changed values are written back on save
- Add 'Settings...' to session context menu in SessionTree
- Wire sessionSettingsRequested signal through to dialog
- loadWorkflows() parses tab-separated 'name<TAB>variants' format
- Add variantsForWorkflow() to return variant list for a workflow
- Add m_workflowVariants map to cache per-workflow variant lists
- createSession() accepts variant parameter, passes to server
- QML: separate Workflow and Variant ComboBoxes
- Variant dropdown repopulates on workflow selection change
- 'default' always present; non-default variants pass through to launcher
- Add OptMem memory section to system_prompt.md with tool-based API
- Fix KRunner plugin icon: resolve via QStandardPaths instead of theme name
- Fix desktop file icon: use absolute path to bypass stale system icon
Session creation now requires cwd= parameter (no fallback to daemon cwd).
Agent cwd is read-only — agents inherit from session, cannot override.
Frontends updated:
- NewSessionDialog: added Directory field with Browse button
- NewAgentDialog: removed Directory field
- createAgent(): removed cwd parameter
- Kate plugin: passes cwd when creating kate session
- Dolphin: removed cwd from agent/new call
Message history sanitization (backend.SanitizeMessages):
- Removes dangling tool calls (assistant with ToolCalls but missing results)
- Removes orphan tool results (tool message without preceding call)
- Applied before sub-agent context inheritance
- Applied before session persistence save
- Applied after compaction (defensive)
Includes unit tests for all sanitization cases.
Server:
- session/idx now outputs: session-id, session-name, paused, connected, remote, cwd
- session/{s}/agent/idx now outputs: session-id, agent-id, agent-name, parent-id, depth, state
- Add error if parent agent not found during sub-agent spawn
GUI:
- refreshSessions() reads session index, then agent/idx per session
- Auto-select first top-level agent (depth 0) instead of first in list
- SessionModel tracks agent expansion separately from session expansion
- Agents with children show expand/collapse arrows and are collapsible
- Add hasChildren role to SessionModel
Tools:
- subagent_spawn now passes parent= argument for proper depth tracking
- Remove hardcoded max_depth=5, use server default of 1
- Makefile installs shell script tools from data/tools/
KRunner:
- Update to parse new session/idx format
- Workflows are executable scripts in data/workflows/
- New 'workflows' 9P file lists available workflows
- Goal file stores text; writing triggers workflow if status allows
- goalstatus file for status read/write, goalwait for blocking
- Session ctl accepts 'run [workflow]' command
- Session now owns CWD; agents inherit via callback
- Conductor workflow: creates agent, primes with instructions, exits
- GUI workflow combo reads from workflows, not agents
- Persistence includes goal, goalstatus, workflow, and session CWD
chat — strips [[[...]]] markers and source fences at the byte level.
Partial lines pass through immediately for true streaming. Terminals
and text editors use this.
chat.raw — full block-structured stream with markers and fences.
GUIs that parse blocks (KDE, web) use this.
The TUI no longer pipes through grep (which was line-buffering and
killing char-by-char streaming since Aug 4).
The stripMarkers state machine processes each chunk from the server:
- Complete lines starting with [[[ and ending with ]]] → dropped
- Fence lines (```...) → dropped (open toggles in-fence state)
- Partial lines (no trailing \n) → always emitted immediately
- Everything else → passed through
Previously, Go binaries and scripts installed to ~/bin while cmake
(KDE) installed to ~/.local/bin. This caused stale binaries in ~/bin
to shadow fresh builds since ~/bin comes first on PATH.
- Change bin variable from ~/bin to ~/.local/bin
- Add migrate-bin target to clean stale ~/bin copies
- Run migrate-bin automatically during install