Commit Graph

30 Commits

Author SHA1 Message Date
Levi Neely de33f954e2 toolsrv: proc/list now rdwr with agent filtering
Write agent ID to filter, or empty for all. Returns pid\tstate\ttool.
Added ListProcsForAgent to State.
2026-08-13 22:08:15 +02:00
Levi Neely fd4a3afa4f move tool catalog to toolsrv: add /all file, tools_all ctl command
- Remove /tools from olliesrv root (toolsrv owns all tool state)
- Add /all file to toolsrv namespace (lists all available tools on disk)
- Add ListAllTools() to toolsrv client library
- Add tools_all ctl command to agent (reads from toolsrv/all)
- Update system prompt with tools_all usage
2026-08-13 21:58:08 +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 9b98a958cc fix: treat meta cmd as shell command string, prepend tools dir to PATH
- ResolveTool now returns cmd field as-is instead of trying to resolve
  it as a binary path. This allows meta-only tools to use full shell
  command strings with env vars, pipes, and subshells.

- Prepend ~/.config/ollie/tools to PATH when executing tools so cmd
  fields can reference other tools by name.
2026-08-12 18:44:16 +02:00
Levi Neely bc7f51a9dc refactor: push proc completion from toolsrv to agent prompt
- toolsrv pushes <system-proc-complete> to agent prompt when bg proc exits
- Remove bgTracker and CollectInterrupts from olliesrv
- Add Cmd, AgentID, SessionID fields to Proc
- Add OnProcExit callback to State
- Add command field to Proc.Stat() output
2026-08-12 16:39:43 +02:00
Levi Neely 1d5e9c4f8d fix: background proc output capture and status display
- Detach background proc context from request context so processes survive
  after the 9P request returns (fixes premature SIGTERM)
- Add /proc/list file to toolsrv showing all procs with state
- Show status (running/exited (N)) in agent proc list
- Keep exited procs in tracker until explicitly dismissed
- Skip 'list' entry in ListDetachedRaw
2026-08-12 14:10:08 +02:00
Levi Neely 46bd0dd999 fix: toolsrv 9P server now handles BlockOnce/Stream reads correctly
- Added BlockingReadMode() and StreamMode() handling to toolsrv's handleRead()
- Made NextPending() context-aware in both toolsrv and olliesrv bypass packages
- This fixes the bypass approval flow via 9P - reads now properly block until
  a request is available and respect context cancellation
2026-08-12 10:49:08 +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 31681cb4e7 Add context cancellation to bypass.Submit
bypass.Submit now takes a context and returns early when cancelled.
This allows the stop command to properly cancel pending bypass requests
that haven't been approved or denied yet.

The full context chain is now:
1. stop command -> Interrupt -> cancel actCtx
2. actCtx cancellation -> closes CallTool fid
3. toolsrv proc/new sees closed fid -> propagates ctx cancellation
4. ExecuteTool -> executeBypassDirect -> bypass.Submit
5. bypass.Submit returns ctx.Err() when context is cancelled
2026-08-12 09:32:21 +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
Levi Neely 9bb3cd76b8 Remove sudo mechanism from bypass system
The sudo credential broker was never functional and added complexity
without value. This removes:

- Sudo field from bypass Request structs (broker, client, toolsrv)
- Sudo parameter from EvaluateRequest interface and implementations
- Sudo/ResetsCounter fields from MetaFile and Variant structs
- sudo: true from system_logs.meta variants
- All sudo documentation from writing-tools.md, tool-registry.md,
  core.md, evolution.md, and misc.md

Bypass remains fully functional for sandbox escapes. Tools that need
elevated privileges should handle that internally or be run manually.
2026-08-12 08:58:41 +02:00
Levi Neely 8bb5c098cc bypass: route approval through 9P instead of Unix socket
This refactors the bypass (sandbox escape) mechanism to work with remote
toolsrv deployments. Previously, bypass used a Unix socket which only
works when toolsrv runs locally. Now:

1. toolsrv exposes bypass/{pending,resolve} 9P files
   - pending: blocking read returns next bypass request as JSON
   - resolve: write JSON {id, approved, error} to complete request

2. olliesrv reads bypass/pending in a loop per session
   - Evaluates requests through the existing bypass broker
   - Policy check, rate limiting, user notification all stay in olliesrv
   - Writes approval/denial back to bypass/resolve

3. When approved, toolsrv executes the command directly (no sandbox)
   - Execution happens on toolsrv's host (local or remote)
   - Output streams back through the normal tool call path

This enables bypass to work when toolsrv is remote:
- User sees the approval notification locally
- Command executes on the remote host outside its sandbox

