Commit Graph

54 Commits

Author SHA1 Message Date
Levi Neely b4baddd826 Memory tools: never expose raw memo commands to agents
- Add MEMO_TOOLS=1 env var to memo script; when set, all printed
  instructions reference native tool names instead of memo paths
- Set MEMO_TOOLS=1 in all memory tool .meta wrappers
- Add memory_nap tool for compressions
- Add memory_zoom tool for tree navigation
- Add part/T pagination args to memory_wake
- Update system prompt to use memory_zoom tool call
- Fix inject ctl: submit as user message when agent is idle
2026-08-19 10:13:02 +02:00
Ollie Agent 2ba9ba036c Document native Landlock and persist session yolo 2026-08-18 12:49:25 +02:00
Ollie Agent f41a580dad Tie event subscription to server context 2026-08-18 07:00:31 +02:00
Ollie Agent c6d9b4aa23 Bound agent prompt queues 2026-08-18 06:58:38 +02:00
Ollie Agent 6a8e897fa5 Bound asynchronous session work 2026-08-18 06:53:51 +02:00
Levi Neely 8a1efe0907 session: require cwd, make agent cwd read-only, sanitize message history
Session creation now requires cwd= parameter (no fallback to daemon cwd).
Agent cwd is read-only — agents inherit from session, cannot override.

Frontends updated:
- NewSessionDialog: added Directory field with Browse button
- NewAgentDialog: removed Directory field
- createAgent(): removed cwd parameter
- Kate plugin: passes cwd when creating kate session
- Dolphin: removed cwd from agent/new call

Message history sanitization (backend.SanitizeMessages):
- Removes dangling tool calls (assistant with ToolCalls but missing results)
- Removes orphan tool results (tool message without preceding call)
- Applied before sub-agent context inheritance
- Applied before session persistence save
- Applied after compaction (defensive)

Includes unit tests for all sanitization cases.
2026-08-17 14:15:55 +02:00
Levi Neely 025c351dcb Fix ollie-9p UNAME fallback, goal file permissions
- ollie-9p: Fall back to USER when OLLIE_UNAME not set (remove fatal error)
- goal/goalstatus: Use mode 0666 (world readable/writable) while keeping GID agent
2026-08-17 13:46:44 +02:00
Levi Neely a17dd683df Split session/idx and agent/idx, fix sub-agent tree display
Server:
- session/idx now outputs: session-id, session-name, paused, connected, remote, cwd
- session/{s}/agent/idx now outputs: session-id, agent-id, agent-name, parent-id, depth, state
- Add error if parent agent not found during sub-agent spawn

GUI:
- refreshSessions() reads session index, then agent/idx per session
- Auto-select first top-level agent (depth 0) instead of first in list
- SessionModel tracks agent expansion separately from session expansion
- Agents with children show expand/collapse arrows and are collapsible
- Add hasChildren role to SessionModel

Tools:
- subagent_spawn now passes parent= argument for proper depth tracking
- Remove hardcoded max_depth=5, use server default of 1
- Makefile installs shell script tools from data/tools/

KRunner:
- Update to parse new session/idx format
2026-08-17 13:03:26 +02:00
Levi Neely 958d3d2ddf workflows: script-based workflows with session-level CWD
- Workflows are executable scripts in data/workflows/
- New 'workflows' 9P file lists available workflows
- Goal file stores text; writing triggers workflow if status allows
- goalstatus file for status read/write, goalwait for blocking
- Session ctl accepts 'run [workflow]' command
- Session now owns CWD; agents inherit via callback
- Conductor workflow: creates agent, primes with instructions, exits
- GUI workflow combo reads from workflows, not agents
- Persistence includes goal, goalstatus, workflow, and session CWD
2026-08-17 12:00:10 +02:00
Levi Neely 743e40c2f8 selectable workflow in new session 2026-08-17 10:52:53 +02:00
Levi Neely 2cde59ac3b session-level goals: goal file + goalwait + conductor workflow
Write to session/{s}/goal to set a session-level objective.
A conductor agent is spawned automatically in the background,
decomposes the goal, spawns sub-agents, and reports completion.

