Streaming partials were appended to log.raw per chunk, each carrying the
full cumulative content, so one response left dozens of partial lines
persisted. GUI clients parsing the snapshot re-rendered the growing block
once per partial (O(N^2)) and replayed all historical partials on every
reconnect, causing intermittent rendering loops.
Partials are no longer persisted: AppendBlock writes only finalized
blocks to log.raw; SetPartial broadcasts the in-flight block without
storing it. New chat.raw StreamRaw file is the authoritative live JSONL
source (finalized history replay, then live deltas); log.raw is a
finalized-only one-shot snapshot. GUI streams chat.raw, never polls
log.raw.
- Add onClear callback to Agent, wired via support.go
- Publish session.{sid}.agent.{aid}.clear event
- GUI listens for agentCleared signal and refreshes chat view
- Works for both GUI clear button and CLI /clear command
- Clear button sends /clear command
- /clear now resets history, chat log, and block counter
- Completely fresh start without creating a new session
- Button disabled while agent is running
- Add blockCounter to Agent, generating sequential hex IDs
- Include #blockId in all block headers: [[[role:name#id]]]
- GUI parses server-provided IDs from headers
- Falls back to client-generated IDs for legacy blocks without server IDs
- Block IDs are now deterministic based on chat history order
- Add blockId field to ChatBlock (12-char MD5 hash of counter+name)
- Bookmarks now use blockId instead of volatile list index
- Plumb URLs use blockId: ollie://session/agent#abc123def456
- Add indexForBlockId() to look up current index from stable ID
- Action buttons always visible (no hover flicker)
- All ToolButtons now use theme.fontSize-based sizing
- Height, width, margins, and spacing scale with font
- Removed all hardcoded pixel values from bookmark UI
- Add ollie:// URL scheme for plumb messages
Format: ollie://session/agent#block
- Plumber listens on 'ollie' port for navigation messages
- Bookmarks show 📋 button to copy ollie:// link
- Add data/plumbing with rules to route ollie:// URLs
- On plumb message: switch session/agent, scroll to block, raise window
- Add bookmark button (📌) on chat block hover
- Bookmarks section in left panel (SessionTree) with grouped view
- Non-persistent GUI-only state using ListModel
- Click bookmark to scroll to that block in chat
- Bookmark format: sessionName/agentName with block content preview
- Add activeSessionName/activeAgentName C++ getters for display
Canvas-based graph visualization of agents in current session:
- Nodes represent agents, colored by state
- Edges represent peer connections
- Drag nodes to reposition
- Click two nodes to toggle connection
- Double-click to start connecting
- Right-click for context menu
- Pan with middle mouse, zoom with scroll wheel
- Auto-layout arranges nodes in circle
9P API methods: listPeers, addPeer, removePeer, listAgentsInSession
Remove complex DiffWidget detection and context menu injection.
Add 'Review Staged Changes' and 'Review Unstaged Changes' to editor
context menu when in a git repo. Uses git diff directly.
-119 lines of code removed.
Based on Kate source analysis (apps/lib/diff/diffwidget.*):
- DiffWidget uses DiffEditor children (QPlainTextEdit subclass)
- In Raw/Unified mode, editors contain the actual diff text
- Use looksLikeDiff() to identify which editor has valid diff
Simplified from multi-strategy approach to single focused method.
Removed unused QTextEdit and functional includes.
Add minimumWidth: 320 and minimumHeight: 240 to allow the window to
resize properly when tiled by KDE's window manager. Without explicit
minimums, Qt calculates implicit minimums from content that may be
too large for narrow tile slots.
Make the six context-menu action methods (askAboutFile, explainSelection,
submitSelectionPrompt, sendVerbatim, reviewDiff, startSession) public
Q_INVOKABLE so sibling plugins can trigger them by name via
QMetaObject::invokeMethod. The Deft radial menu uses this to offer an 'Ollie'
submenu when this plugin is enabled. No behaviour change for existing callers.
Right-click on files/directories in Kate's project panel now shows
Ollie actions:
For directories:
- Ask about directory (explain purpose/structure)
- Start Session Here
For files:
- Ask about file (explain purpose/design)
- Review file (code review)
- Write tests
Constructs file paths by walking up the tree model and joining with
the project base directory.
Frontend changes to match the backend's multi-request support:
- bypassResolved signal now includes the request ID
- ChatPane tracks pendingBypasses as a map (id -> request)
- onBypassRequested adds to the map; onBypassResolved removes by ID
- currentBypass shows the first pending request for the active agent
- Switching agents updates the banner to show that agent's pending request
- Approve/Deny now remove only the specific resolved request
This allows each agent to have multiple pending bypass requests without
blocking other agents, and the banner correctly shows requests only for
the currently active agent.
- SplitView allows dragging the divider between process list and output
- Show/Hide Output button toggles output panel visibility
- View Output and double-click auto-show the panel if hidden
- Output panel always visible with placeholder text
- View Output clears panel before refreshing
- Added Dismiss button to close window
- Process list has fixed height, output panel fills remaining space
Removed 'Explain this', 'Fix the bug', 'Write tests', 'Summarize',
'Refactor', 'Add comments' — these require context from Kate which
isn't available in the GUI. Replaced with 'Load skills and tools...'
which inserts 'Load all required skills and tools, then ' into the
prompt for user to complete.
B3 (right-click) on a word now plumbs it directly without visually
selecting it first. Double-click word selection includes underscores,
so snake_case selects the whole identifier.
MouseArea's onDoubleClicked doesn't fire when placed on a TextEdit with
selectByMouse:true — the TextEdit's internal mouse handling consumes the
events. TapHandler coexists with TextEdit's mouse handling and reliably
receives double-tap events.
Double-click on a bracket or quote delimiter now selects the content
between the matching pair. Quotes use parity to decide opener/closer
direction. Falls back to word selection when no delimiter is adjacent.
Works in chat blocks (mainTextEdit, textComponent) and prompt textarea.
The MouseArea (chordArea) is a sibling of the ScrollView containing
promptInput, so mouse.x/y are relative to the parent Item, not the
TextArea. Map coordinates via mapToItem before calling positionAt.
Extract selectBracketPair, selectWordAt, and helpers into TextSelection.js
and import it in both ChatBlockDelegate.qml and ChatPane.qml. Prompt
textarea now has the same double-click behavior as chat blocks:
delimiter adjacent to cursor -> select pair content, else -> word select.
Quotes use parity to decide direction and scan document-wide, so
multi-line content between the pair is selected in full.
Quote matching now scans the whole block text and computes opener/closer
parity document-wide, so content between a '' "" `` pair that spans
multiple lines is selected in full. The adjacent quote (left or right of
the cursor) sets the direction; the matching end quote is found by
scanning that way.
The overlay MouseArea swallows the B1 double-click, so returning
mouse.accepted=false did not make the TextEdit perform its native
word select — a non-delimiter double-click selected nothing. Now the
handler selects the word directly via selectWordAt when no bracket or
quote pair is under the cursor, so double-click is deterministic:
delimiter -> pair select, otherwise -> word select.
The Item wrapper added in 1cfa424 set height: mainTextEdit.height while
the TextEdit used anchors.fill: parent, so the TextEdit height depended
on the Item height and vice versa. The resulting binding loop collapsed
the block layout into visual garbage. Size the TextEdit from
width: parent.width and let its content height drive the Item.
selectBracketPair now also handles '' "" `` quote pairs. Since a
quote char opens and closes with the same glyph, a quote to the right
of the cursor scans forward for its match, and a quote to the left
scans backward.
Double-clicking next to a bracket (()[]{}) selects the text between
the matching pair, including nested pairs. Falls through to the
default word-select when no bracket is adjacent.
Also: B3-click on word now highlights it before plumbing.
Handle events in onPressed instead of onClicked so they work
with editable text. Remove propagateComposedEvents since we
want to intercept middle/right clicks completely.
RMB on text without selection now expands to the word at cursor using
acme's isfilec character set: alphanumeric + . - + / : @
This matches acme behavior for clicking on file paths like foo.go:42
Plumb messages now include the current session's cwd as wdir.
Added sessionCwd() helper to ChatPane and ChatBlockDelegate.
Updated plumber.plumb() to accept optional wdir parameter.
B3 (right-click) now only plumbs selection when available — no fallback
context menu. Mouse chording (B1+B2 cut, B1+B3 paste) still works.
Removed contextMenuComponent and promptContextMenu entirely.
- Convert from Dialog to Window (not modal, separate window)
- Title shows current session name
- Syncs to activeSessionId/activeAgentId changes in main window
- Header shows which session is being displayed
- Double-click row to view output
- Sort processes by PID descending (newest first)
- Clear selection when session changes