Architecture:
  toolsrv (remote)          olliesrv (local)
  ┌─────────────────┐      ┌──────────────────┐
  │ sandboxed cmd   │      │ bypass broker    │
  │      ↓          │      │  - policy        │
  │ bypass.Submit() │──────│  - notification  │
  │      ↓          │ 9P   │  - rate limit    │
  │ wait for result │←─────│  - user approval │
  │      ↓          │      └──────────────────┘
  │ execute direct  │
  └─────────────────┘
2026-08-12 08:48:32 +02:00
Levi Neely 1814f82928 toolsrv: include session ID in bypass broker requests
The bypass broker requires a 'session' field in the JSON request
to identify which session is making the bypass request. The toolsrv
was sending requests without this field, causing 'denied (no session
identity)' errors.

Fix: Include OLLIE_SESSION_ID from environment in the bypass request.
2026-08-12 08:34:52 +02:00
Ollie Agent b01dbdfba8 sandbox: single config, no named profiles
Removed the named sandbox concept. One config file: sandbox.yaml
(installed to ~/.config/ollie/sandbox.yaml). Removed sandbox-remote.yaml,
moved restricted.yaml to doc/ as a sample. Removed the 'sandbox' arg
from tool dispatch.
2026-08-11 21:23:23 +02:00
Ollie Agent 62fe1dab83 toolsrv: rename internal/fs → internal/server
The package defines the Server type and its namespace — 'fs' was a
leftover name from when it only held the filesystem spec. Now it's
the server definition. File renamed spec.go → server.go to match.
2026-08-11 21:15:03 +02:00
Ollie Agent 8ce8390421 toolsrv/fs: move Server into spec.go, delete state.go
Server is the top-level type that Spec() takes as input. It belongs
with the namespace declaration, not in a separate file.
2026-08-11 21:12:51 +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 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
Ollie Agent 53b4618561 toolsrv: migrate to closure-capture fsedsl API
Spec() now takes *Server and all handlers are closures that capture it.
Removed Ctx type, type aliases, and separate handler functions.
The tree structure and behavior are declared together in one place.
2026-08-11 16:56:34 +02:00
Ollie Agent de5456e4aa toolsrv: fix per-agent tool registry to actually work
The registry was keyed by agent ID but read it from a shared env map
(st.env["OLLIE_UNAME"]) that all agents overwrote — making it
effectively per-session with a race condition.

Fix: pass agent ID explicitly through the protocol at every call site.

- ctl protocol: 'load <agentID> <tool>', 'unload <agentID> <tool>'
- tools file: rdwr (Request) — write agent ID, read filtered list
- proc/new payload: 'agent=<id>' field required
- Client: SetAgentID() stores identity, included in all operations
- Empty agent ID is a hard error everywhere
- Setting OLLIE_UNAME via env ctl is blocked (prevents reintroduction)
2026-08-11 16:26:12 +02:00
Ollie Agent 659f89fcbf toolsrv: per-agent tool registry
Tools are now scoped per-agent within a session. The registry uses
OLLIE_UNAME (agent identity) to partition loaded tools. Each agent
sees only its own tools — load/unload/list/lookup all key on the
agent ID read from the toolsrv environment.
2026-08-11 11:49:19 +02:00
Ollie Agent 83f6211369 toolsrv: simplify registry to flat list, remove session scoping
One toolsrv per session means the session-scoped map was unnecessary
indirection. Registry now tracks a flat map of loaded tools with no
session ID parameter. This eliminates the class of bugs where sessID
could be empty or mismatched between Load and Loaded calls.
2026-08-11 11:30:47 +02:00
Ollie Agent cfc429f5f4 fix: expand ~ in toolsrv CWD to prevent fork/exec ENOENT
Shell doesn't expand ~ in programmatically-passed arguments, so
toolsrv received literal ~/src/ollie as --cwd. Go's os/exec fails
with ENOENT on chdir before exec, breaking all tool execution.

Expand ~ at two boundaries:
- main.go: immediately after flag parsing
- proc.go SetCWD(): for runtime cwd changes via 9P
2026-08-11 11:24:10 +02:00
Ollie Agent 391465be77 doc+fix: background procs force timeout=0, update docs
- Background procs get timeout=0 (no deadline) set by toolsrv at
  the execution layer — not injected as an arg from olliesrv.
  Foreground default remains 30s. User can still override via args.