- goal file: write sets goal + starts conductor; read returns status
- goalwait file: blocks until goal status changes (BlockOnce)
- Conductor writes status=complete/blocked back to goal when done
- Session.Goal() / SetGoal() / GoalSignal() on Session struct
2026-08-17 10:15:00 +02:00
Levi Neely da0ee4456f sub-agent guardrails: depth, parallelism, timeout
Enforce three limits on sub-agent spawning:
- depth (default 1): sub-agents cannot spawn their own sub-agents
- parallelism (default unlimited): cap concurrent children per parent
- timeout (default 600s): sub-agents are killed after 10 minutes

Top-level agents are never constrained by timeout.

Also: refactored parseAgentNewRequest to return a struct instead of
4 positional values. Added depth/activeChildren fields to Agent.
OLLIE_SUBAGENT_DEPTH env var set on sub-agents.

Deferred: remove maxSteps (replace entirely with timeout).
2026-08-17 09:37:59 +02:00
Levi Neely ecb9406413 agent/new: fork_at= parameter for historical context forking
Sub-agents can now inherit parent context truncated at a specific
user turn: fork_at=5 means 'fork from the state after the 5th
user message.' Enables backtracking without losing unsummarized
context from before a failed approach.

Without fork_at (or fork_at=0), full history is inherited as before.
2026-08-17 09:04:57 +02:00
Ollie Agent e51a1d4ff7 Update architecture documentation and remove obsolete frontends 2026-08-16 19:21:26 +02:00
Ollie Agent 59c4ed23ac merge paths utilities into util 2026-08-16 18:37:03 +02:00
Ollie Agent e972b5666a add metrics queries and agent-scoped plans 2026-08-16 17:00:11 +02:00
Ollie Agent 9409920c61 Propagate parent ID for spawned sub-agents 2026-08-16 11:44:39 +02:00
Ollie Agent 741ac689f8 Persist and expose aggregate usage metrics 2026-08-15 20:12:21 +02:00
Ollie Agent a945445c1f Expose cache metrics in agent stats 2026-08-15 20:04:34 +02:00
Ollie Agent 45746a3164 Expose agent stats through ctl 2026-08-15 19:58:54 +02:00
Levi Neely 617bb5586a subagent: inherit parent context via parent= parameter
Sub-agents now clone the parent agent's conversation history into
their initial context. The tool script passes parent=$OLLIE_UNAME
automatically. Mechanically identical to session restore — uses
RestoreHistoryFromMessages on the parent's Messages().
2026-08-14 15:12:16 +02:00
Levi Neely 250ad4b233 agent/new: sub-agent support via rdwr with prompt=
Writing to session/{s}/agent/new with a prompt= key now blocks
until the agent completes its task, then returns the reply and
destroys the transient agent. Without prompt=, behaves as before
(creates agent, returns ID).

Also:
- Move ParsePayload/UnescapeValue to shared ollie/toolsrv package
- Remove duplicate implementations from cmd/toolsrv/internal/server
- Add session.CreateAgentWithParams for direct AgentParams usage
- Eliminate flattenParams/unescapeValue redundancy in fs package
2026-08-14 14:48:12 +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 d64f6c1d76 proc: rename ListDetachedRaw→ListProcs, use proc/list rdwr (agent-filtered)
ListProcs now does rdwr to toolsrv proc/list with the agent ID.
Returns pre-formatted text. Ctl proc handler uses it directly.
Removed all ListDetachedRaw map-parsing logic.
2026-08-13 22:22:54 +02:00
Levi Neely 9246c44c0e agent: remove proc/detach/tool wrapper methods — inline in ctl handler
Removed from Agent: ListProcs, SignalProc, ProcOutput, DismissProc,
Detach, ListDetached, SignalDetached, GetDetachedOutput,
DismissDetached, BgProc type, DetachedInfo type.

