Commit Graph

18 Commits

Author SHA1 Message Date
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
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 0c30ec71d2 server: reduce statewait timeout from 5s to 500ms
When reading statewait with the same state value, the server now waits
500ms instead of 5 seconds before returning. This makes state polling
much more responsive.
2026-10-06 11:32:31 +02:00
Levi Neely 4a037c0bbe refactor: remove dead code from embedding/virtfs/skills (-108 lines)
embedding/embedding.go:
- Remove EmbedBatch() (never called in production, test updated to use Embed)
- Remove padID field (written but never read)

embedding/index.go:
- Remove Index.mu mutex (Index is immutable after construction)

skills/skills.go:
- Remove Index.All() (never called)
- Remove Index.Reload() (never called)

virtfs/decl.go:
- Remove RemoveNode() NodeOption (never used)
- Remove RenameNode() NodeOption (never used)
- Remove Alias() NodeOption (aliases set directly on struct)

virtfs/tree.go:
- Remove Tree.Data field (never used)
- Remove Tree.Mount() (never called in production)
- Remove Tree.Child() (never called in production)
- Remove Tree.Children() (never called in production)
- Replace indexOf() with strings.IndexByte

Tests updated to directly manipulate internal children map where needed.
2026-08-21 17:20:13 +02:00
Levi Neely 2a34c4719a fix(virtfs): support Each() with non-template directory names
Each('peer', ...) creates a named directory whose children come from
Bindings(). Previously, listDir and findChild only checked Bindings
for template names like {foo}. Now they also handle directories that
have Bindings but no Children.

This fixes the peer/ directory in olliesrv which was listing empty
even though peers were configured via peeradd.

Added test for the non-template Each pattern.
2026-08-21 13:11:46 +02:00
Ollie Agent 562e1ef780 Synchronize virtfs rdwr state 2026-08-18 06:48:34 +02:00
Ollie Agent c0ce185051 Close files opened during virtfs stat 2026-08-18 06:46:36 +02:00
Ollie Agent aca5546ea5 refactor environment utilities and file tools 2026-08-16 18:20:50 +02:00
Levi Neely ab4668cb3f virtfs: rename Request to Rdwr
Read, Write, and Rdwr are the three atomic 9P operations:
- Read: non-blocking read
- Write: non-blocking write (fire-and-forget)
- Rdwr: atomic write-then-read (blocking, produces result)

BlockOnce and Stream are special cases of Read.
Rdwr is its own primitive — not a variant of either.
2026-08-14 14:42:19 +02:00
Levi Neely dcd119c853 virtfs: refactor Stream to accept (readFn, signalFn)
Same pattern as BlockOnce: framework handles the blocking loop.
readFn takes a base string, returns (data, nextBase, error).
signal fires when new data may be available.

Chat stream handlers now use Stream(a.ChatRead, a.ChatSignal).
StreamRaw retained for custom handlers.

streamChat() in support.go is now dead code (replaced by ChatRead).
2026-08-13 20:50:27 +02:00
Levi Neely 794673a5d0 virtfs: refactor BlockOnce to accept (readFn, signalFn)
BlockOnce now takes a value-reader and a signal source. The framework
handles the blocking loop: read → compare hash to base → if different
return → else wait on signal or timeout.

On timeout (ctx.Done), returns empty (not error) so clients re-open
cleanly.

BlockOnceRaw retained for queue-style handlers (bypass/pending,
proc/wait) that manage their own blocking.

EventValue adapter wraps a <-chan Event into BlockOnce-compatible
(readFn, signalFn) pair via a thin goroutine.
2026-08-13 20:44:50 +02:00
Levi Neely d59f49ee54 feat: context cancellation for tool calls
Wire context cancellation through the toolsrv 9P server so that
canceling a CallTool context properly terminates the running tool.

Changes:
- virtfs: Add Close() to File interface with CloseFn for Request handlers
- toolsrv/p9.go: Handle requests concurrently to allow Tclunk during
  blocking Twrite; call entry.Close() in handleClunk
- toolsrv/internal/server/proc.go: Check ctx.Done() while waiting for
  tool completion in NewProc
- Integration tests for context cancellation chain

The cancellation chain: client cancels ctx → fid.Close() → Tclunk →
handleClunk calls entry.Close() → closeFn cancels reqCtx → NewProc
sees ctx.Done() and returns ctx.Err()
2026-08-12 10:00:44 +02:00
Levi Neely 5dc7cdc2ea Make Request() always context-aware
Remove RequestCtx variant - all Request handlers now take context.
This is required for proper cancellation of blocking operations
like tool execution when the client disconnects or stop is called.
2026-08-12 09:30:39 +02:00
Levi Neely 861cb47d13 Add RequestCtx for context-aware request handlers
- Add RequestCtx field to FsNodeDecl for handlers that need context
- Add RequestCtx() helper function in virtfs/decl.go
- Update builder.go to handle RequestCtx in validation and Open
- Use RequestCtx in toolsrv proc/new and proc/new.bg handlers

This allows the context to be cancelled when the 9P fid is closed,
enabling proper stop/interrupt of running tool calls.
2026-08-12 09:26:02 +02:00
Ollie Agent a40e1dbed0 virtfs: inline tree options, remove newTree/with* indirection
The with* functional options and newTree constructor were private and
only used in two places (builder and tests). Replaced with direct
field assignment on &Tree{} literals. Less abstraction, same behavior.
2026-08-11 20:28:18 +02:00
Ollie Agent 5a09e603cd virtfs: eliminate Binding type
Each() now returns []FsNodeDecl directly. The Binding type was a
redundant subset of FsNodeDecl with a slightly different Remove
signature. Dynamic entries are now expressed uniformly — Remove,
Rename, Children, Aliases all live on FsNodeDecl like everything else.

Also added: Alias() and RenameNode() options, DirNode accepts
[]FsNodeDecl for passing pre-built child slices.
2026-08-11 20:24:29 +02:00
Ollie Agent 3a875da9f6 virtfs: rewrite README for closure-capture model
Drop all references to generic context type parameter, Applier,
and type/function aliasing patterns. Document the actual API:
plain closures, pre-bound Children in Bindings, no generics.
2026-08-11 17:22:39 +02:00
Ollie Agent ee7426d628 rename fsedsl → virtfs
The package has outgrown its original 'eDSL' framing. It's a virtual
filesystem library. Rename to match.
2026-08-11 17:21:44 +02:00