- Timeout logic simplified: caller sets default, ExecuteTool honors it.
- System prompt: clarify background has no timeout, uses 'stop' not 'kill'
- architecture.md: document lifecycle (connection-based ownership)
- evolution.md: update interrupt section with streaming, lifecycle, id= format
- README.md: add parallel execution to capabilities table
2026-08-11 10:18:30 +02:00
Ollie Agent 97c208fa39 toolsrv: kill all procs on shutdown, add Pdeathsig
- KillAll(): sends SIGKILL to all running proc groups on clean shutdown
- Pdeathsig: SIGKILL ensures child procs die if toolsrv crashes
- Shell tool: use subshell + set +e for streaming without exit propagation
- Fix integration test to match new error format

This is the connection-based ownership model: toolsrv death = proc death.
The session owns the toolsrv process, so session death cascades to all procs.
2026-08-11 10:02:53 +02:00
Ollie Agent 0d1eeaa46b fix: background process lifecycle, streaming output, and connection deadlocks
- Timeout: timeout=0 means no deadline (was defaulting to 30s)
- Signal: send to process group (-pgid) not just process; SIGTERM no longer
  cancels context (only SIGKILL does); cmd.Cancel sends SIGTERM with 5s WaitDelay
- Streaming: background procs stream output in real-time via procWriter;
  shell tool no longer buffers all output into a bash variable
- Proc tree: olliesrv exposes proc/{id}/out, proc/{id}/ctl, proc/{id}/status
  as proper 9P directory (was broken flat file)
- Connection: proc handlers dial fresh toolsrv conn per request via
  Session.DialToolServer() to avoid deadlocking the agent's blocked conn
- Stat format: key=value (exited=true, exit_code=N, id=N) matching client parser
- GC: procs auto-removed 10min after LastRead (exited procs only)
- Rename: PID -> ID throughout (synthetic, not OS PID)
- Ctl commands: term (SIGTERM), kill (SIGKILL), signal <n>, dismiss
- System prompt: correct ollie-9p commands for proc management
2026-08-11 09:44:08 +02:00
Ollie Agent aa270525a5 agent: background process interrupts — auto-inject output at safe points
When a tool call includes "background": true, it executes via
proc/new.bg and returns immediately with a PID in a
<system-proc-background> tag. The agent tracks active background
processes and injects their output as <system-proc-interrupt> blocks
alongside subsequent tool results.

The model sees background updates without polling. It can react to
build failures, log events, etc. naturally. Kill via PID when done.

Implementation:
- toolsrv client: CallToolBackground (uses proc/new.bg as rdwr)
- toolsrv: proc/new.bg upgraded from write-only to request-response
- agent: bgTracker collects last 20 lines of output per proc
- agent loop: injects interrupts after execToolCalls, before h.update
- system prompt: documents the background mechanism and rules
2026-08-11 08:23:00 +02:00
Ollie Agent df74c3fa60 toolsrv: restructure into internal packages, fix process lifecycle
- Move toolsrv logic into internal/{fs,exec,registry} packages to match
  olliesrv's structure. server.go (9P protocol) stays at top level.

- Deduplicate parseKV/parsePayload: remove dead code from old spec9p.go,
  single implementation in internal/fs/parse.go.

- Add structured logging via log/slog (replaces fmt.Fprintf to stderr).

- Standardize qid path hashing to fnv.New64a (matches olliesrv).

- Fix toolsrv process leaks:
  - Add Pdeathsig to local spawn so toolsrv dies when olliesrv exits.
  - Add idle timeout (30s with no connections after first client seen).
  - ProcessKeeper.Dial() kills old process before respawning.
2026-08-11 07:21:14 +02:00
Levi Neely 1fc051e3bf refactor: move olliesrv and toolsrv packages to cmd/*/internal/
Move server-only packages under their respective cmd directories:

olliesrv:
- agent/ -> cmd/olliesrv/internal/agent/
- backend/ -> cmd/olliesrv/internal/backend/
- bypass/ -> cmd/olliesrv/internal/bypass/
- fs/ -> cmd/olliesrv/internal/fs/
- prompts/ -> cmd/olliesrv/internal/prompts/
- session/ -> cmd/olliesrv/internal/session/

toolsrv:
- Server-only code (exec9p, fs9p, server9p, spec9p, auth9p) -> cmd/toolsrv/
- sandbox/ -> cmd/toolsrv/internal/sandbox/
- Keep shared client code (client9p, spawn, registry, meta) in toolsrv/
- Add toolsrv/types.go for shared types (ToolResult, ToolResultContent)

This enforces package boundaries - code in cmd/*/internal/ cannot be
imported by external packages, while shared code remains importable.
2026-08-10 20:16:44 +02:00