The ctl handler now calls toolsrv client methods directly.
RefreshTools replaced by SetToolsPreamble + inline ListTools call.
renderTools exported as RenderTools.
2026-08-13 22:14:37 +02:00
Levi Neely c767ee10c9 ctl proc top: include STATUS column 2026-08-13 22:09:09 +02:00
Levi Neely 48add8f7bb remove proc/ directory from agent namespace (processes live in toolsrv) 2026-08-13 22:01:06 +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 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 b5cb8c397d feed: BlockOnce with base init, ConsumeFeed via lib9p client
- BlockOnce handler initializes base to current hash on fresh open,
  then blocks until hash changes. Same pattern as statewait.
- ConsumeFeed is a plain function: dials 9P, reads feed in a loop
  (open → block → data → close → repeat), submits to agent.
- Context cancellation closes the 9P client, unblocking Read().
- Called as go ConsumeFeed(ctx, ag) from AddAgent and session resume.
2026-08-13 20:26:08 +02:00
Levi Neely e2695d6f50 feed: export Feed field, remove wrapper methods, use direct access everywhere 2026-08-13 20:09:28 +02:00
Levi Neely 1537e35685 feed: inline handlers, remove FeedBlockingRead wrapper 2026-08-13 20:06:32 +02:00
Levi Neely 721b49ffa2 feed: use BlockOnce for read side, dedup via base comparison
Feed is now read-write (0666):
- Write: store data, signal via notifyChange
- Read (BlockOnce): block until content hash differs from base

Dedup lives in the read layer (same pattern as statewait). The
internal consumer goroutine and external 9P clients both call
FeedBlockingRead — same codepath, same dedup logic.

Consumer started from AddAgent (if ctx available) and session resume.
2026-08-13 20:05:46 +02:00
Levi Neely dfacdfed94 agent: add feed file — change-detecting streaming input gate
Feed is a write-only file in the agent namespace. Writes are
deduplicated against the previous value; the internal consumer
(blocking on WaitChange/WatchFeed) only wakes when genuinely new
data arrives.

Wiring is external and source-agnostic:
  # human → observer (poll git):
  while :; do git diff HEAD; sleep 5; done | ollie-9p write .../feed
  # agent → observer (stream chat):
  ollie-9p read .../coder/chat | ollie-9p write .../observer/feed

