New files:
- fs9p.go: FS9P struct with /proc-based execution model
- /proc/new (rdwr): blocking tool execution
- /proc/new.bg: background/detached execution
- /proc/<pid>/out, wait, stat, ctl: process management
- /tools, /ctl, /info: tool registry and host info
- Payload format: key=value lines, newline delimited
- exec9p.go: Decoupled tool execution
- ExecuteTool: standalone function for tool execution
- executeSandboxed: sandbox execution without Server deps
- executeBypassDirect: bypass broker without Server deps
- fs9p_test.go: Unit tests for FS9P
Design notes:
- Background execution is the internal model
- Foreground (/proc/new rdwr) blocks until completion
- No per-connection state (cwd/env passed per-call or at attach)
- Session ID scoping via registry, not env propagation
This is the foundation - actual 9P serving and integration pending.
The ctl tool_load command was loading tools into the registry but
not notifying the agent to refresh its preamble. Add RefreshTools()
method and call it after loading a tool via ctl.
Paths starting with / are treated as root-relative, ignoring
the current context prefix. This allows accessing root-level
files/directories even when in a session or agent context.
Example: o myproj/coding ls /bypass # list root bypass/ dir
Simplify context parsing:
- o <cmd> Root level
- o <session> <cmd> Session level
- o <session>/<agent> <cmd> Agent level
The slash disambiguates between session and agent context,
eliminating the need for 'session' and 'agent' keywords.
Examples:
o myproj ls # session level
o myproj/coding prompt # agent level
Refactor o CLI to use explicit context selectors:
- o <cmd> Root level
- o session <sname> <cmd> Session level
- o agent <sname> <aname> <cmd> Agent level
Environment variables $session and $agent can substitute for
positional args for backwards compatibility.
Update acme scripts to use new interface:
o agent $OLLIE_SESSION $OLLIE_AGENT prompt
Add acme-ollie-ensure helper that:
- Creates 'acme' session if it doesn't exist
- Creates agent named by cwd hash (like kate's approach)
- Updates agent cwd if it already exists
All acme commands now call acme-ollie-ensure before sending
prompts, so they work without manual session setup.
Add acme scripts for interacting with ollie:
- AskFile: describe the current file
- Explain: explain selected code
- Document: add documentation comments
- Fix: fix selected code
- Refactor: refactor selected code
- AddTests: write unit tests for selection
- SendVerbatim: send selection as-is
Scripts use the acme 9P filesystem to read selections and pipe
prompts to ollie via 'o prompt'.
Also update justfile to install/uninstall the acme scripts.
- backend: add CWD field to GenerationParams for environment state
- kiro: pass session CWD to kiroCurrentEnvState instead of hardcoded '/'
- agent/runtime: set GenParams.CWD from session cwd in BuildRuntime
- o new: create sessions and agents via 9P filesystem
- o prompt: support piped stdin for non-interactive use
- doc/boot-sequence.md: init chain from main → fs.NewRoot → session.Init
- Event protocol: comprehensive table on Event struct (all roles, semantics)
- RPC server moved from cmd/ollie-remote into toolsrv/rpcserver.go
(binary is now a 93-line thin wrapper)
- SetSessionEnv folded into NewAgent (no post-construction wiring needed)
- AgentCfg → AgentParams (distinguish from AgentConfig JSON schema)
- ToolResult/ToolResultContent shared types in toolsrv/rpcwire.go
- toolsrv/shell.go → toolsrv/exec.go (name matches purpose)
- Resume unified: single path, always through Keeper
- AGENTS.md: fix fs/builder.go → fsedsl/builder.go, handlers_*.go
- fsedsl/Tree: document dual-mode (EDSL lazy vs Manual Mount)
- Truncation stack documented at defaultToolResultMaxBytes
- Integration test rewritten to use srv.ServeRPC directly
The inFence state machine was dropping all lines between fence
delimiters, not just the delimiters themselves. Tool output, assistant
code blocks — everything inside fences was being eaten.
Fix: remove only [[[...]]] marker lines and bare fence delimiter lines
(``` / ```lang). Preserve all content between them.
When a streaming chunk contains only markers/fences, stripMarkers
returns empty bytes. The 9P server interprets a zero-length Rread as
EOF, closing the client's FD.
Fix: loop in streamAgentChatText, advancing the offset past
marker-only chunks until real content arrives.
README: mermaid diagram now shows o CLI as the foundation layer
between text frontends and 9P. KDE/Kate connect directly to 9P.
ellie.el (both versions): add ellie--9p-rdwr wrapper, use ctl rdwr
for systemprompt (no longer a standalone file).
Replace switch statement with rdwrHandler map. Each command is a
function value in agentCtlHandlers — easy to extend, test individually,
and introspect.
/tools — lists loaded tools
/tool_load X — loads a tool by name
System prompt updated to instruct the agent to use ctl for tool
management instead of writing to a tools file.
/models now lists available models for the agent's backend via ctl.
One fewer file in the agent namespace. The root-level models file
(all backends) remains unchanged.
The ctl file is now a Request handler: write a command, read the
response. Commands with no args return current state:
/model → prints current model
/agent → prints current profile
/cwd → prints working directory
/name → prints agent name
/model X → switches model, returns new model
/compact → compacts, returns 'ok'
o ctl now uses 'ollie-9p rdwr' to get the response.
This enables /model (no args) to show the current model from
any frontend.
The slash prefix in prompts now routes directly to the agent's ctl
file. No special-cased commands in the agent — the ctl handler is the
single command surface:
/stop, /compact, /clear, /model X, /inject X, /agent X, /cwd X
New ctl commands:
inject <text> — overwrites any pending inject (replaces /i and /irw)
agent <name> — switch agent profile (moved from commands.go)
Agent methods added: Compact(), Clear(), SwitchProfile() — called by
the ctl handler, testable independently.
HandleCommand is now a thin trampoline: strip /, run 'o ctl <rest>'.
commands.go reduced from 155 to 50 lines.
REPL: / → o ctl, !q/!quit → exit TUI.
The sandbox escape mechanism is a bypass, not privilege elevation.
The old name caused the agent to confuse it with sudo.
- elevate/ → bypass/ (package, types, tests)
- elevate_notify.go → bypass_notify.go
- Namespace: /elevate → /bypass, session/*/elevate → session/*/bypass
- Tool arg: "elevated" → "bypass"
- Env: OLLIE_ELEVATE_SOCKET → OLLIE_BYPASS_SOCKET
- File: elevate-policy.yaml → bypass-policy.yaml
- All docs, prompts, and scripts updated
- Merge readloop into read -l (loop mode)
- Remove chatstream command (TUI uses 'o read chat' directly)
- Rework ctl: o ctl <cmd> [session] [agent] — dispatches to
session ctl for kill/pause/resume, agent ctl for the rest
- Add trap EXIT to kill background statewait loop (fixes zombie)
- Remove -1 compatibility flag from generate
- Remove no-op bracketed paste bind from prompt REPL
- Fix /i help text: 'inject prompt (mid-turn or queued)'
- Update all help text to reflect current commands
- Delete agent/usage_log.go — wrote JSONL to disk but nothing read it
- Remove appendUsageLog call from turn.go
- Remove dead 'offset' from AGENT_FILES tab-completion list
- TUI: grep markers+fences from o log (single read, not streaming)
- fifo.in + fifo.out → single fifo (write enqueues, read dequeues)
- cost + usage + ctxsz → single stats (key=value lines)
- Remove connection (no consumers, heartbeat handles it)
- Remove dead prevPrompt field and write (prompt.prev gone from spec)
- Clean AGENT_FILES: remove state, context, tail (all already absent
from spec, were only in the tab-completion list)
chat — strips [[[...]]] markers and source fences at the byte level.
Partial lines pass through immediately for true streaming. Terminals
and text editors use this.
chat.raw — full block-structured stream with markers and fences.
GUIs that parse blocks (KDE, web) use this.
The TUI no longer pipes through grep (which was line-buffering and
killing char-by-char streaming since Aug 4).
The stripMarkers state machine processes each chunk from the server:
- Complete lines starting with [[[ and ending with ]]] → dropped
- Fence lines (```...) → dropped (open toggles in-fence state)
- Partial lines (no trailing \n) → always emitted immediately
- Everything else → passed through
session.New() had zero callers — all sessions are created via
NewEmpty() + buildAgent(). Remove it along with the Config struct
(20 fields) and the per-session sessionsDir field (always identical
to pkgSessionsDir).
-92 net lines
sync.Cond is the wrong primitive for Go — it requires holding a mutex
during Wait and needs context.AfterFunc hacks to integrate with ctx
cancellation.
Replace with a simple signal channel: notifyChange() closes it and
swaps in a fresh one; WaitChange() selects on the channel and
ctx.Done(). Simpler, idiomatic, no spurious wakeups.
Add JSON tags to backend.GenerationParams and embed it directly in
AgentConfig. The 14 duplicated sampling fields and the field-by-field
copy method disappear. Callers use cfg.GenerationParams directly.
Remove entire TaskState machinery:
- TaskState struct, render(), taskState()/updateTaskState() on History
- inferTaskStateUpdate() — heuristic plan tracking
- Replan gate (replanGate const, roundsWithoutPlan counter)
- Stall detection (stallThreshold, stallRounds, lastAction)
- Plan reinject (planReinjectInterval, periodic re-injection)
- ReadPlanStep plumbing (AgentCfg → Agent → TurnCtx → loop)
None of this worked in practice — the model ignores the nudges,
ReadPlanStep was never wired to a non-nil value, and TaskState
fields were only populated by inferTaskStateUpdate (which depends
on ReadPlanStep being set).
Compaction now uses plain summary text in the cold zone instead
of serialized TaskState JSON.
Also remove:
- Verbosity field (declared, threaded, never read by any backend)
- DisallowTools (config field never wired to session)
- Session.listHandlers, plan, prevPrompt (never read)
- Config.Output, Config.ListHandlers, Config.ReadPlanStep (never set)
-246 net lines
All agent configs use the same pattern: JSON array of file paths with
env var expansion. Remove unused features:
- Shell command execution via resolveStringPrompt (the `!cmd` prefix
path is kept as a trivial fallback but the 30s timeout/stderr
capture/cwd probing is gone)
- Prompt directory search (PromptsDirs, resolvePromptFile)
- Shell metacharacter detection guard
- Literal-text-if-contains-newline heuristic
- IsExec flag on Prompt type (both string and array now mean the same
thing: list of file paths)
207 + 49 → 104 + 38 lines (-114)