Commit Graph

363 Commits

Author SHA1 Message Date
Ollie Agent a1dc5b990e Bound toolsrv process completion notifications 2026-08-18 07:07:46 +02:00
Ollie Agent 4b86ad94ff Make bypass resolution atomic 2026-08-18 07:04:11 +02:00
Ollie Agent 1284a83b52 Discard cancelled bypass requests 2026-08-18 07:01:48 +02:00
Ollie Agent f41a580dad Tie event subscription to server context 2026-08-18 07:00:31 +02:00
Ollie Agent 7ec8583e06 Honor cancellation during compaction streams 2026-08-18 06:59:35 +02:00
Ollie Agent c6d9b4aa23 Bound agent prompt queues 2026-08-18 06:58:38 +02:00
Ollie Agent e01d578303 Bound agent chat and summary caches 2026-08-18 06:57:15 +02:00
Ollie Agent 6c1de81cf9 Bound agent result cache 2026-08-18 06:55:25 +02:00
Ollie Agent 062642dfd3 Bound parallel tool dispatch 2026-08-18 06:54:48 +02:00
Ollie Agent 6a8e897fa5 Bound asynchronous session work 2026-08-18 06:53:51 +02:00
Ollie Agent 6a4f83b5b2 Bound completed toolsrv process retention 2026-08-18 06:51:43 +02:00
Ollie Agent b6009f0f7a Reserve toolsrv process slots atomically 2026-08-18 06:50:02 +02:00
Ollie Agent 93e7f510b7 Prevent dismissing running processes 2026-08-18 06:45:23 +02:00
Ollie Agent d31d0661bd Drain remote toolsrv stderr until exit 2026-08-18 06:43:14 +02:00
Ollie Agent e24ed79355 Avoid keeper lock during process dialing 2026-08-18 06:41:34 +02:00
Ollie Agent 3c777667ff Fix paused session process lifecycle 2026-08-17 18:31:37 +02:00
Ollie Agent 701e9bcaad Prevent backend stream goroutine leaks 2026-08-17 18:27:35 +02:00
Ollie Agent 49e43248b0 Fix paused session resume lifecycle 2026-08-17 18:23:00 +02:00
Ollie Agent e1136fdb82 Cancel session context during close 2026-08-17 17:59:19 +02:00
Ollie Agent 16bf2115fa Cancel active agent work during shutdown 2026-08-17 17:57:14 +02:00
Ollie Agent 2599ccb4ac Clear stale agent actions on turn exit 2026-08-17 17:52:51 +02:00
Ollie Agent c5a8f18884 Close 9P fids during connection teardown 2026-08-17 17:51:08 +02:00
Ollie Agent 3ad431957c Fix background tool startup signaling 2026-08-17 17:48:23 +02:00
Ollie Agent 4ac3b2135a Reap toolsrv processes on shutdown 2026-08-17 17:45:29 +02:00
Ollie Agent 2352f2483a Close feed consumer resources 2026-08-17 17:42:30 +02:00
Ollie Agent 6131fad5c5 Cancel timed out 9P reads 2026-08-17 17:41:55 +02:00
Ollie Agent c652c562c3 Fix 9P response writer shutdown 2026-08-17 17:39:56 +02:00
Levi Neely 9147fd790a session: expand ~ and env vars in SetCwd 2026-08-17 14:29:02 +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 74ba5ae7ac remove maxSteps and ResetsCounter entirely
The step budget mechanism is gone. Agents run until they finish,
are interrupted by the user, or (for sub-agents) hit the timeout.

