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
Changed from 0600 (owner only) to 0660 (owner + group):
- ctl: frontends need to send slash commands
- plan: frontends need to read/write plan
- fifo: frontends need to submit prompts via queue
The agent group includes frontends, so group permissions enable
frontend interaction while still maintaining ownership-based isolation.
Per-agent file ownership with Unix permission enforcement:
- Agent directories owned by agent ID (UID), group 'agent' (GID)
- Private files (plan, ctl, fifo): mode 0600 - owner only
- Group-readable (chat, log): mode 0440 - owner + agent group
- World-readable (state, id): mode 0444 - observable by all
- Prompt: mode 0220 - CLI and owner can write
virtfs: fix UID/GID inheritance through nested paths
- Added findChildWithInheritance() to accumulate inherited UID/GID
- Stat now correctly shows agent ID as owner for nested files
server: admin bypass for server owner
- serverAdmin variable captures the Unix user running olliesrv
- Admin bypass includes empty uname, 'admin', or server owner
Documentation updates:
- fs/doc.go: 'The Namespace IS the Security Model'
- registry/doc.go: capability-based tool access
- peer.go: capability-based peer access
- lessons-learned.md: 'Model compliance is not a security boundary'
- architecture-9p.md: per-agent file ownership section
Security evaluation:
- Added experiments/security-eval/ with NERV attack corpus adaptation
- Test scripts for Landlock sandbox validation
- RESULTS.md documenting 0% ASR on hostile operations
This implements the NERV thesis: 'An agent can only access resources
explicitly bound into its namespace.' Enforcement is structural via
file permissions, not behavioral via model compliance.
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.
o sess approve [id] - approve pending, optionally verify id
o sess deny [id] - deny pending, optionally verify id
Shows request id in output: [42] approving: cmd
Errors if specified id doesn't match pending request.
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.
Works at session or agent context (bypass is session-level).
Reads pending request from session/{s}/bypass, extracts ID,
writes '{id} approve' or '{id} deny' to resolve.
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
Bypass approval now flows through:
1. GUI - via event stream and banner
2. CLI - via agent loop (to be implemented)
Removed:
- bypass_notify.go (D-Bus notification)
- BypassNotifyFunc type and all references
- godbus/dbus dependency
The bypass event is still published via SetBypassPending.
If fidOK was false for the event file, we fell through to the
default stream handling path which doesn't work for events.
Now we return 'bad fid' error instead of falling through.
The pubsub library had issues:
- Published to literal '*' topic (nonsensical)
- Used TrySend which drops events
- Complex hierarchical wildcard publishing
New implementation:
- Simple eventHub with map of subscribers
- PublishEvent fans out to all subscribers (blocking send)
- SubscribeEvents returns channel, cleaned up on ctx cancel
- SubscribeEventsFiltered filters client-side with MatchTopic
- Removed simonfxr/pubsub dependency
The subscription was being cancelled after the first read completed
because we used the per-request context. Now using the connection
context so the subscription lives until the fid is clunked or
connection closed.
The pubsub library's SubscribeChan uses non-blocking TrySend which
drops events when the channel is full. Switch to Subscribe with a
blocking callback to ensure no events are lost.
Also increased channel buffers from 64 to 256.
Plain reads (without writing a filter first) now get a per-fid
subscription with '*' filter, identical to 'echo * | rdwrs event'.
This fixes event loss — the old EventStream path overwrote events
when multiple arrived before the reader consumed them.
Backend adapters (~8.3K) are mostly mechanical API glue.
Core runtime is ~25K lines (22K Go core + 3K compiled tools).
KDE adds another ~15.7K. The interesting logic is concentrated.
The feed file was documented but never used by any frontend
or script. The observer agent pattern was never adopted.
- Remove feed.go, FeedWrite, ConsumeFeed
- Remove WatchFeed constant
- Remove feed file from 9P namespace
- Remove ConsumeFeed goroutine spawns from session
- Update docs (architecture-9p, architecture-ide, architecture, usage)
The event stream now covers real-time observation patterns better.
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.
- AGENTS.md: simplified architecture description
- architecture-9p.md: examples use event stream with filtering
- usage.md: o tui and wiring examples use rdwrs event
- state: immediate read of current agent state
- statewait: blocks until state changes
Clearer semantics than overloading statewait with both behaviors.
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.