Uses the agent's existing signalCh/notifyChange plumbing — no new
channel infrastructure. Consumer goroutine spawned at agent creation
(AddAgent) and session resume, tied to session context.
2026-08-13 20:02:06 +02:00
Levi Neely 1ac50d97e1 agent: drain FIFO after interrupted/failed turns
Fix holes where queued prompts were orphaned:
- After turn loop exits (interrupt, toolsrv unavailable), drain remaining FIFO items
- On panic recovery, pop next FIFO item and re-submit in a new goroutine
- fifo file write triggers Submit when agent is idle
2026-08-13 11:59:11 +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 edd5ade471 refactor: simplify bypass - toolsrv is the broker, olliesrv is just a client
- Remove olliesrv's bypass broker machinery (pendingCh, EvaluateRequest, etc)
- Session bypass loop now just reads from toolsrv's bypass/pending and notifies
- Notification handler writes directly to toolsrv's bypass/resolve (fire and forget)
- Remove bypass/ directory from olliesrv's 9P namespace
- Remove session/*/bypass file (policy can be added back to toolsrv later if needed)

The flow is now:
1. toolsrv blocks tool execution, exposes request via bypass/pending
2. olliesrv reads from toolsrv, shows D-Bus notification
3. User clicks approve/deny, olliesrv writes to toolsrv's bypass/resolve
4. toolsrv unblocks and executes (or denies)
2026-08-12 11:01:29 +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 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 3a68b0be53 Fix bypass broker and stop command
- Wire BypassBroker into fs.Config before creating root tree
- Move defer bypassBroker.Close() outside block scope (was closing immediately)
- Rewrite /bypass/ FS nodes to match toolsrv's expected interface:
  - bypass/pending: blocking read returning JSON {id,cmd,cwd,env}
  - bypass/resolve: write JSON {id,approved,error}
- Add NextPending() method to broker with channel-based blocking
- Add JSON tags to Request struct for proper serialization
- Start bypass loop for restored sessions (was only for new sessions)
- Add context cancellation support to CallTool (closes fid on cancel)
2026-08-12 09:16:59 +02:00
Levi Neely 3ee4f16653 fs: remove proc/ proxy, add tool_unload to agent ctl
- Remove session/{sname}/agent/{aname}/proc/ tree (redundant proxy to toolsrv)
- Add tool_unload command to agent ctl file
2026-08-12 08:25:22 +02:00
Ollie Agent 47b460b2da fs: eliminate AgentLog, SessionNode, RootState
State now lives where it belongs:
- Chat log/plan/condvar → agent.Agent (new chatlog.go)
- Models cache → session.Session
- Event handler → agent.Agent.initChatHandler() (self-wiring)
- Message replay → agent.Agent.ReplayMessages()

The fs package is now a pure presentation layer: spec.go (namespace),
support.go (shared utils), newroot.go (entry point), cache.go (ModelCache).
No wrapper types, no parallel registries.

Deleted: AgentLog, SessionNode, RootState, lifecycle.go, agent_log.go,
session_node.go. Removed replay_test.go (needs rewrite against agent pkg).
2026-08-11 21:50:18 +02:00
Ollie Agent 7591b2369b fs: remove connected file, agent tools file; fix session/idx hang
- Removed session/connected (used blocking Ping() on potentially stale conn)
- Removed per-agent tools file (tool management now via toolsrv ctl)
- Fixed session/idx: replaced blocking IsConnected() with non-blocking
  toolsConn != nil check, preserving the field for GUI compatibility
- Removed dead IsConnected() method from session.Session
2026-08-11 21:06:42 +02:00
Ollie Agent 066659b8b6 olliesrv/fs: inline all handlers into spec.go
The namespace spec is now fully self-contained. Every handler is a
closure right where it's declared — no jumping between files.

Deleted: handlers_root.go, handlers_session.go, handlers_agent.go, ctl.go
Added: support.go (shared utilities: mergeCtx, streamChat, stripMarkers,
dispatch, wireAgentEvents, help memoization)

spec.go grew to ~920 lines but is now the single source of truth for
the entire 9P namespace. Know where to look.
2026-08-11 20:41:08 +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 d71ff80993 olliesrv/fs: embed *session.Session in SessionNode, kill delegation
SessionNode now embeds *session.Session instead of wrapping it.
All delegation methods (ID(), Name(), Ctx(), Pause(), etc.) are removed.
Handlers access session fields/methods directly through promotion.

Moved pause/resume event publishing into session.Session itself since
the session package already owns PublishEvent.

Eliminated all s.Session.X stutter from handler code.
2026-08-11 17:19:10 +02:00
Ollie Agent 1474eeaaed olliesrv/fs: eliminate Hctx adapter struct
Handlers now accept their actual dependencies directly:
- Root handlers: ModelCache, Broker, context.Context, etc.
- Session handlers: *SessionNode, removeFn, renameFn
- Agent handlers: *agent.Agent, *AgentLog, *SessionNode

Ctl dispatch tables are now per-instance closures built at binding
time rather than package-level vars with generic Hctx.

Also inlined the intermediate internalBinding type — adapt functions
now build fsedsl.Binding directly from raw data.

Removed: Hctx struct, rdwrHandler type, rdwrDispatch func,
bypassBindings helper, processBindings helper, internalBinding type.
2026-08-11 17:15:01 +02:00
Ollie Agent 95eb402c68 olliesrv/fs: migrate to closure-capture fsedsl API
Converted the entire 9P namespace spec from progressive Hctx population
to closure capture. Handlers are now closures that capture their
dependencies (session, agent, etc.) at binding time.

Hctx remains as a transitional adapter — handlers still receive it,
but it's constructed per-call from captured state rather than threaded
through the tree. Will be removed in a follow-up once handlers are
converted to use captured state directly.
2026-08-11 17:04:40 +02:00