No replacement. The human is the kill switch.
2026-08-17 09:48:58 +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 9789eb56ff show agent reasoning output 2026-08-17 00:08:27 +02:00
Ollie Agent 5d834934de sandbox update 2026-08-16 21:23:15 +02:00
Ollie Agent 45ff8a2864 Remove legacy backend environment configuration 2026-08-16 19:48:52 +02:00
Ollie Agent e51a1d4ff7 Update architecture documentation and remove obsolete frontends 2026-08-16 19:21:26 +02:00
Ollie Agent 57aa906e5f move toolsrv client into olliesrv 2026-08-16 19:07:25 +02:00
Ollie Agent b321eed99f use 9fans client directly 2026-08-16 19:00:48 +02:00
Ollie Agent 16a2b72051 consolidate 9P clients 2026-08-16 18:56:11 +02:00
Ollie Agent 59c4ed23ac merge paths utilities into util 2026-08-16 18:37:03 +02:00
Ollie Agent aca5546ea5 refactor environment utilities and file tools 2026-08-16 18:20:50 +02:00
Ollie Agent 81933e5281 refactor toolsrv into client protocol and metadata packages 2026-08-16 17:22:59 +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 72209239b0 Add parent IDs for sub-agents 2026-08-16 11:44:06 +02:00
Ollie Agent ab9fd60901 Document sub-agent context isolation 2026-08-16 11:39:21 +02:00
Ollie Agent e1ac2df3fb Lower tool output cap and add Markdown parsing 2026-08-16 11:31:31 +02:00
Ollie Agent f82b36ea80 Integrate OptMem memory backend 2026-08-16 11:10:50 +02:00
Ollie Agent 929b94e34d Clarify workspace path placeholders 2026-08-16 10:30:56 +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
Ollie Agent 9330632402 Use compaction model context limit 2026-08-15 14:13:44 +02:00
Ollie Agent 8553bc7283 measure provider prompt cache hits 2026-08-15 14:09:00 +02:00
Ollie Agent 64b76d9bf0 stabilize provider prompt prefix 2026-08-15 13:57:31 +02:00
Ollie Agent 5dbfa3060f include tool semantics in summary cache 2026-08-15 13:46:29 +02:00
Ollie Agent 82ab9dd2e3 version summary cache keys 2026-08-15 13:43:04 +02:00
Ollie Agent 60c4541fa4 normalize summary cache keys 2026-08-15 13:42:11 +02:00
Ollie Agent adf7242774 reduce cold results locally 2026-08-15 13:33:26 +02:00
Ollie Agent 32180bee4d defer cold summaries until needed 2026-08-15 13:27:16 +02:00
Ollie Agent f167168df3 incrementalize cold summaries 2026-08-15 13:13:22 +02:00
Ollie Agent 25d502cfc2 cache cold result summaries 2026-08-15 13:10:15 +02:00
Ollie Agent b6584d35e7 cancellation persistence fix 2026-08-15 12:59:21 +02:00
Ollie Agent f38b130111 explicit summary state on stripCold 2026-08-15 12:55:04 +02:00
Ollie Agent f4cb1ea290 validate cold history summaries 2026-08-15 12:42:28 +02:00
Ollie Agent 329edc3218 account for stripCold in cost 2026-08-15 12:37:13 +02:00
Ollie Agent e4c24e8a5f fix cancellation and configure compaction model 2026-08-15 12:29:55 +02:00
Ollie Agent 93f021f068 feat: add userPrompts config field for active global rules
- Add UserPrompts Prompt field to AgentConfig (same JSON format as prompt).
- Resolve userPrompts via resolvePrompt() in BuildRuntime, store in Runtime.UserPrompt.
- Prepend resolved user prompts to every user message at executeTurn start.
- Document userPrompts in data/agents/README.md.
2026-08-15 11:52:21 +02:00
Ollie Agent f61a93e426 refactor: move stripCold to executeTurn, replace clone with rollback pointer
- Remove per-step stripCold from run(); call once per turn in executeTurn
  after accumulator reset and before preamble injection.
- Replace expensive deep-clone snapshot with pre-turn length truncation.
- Delete unused cloneMessages() helper.
2026-08-15 11:30:19 +02:00
Ollie Agent e0f846bd31 fix: batch preamble injection and stripCold into single LLM calls
- Remove per-step preamble prepending in loop.go; inject once at turn start
  in turn.go instead of re-sending thousands of tokens every tool step.
- Re-inject preamble after overflow retry compaction where it was lost.
- Replace stripCold() N sequential LLM calls with one batched call that
  summarizes all cold-zone tool results via a single JSON response.
