Commit Graph

388 Commits

Author SHA1 Message Date
Ollie Agent cd26595cb7 Fix stream chunk truncation; style TUI initial state from state file
server.go: the stream read handler truncated each chunk to the client's
requested Count and advanced waitBase, silently dropping bytes beyond
the buffer (e.g. a chat block larger than 64KB). It now caches each
stream chunk per-fid (readCache + streamBase) and pages within it by
absolute offset, fetching the next chunk only once the current one is
fully drained, preserving 9P offset semantics.

o tui: read the state file for the initial status bar and apply the same
color styling and status text the event loop uses, so the bar is correct
immediately on attach; the event stream drives subsequent updates.
2026-10-10 16:37:00 +02:00
Ollie Agent 89fbb7438a Stream assistant text incrementally to log/chat; fix stream lost-wakeup
Two fixes for the rendered text views (chat stream, log snapshot):

1. Lost wakeup: blocking stream readers captured the signal channel
   after their data check, so a write landing in that window signaled a
   now-stale channel and the reader blocked until the next event (the
   TUI's last message appeared only after the next prompt). Stream and
   BlockOnce now capture the signal channel before reading; chat.go
   swaps the channel under chatMu via signalChatLocked and RawLogStream
   captures it while holding the lock.

2. No streaming in TUI: textLog only received finalized blocks, so the
   chat text stream showed nothing until a block completed. SetPartial
   now appends the assistant partial's new suffix to textLog
   incrementally (tracking textPartialLen); AppendBlock appends only the
   trailing separator on finalization to avoid duplicating the body.
2026-10-10 16:25:14 +02:00
Ollie Agent 6211a6cdee Surface tool calls, output, and bypass status in log/chat views
The filtered text views (log, chat) hid call and tool blocks entirely
and had no visibility into bypass requests, which were only published as
events. RenderBlock now renders tool calls (name + args), tool output,
and bypass request/resolution notices; reasoning and context stay
hidden. SetBypassPending/ResolveBypass append bypass and bypass-resolved
blocks to the requesting agent so clients without an event stream (TUI,
o script) see pending approvals and outcomes.
2026-10-10 16:05:05 +02:00
Ollie Agent a4972cd456 Separate chat persistence from live delivery via chat.raw
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.
2026-10-10 15:57:48 +02:00
Ollie Agent e7c9a34c3b Make log.raw a one-shot read, not a stream
A Stream handler on log.raw blocks the GUI's synchronous startup read
forever, so the window never renders. Live updates come from the
separate chat and event streams.
2026-10-10 15:39:54 +02:00
Levi Neely 8ce50452a1 Fix log.raw streaming: add Stream handler back 2026-10-09 19:37:58 +02:00
Levi Neely 6cf283044e docs: add chat file to namespace documentation
- AGENTS.md: four views (log.raw, log, chat, block)
- system_prompt.md: add chat to agent file table
- architecture-9p.md: add chat to namespace table
- evolution.md: correct Phase 40 (chat not deleted)
2026-10-09 19:16:20 +02:00
Levi Neely f2f33e7826 Add chat file back for TUI text streaming 2026-10-09 19:09:20 +02:00
Levi Neely 57c40d541e Migrate chat log to JSONL format
Server changes:
- format/block.go: Block struct with JSONL marshaling, RenderBlock for text
- Deleted format/event.go, format.go, format_test.go (old delimiter format)
- agent/chat.go: Dual logs (rawLog JSONL + textLog rendered text)
- agent/chatlog.go: Streaming via flushPartial/closeBlock, skip internal events
- fs/spec.go: New files log.raw (JSONL), log (text), block (ID lookup)
- Removed chat/chat.raw/chat.search from namespace

GUI changes:
- chatblockmodel: JSONL parsing via QJsonDocument, removed regex state machine
- ollie9pclient: Read from log.raw for replay, removed old streaming code
- Added Context type to block enum, filter context/state/usage from display

Docs updated: AGENTS.md, system_prompt.md, architecture-*.md, scripts/o
2026-10-09 18:52:40 +02:00
Levi Neely c186c87525 Use sha256-based deterministic block IDs
- NextBlockID now uses sha256(sessionID:agentID:counter)[:8] for deterministic,
  collision-resistant block IDs across sessions and agents
- Remove blockCounter reset on Clear() to maintain ID stability
2026-10-09 17:01:22 +02:00
Levi Neely 5f67d44e27 Add block ID tests and document chat log files
- Add unit tests for NextBlockID and ChatSearchByID in chat_test.go
- Add unit tests for ParseBlockHeader, BlockDelim, FormatEvent in format_test.go
- Add e2e test script using o CLI to verify block ID search
- Document chat log files in AGENTS.md: chat/chat.raw are blocking streams,
  use log for non-blocking reads, chat.search for block lookup
2026-10-09 17:00:37 +02:00
Levi Neely 582f7d1b91 event: emit clear event when agent is cleared
- 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
2026-10-09 16:31:30 +02:00
Levi Neely 1c863ce9ec gui: add Clear button next to Compact
- 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
2026-10-09 16:28:28 +02:00
Levi Neely c8f42b2400 9p: add chat.search file for block lookup by ID
Write a block ID to chat.search, read the block content back.
Returns error if block not found.
2026-10-09 16:15:30 +02:00
Levi Neely 46aa901763 server-side block IDs for stable bookmark/plumb targets
- 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
2026-10-09 16:12:26 +02:00
Levi Neely f822bf6c32 prompts: enforce terse output more aggressively
- Changed 'brief' to 'TERSE' with explicit one-sentence-per-action rule
- Added 'instant failure' framing for banned patterns
- Banned multi-sentence summaries explicitly
- Removed hedging allowances
- Simplified task completion to 'ONE sentence. Stop.'

Also: kate plugin diff widget detection with debug logging
2026-10-09 11:53:58 +02:00
Levi Neely dda056e3b1 prompts: explicit bans on verbose patterns
Added concrete examples of banned output patterns:
- Preambles (Great question!, I'd be happy to help!)
- Narration (Let me..., I'll now...)
- Hedging (It seems like, It appears that)
- Over-explaining and self-congratulation
- Summaries that restate what was just done

Added positive guidance: start with the answer, state task
completion in one sentence, end when information is delivered.

Models respond better to explicit prohibitions with examples than
to general instructions to 'be brief'.
2026-10-09 10:41:14 +02:00
Levi Neely 5c0f463e4d session: add unit tests for concurrent bypass requests
Tests cover:
- Multiple concurrent pending requests from different agents
- BypassPendingByID lookup
- ResolveBypass removes only the resolved request
- ResolveBypass fails for non-existent ID
- Empty session has no pending requests
2026-10-09 10:17:37 +02:00
Levi Neely 412b8513e6 session: support multiple concurrent bypass requests
Previously a session tracked only one pending bypass request. If
agent A1 had a pending bypass, agent A2's bypass request would block
waiting to be read from toolsrv's channel, effectively blocking all
agents in the session.

Now bypassPending is a map keyed by request ID:
- SetBypassPending adds to the map instead of overwriting
- BypassPending returns all pending requests (slice)
- BypassPendingByID returns a specific request
- ResolveBypass removes from the map by ID
- 9P bypass file returns JSON array of all pending

The bypass loop reads requests continuously without waiting for
resolution, so multiple agents can have concurrent pending requests.
2026-10-09 10:16:17 +02:00
Levi Neely 94e5d76b5b agent: clarify in system-proc-background tag that output requires idle
The tag text now explicitly says output is injected when the agent is
IDLE (not making tool calls), not just 'when available'.
2026-10-08 18:48:58 +02:00
Levi Neely 35fefefdc7 prompts: clarify background output requires agent idle state
Background process output is only injected when the agent is idle.
If the agent keeps making tool calls, the output won't arrive until
the turn ends.
2026-10-08 18:47:57 +02:00
Levi Neely 414dae05f8 dispatch: include no-wait instruction in system-proc-background
Every background process emission now includes:
'Do NOT sleep, poll, or wait. Continue with other work —
output will be injected automatically when available.'
2026-10-08 11:21:40 +02:00
Levi Neely 8da78f4deb prompts: never sleep/poll/wait for background processes
Add explicit instruction that models must not use sleep, loops,
or any blocking mechanism to wait for background process output.
The system injects updates automatically.
2026-10-08 11:13:54 +02:00
Levi Neely c2df632744 agent/new: parse cwd parameter into CwdOverride
The cwd= parameter in agent/new payloads was not being parsed,
so scripts like ollie-session-here could not set agent cwd on
creation. Now parsed and passed to AgentParams.CwdOverride.
2026-10-07 15:32:53 +02:00
Levi Neely f7a52088db per-agent cwd override with session-level inheritance
- 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
2026-10-07 15:14:04 +02:00
Levi Neely 1c02b50add proc events, bypass shortcuts, Kate session fix
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
2026-10-07 14:22:44 +02:00
Levi Neely b9026bb48b fix: allow group access to ctl, plan, fifo for frontend interaction
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.
2026-10-07 11:01:01 +02:00
Levi Neely d65c06f27b implement namespace-bounded capability model
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.
2026-10-06 17:41:27 +02:00
Levi Neely b25e5e2fc6 bypass: emit resolved event for cross-client coordination
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.
2026-10-06 14:54:55 +02:00
Levi Neely 6dc9ffe3b1 gui: show bypass banner per-agent, not globally
- 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
2026-10-06 14:32:30 +02:00
Levi Neely 59d07d3b4a fix bypass event payload truncation at newlines
The shell command in bypass requests contains embedded newlines.
Escape them as \n in the event payload, unescape in GUI.
2026-10-06 14:28:10 +02:00
Levi Neely 7b870443bb remove desktop notification for bypass requests
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.
2026-10-06 14:03:07 +02:00
Levi Neely a4378c1be4 server: also match /event path in read handler 2026-10-06 13:51:25 +02:00
Levi Neely 5077dfea8e server: fix event file fallthrough when fid lookup fails
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.
2026-10-06 13:50:58 +02:00
Levi Neely d7d9e9654a replace pubsub library with simple fan-out event hub
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
2026-10-06 13:43:57 +02:00
Levi Neely 63e7ed5c65 server: clarify event subscription uses 9P client connection context 2026-10-06 13:37:56 +02:00
Levi Neely 43d3051a78 server: fix event subscription context - use connection ctx not request ctx
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.
2026-10-06 13:36:26 +02:00
Levi Neely 79ab165ffd server: fix event dropping - use blocking callback instead of TrySend
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.
2026-10-06 13:32:56 +02:00
Levi Neely cd94cdf259 server: event file plain read now uses per-fid subscription
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.
2026-10-06 13:27:02 +02:00
Levi Neely a90ca6b57c remove unused feed file and observer agent support
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.
2026-10-06 12:55:39 +02:00
Levi Neely 88062d0ed0 server: remove redundant bypasswait file
Bypass requests are delivered via the event stream.
The bypasswait file was unused.
2026-10-06 12:47:57 +02:00
Levi Neely 3b6c78d08d docs: state file is read-only 2026-10-06 12:44:37 +02:00
Levi Neely 86fbc90b43 server: remove statewait file, use event stream instead
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.
2026-10-06 12:44:02 +02:00
Levi Neely 772d77940c server: implement filtered event subscriptions
Event file now supports streaming rdwr pattern:
- Read: streams all events (unchanged)
- Write filter, then read: streams only matching events

Filter syntax:
- * matches single segment
- > matches rest of topic
- Examples: session.*.agent.*.state, session.abc.>

Usage: echo filter | rdwrs event

Per-fid subscription state is cleaned up on clunk.
2026-10-06 12:34:14 +02:00
Levi Neely 9a66944859 ollie-9p: add rdwrs command for streaming rdwr
rdwrs writes stdin then streams reads (does not wait for EOF).
Use for event subscription: echo filter | rdwrs event

Also adds prototype in experiments/streamrdwr demonstrating:
- Write topic filter, stream matching events
- Glob-style filtering: * (single segment), > (rest)
2026-10-06 12:31:06 +02:00
Levi Neely 6ea2bc052e server: add non-blocking state file separate from statewait
- state: immediate read of current agent state
- statewait: blocks until state changes

Clearer semantics than overloading statewait with both behaviors.
2026-10-06 12:17:19 +02:00
Levi Neely f86c8d3cdc docs: update all references from eventwait to event 2026-10-06 12:08:35 +02:00
Levi Neely 25d7fbfc5b server: rename eventwait to event 2026-10-06 12:02:37 +02:00
Levi Neely adb08fc51d server: fix eventwait to use Stream instead of BlockOnce
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.
2026-10-06 12:01:26 +02:00
Levi Neely f2f1f80e4e gui: integrate bypass requests into chat UI
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.
2026-10-06 11:44:15 +02:00