2026-08-15 11:16:49 +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 a8d087a080 system prompt: document sub-agent mechanism
Add Sub-Agents section explaining agent/new with prompt= key.
Include examples for single and parallel sub-agent spawning.
Update 9P namespace table to show agent/new as rdwr.
2026-08-14 14:52:53 +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 5dcde264e6 toolsrv: add path-based lock table for cross-agent serialization
All foreground tool calls now acquire a lock based on the tool's
declared scope and file path before execution:

  - scope "read":  no lock (reads never conflict)
  - scope "write": exclusive lock on the file path
  - scope "global": exclusive global lock (serializes with everything)

This ensures writes to the same path serialize regardless of which
agent initiated the call, enabling safe parallel sub-agents within
a session without explicit coordination.

Cross-session serialization (shared toolsrv per host) is left as
future work.
2026-08-14 14:27:21 +02:00
Levi Neely c61fada3e9 extract rebuildAgentRuntime, remove nil guards on things that must not be nil 2026-08-14 08:47:59 +02:00
Levi Neely 785fe0996f fix: rebuild full agent runtime on session resume
Paused sessions restored agents with stub Runtime (nil Backend,
nil Preamble). On resume, now rebuilds the complete runtime:
loads config, builds preamble, creates backend, loads tools.
Added Agent.SetRuntime for this path.
2026-08-14 08:45:54 +02:00
Levi Neely a8fd7a658f fix: nil Preamble on restored paused sessions
The persist path created agents with &Runtime{} (nil Preamble).
If a prompt arrived before resume rebuilt the runtime, Preamble.Set
panicked. Initialize with an empty Preamble.
2026-08-14 08:42:03 +02:00
Levi Neely dc26871bdc proc/list: use fixed-width columns instead of tabs 2026-08-13 22:28:33 +02:00
Levi Neely 0fed1aa124 proc/list: include command in output 2026-08-13 22:24:50 +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 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 48add8f7bb remove proc/ directory from agent namespace (processes live in toolsrv) 2026-08-13 22:01:06 +02:00
Levi Neely 6e5b1aa5f0 system prompt: remove proc/ from agent table (managed via ctl) 2026-08-13 21:59:34 +02:00
Levi Neely 6d4e7e237d system prompt: remove tools file from table (it's a ctl command, not a file) 2026-08-13 21:59:04 +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 a1e78bca80 remove tool_load: use ctl directly
tool_load was a built-in intercept in the agent loop — the only
'tool' that didn't run in toolsrv. Removed entirely:
- Intercept in loop.go (25 lines)
- Script + .meta in data/tools/
- autoLoad references in agent configs

Loading tools is now exclusively via ctl (which already existed):
  echo 'tool_load X' | ollie-9p write .../ctl

System prompt updated to show the ctl pattern.
2026-08-13 21:44:46 +02:00
Levi Neely 324247b6a5 fs: remove dead streamChat and mergeCtx 2026-08-13 21:09:53 +02:00
Levi Neely 4bb08e860b toolsrv: fix shutdown ordering — close conn before draining
On context cancellation, close the connection first to unblock
ReadFcall. Then wait for in-flight handlers, drain the response
channel, and wait for the writer to finish. No writes hit a
closed socket.
2026-08-13 21:08:01 +02:00
Levi Neely d9026499fd toolsrv: suppress write errors during shutdown 2026-08-13 21:04:19 +02:00
Levi Neely bcaac146d2 server: on BlockOnce timeout, fall back to non-blocking Read
Frontends rely on getting the current state on timeout (heartbeat).
When BlockOnce returns empty (5s timeout), the server now calls the
plain Read handler as fallback. Files without a Read handler (like
feed) return nothing — consumer unaffected.
2026-08-13 20:59:05 +02:00
Levi Neely dd0021fd9a fix: restore sessions after 9P listener is ready
ConsumeFeed dials the 9P socket. Moving session restore to after
the listener starts ensures the socket exists when feed consumers
connect.
2026-08-13 20:55:02 +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 5459e7fa2a fix: XDG default fallback in prompt resolver; deduplicate tool headers
- envLookup: provide XDG spec defaults (XDG_CONFIG_HOME, XDG_DATA_HOME,
  XDG_CACHE_HOME) when env vars are unset, so agent profile prompts
  resolve correctly on systems without explicit XDG vars.
- renderTools: skip generated '## name' header when the tool's prompt
  text already starts with a markdown header, eliminating duplicates.
2026-08-13 15:45:35 +02:00
Levi Neely 6a19c135eb prompts: drop tool path from system prompt, use absolute path in copilot example 2026-08-13 12:04:52 +02:00
Levi Neely 83b0b59a0a prompts: remove /home/user path examples that bias tilde expansion
- file_grep.meta: replace /home/user/project with /abs/path
- system_prompt.md: use $XDG_CONFIG_HOME instead of ~
- agent-copilot.md: use relative path in code block example
2026-08-13 12:02:27 +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 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 23f3a6092b refactor: eliminate TurnCtx, make loop functions Agent methods
- Delete TurnCtx struct (was progressively populated with optional nil fields)
- Convert run(), streamResponse(), execToolCalls(), execBatch(), execOne(),
  trackErrors(), retryCountdown() to Agent methods
- Extract autoCompact() and popInject() as Agent methods
- turn.go now installs a turn-scoped output handler wrapper instead of
  building TurnCtx closures
- context.Context is now passed as plain context.Context, not smuggled
  with unrelated state

Add history compaction tests:
- TestBuildCompactedHistory_OrphanedToolWalkback: verifies walk-back logic
  prevents orphaned tool messages at hot zone boundary
- TestBuildCompactedHistory_NoOrphanAtBoundary: verifies tool messages
  always have preceding assistant with tool_calls
2026-08-12 18:06:19 +02:00
Levi Neely 0a4149218a fix: set agent ID on toolsrv connection after resume 2026-08-12 17:10:22 +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 6eb3bf960b refactor: query toolsrv directly for proc list
- ListProcs() now queries toolsrv directly instead of local tracker
- Remove ownership verification from SignalProc/ProcOutput/DismissProc
- Clean up stale procs from local tracker when they disappear from toolsrv
- Document local tracker quirk in TODO.md for future cleanup
2026-08-12 16:06:22 +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 85435447bf fix: clean shutdown without connection errors
- Use read deadline + context check instead of forcibly closing connections
- ReadBypassPending now takes context and exits cleanly on cancellation
- Remove forced conn.Close() loop from Kill() - context cancellation suffices
2026-08-12 12:40:40 +02:00
Levi Neely 9057d36f05 docs: clarify tool usage in system prompt, add cancellation flow doc
- Explicitly tell model not to invoke tool scripts via filesystem path
- Add concrete examples of native tool calls vs shell
- Document context cancellation flow from interrupt to process kill
2026-08-12 12:20:34 +02:00
Levi Neely aedfdec564 fix: set agent ID on tool server after /agent switch
SwitchProfile creates a new tool server connection but wasn't calling
SetAgentID on it, causing 'agent ID required' errors when listing tools.
2026-08-12 11:08:37 +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 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 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 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 62a7d0d13f bypass: remove socket-based execution (dead code)
The old bypass broker used a Unix socket to receive requests and
execute commands locally. This was replaced by the 9P-based flow
where toolsrv executes commands after receiving approval from
olliesrv.

Removed:
- acceptLoop, handleConn, executeAndStream, executeWithSudo
- sendFrame, indexOf, socketDir helpers
- SocketPath and Credential config options
- Frame constants (FrameData, FrameExit)
- net.Conn field on Request

The broker now only handles:
- Policy management (global + per-session)
- Request evaluation (EvaluateRequest)
- Rate limiting
- User notification callbacks

Execution happens in toolsrv, not olliesrv.
2026-08-12 08:51:19 +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
Levi Neely 23b427f97b toolsrv: fix integration test to provide sandbox.yaml 2026-08-12 08:30:09 +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 31d3466b3b agent: add chat log fields and methods
Moved log buffer, plan, mutex, and condvar onto agent.Agent directly.
These were previously on the fs package's AgentLog type. The fs package
will be migrated to use these in the next commit.

New methods: AppendChat, EnsureTrailingNewline, ChatMu, ChatCond,
ChatLog, Plan, SetPlan.
2026-08-11 21:41:12 +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 d06183c8d5 fix file header comment 2026-08-11 21:19:05 +02:00
Ollie Agent b5411f930f toolsrv: rename server.go → p9.go 2026-08-11 21:17:18 +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 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
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 8a5883fcb2 fs: rename HandlerCtx → Hctx, SessionNode.Core → Session 2026-08-11 16:30:05 +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