server: remove statewait file, use event stream instead

The event stream with filtering replaces statewait:
- echo filter | rdwrs event

Removed:
- statewait file from agent namespace
- All non-historical references in docs and code

The state file remains for simple polling reads.
This commit is contained in:
Levi Neely 2026-10-06 12:44:02 +02:00
parent 7e7c29c900
commit 86fbc90b43
11 changed files with 17 additions and 21 deletions

View File

@ -69,7 +69,8 @@ session/
├── cfg configuration ├── cfg configuration
├── ctl control commands ├── ctl control commands
├── state current state ├── state current state
├── statewait block until state changes ├── state current state (idle, calling, thinking, paused)
├── status human-readable status
├── prompt submit a prompt ├── prompt submit a prompt
├── peer/ write to peer/{name} to message a peer agent ├── peer/ write to peer/{name} to message a peer agent
├── chat conversation history ├── chat conversation history
@ -106,7 +107,7 @@ Persistent memory is provided by [OptMem](https://github.com/VictorTaelin/OptMem
## Frontends ## Frontends
Shell scripts, Acme, KDE components, and other clients create sessions, write prompts, and read files such as `chat`, `statewait`, `context`, and `feed`. They do not embed provider, tool, or agent-loop logic. Shell scripts, KDE components, and other clients create sessions, write prompts, and read files such as `chat`, `state`, and `feed`. They subscribe to state changes via the `event` stream. They do not embed provider, tool, or agent-loop logic.
See [`doc/architecture-ide.md`](doc/architecture-ide.md) for integrating Ollie with an editor or IDE. See [`doc/architecture-ide.md`](doc/architecture-ide.md) for integrating Ollie with an editor or IDE.

View File

@ -2,7 +2,7 @@
// //
// The agent state tracks execution progress: idle, running, completed, error. // The agent state tracks execution progress: idle, running, completed, error.
// WaitChange blocks until a field changes, enabling 9P clients to observe // WaitChange blocks until a field changes, enabling 9P clients to observe
// state transitions via statewait. // state transitions via the event stream.
package agent package agent

View File

@ -27,13 +27,14 @@
// //
// prompt - submit prompt (write) // prompt - submit prompt (write)
// chat - conversation history (streaming read) // chat - conversation history (streaming read)
// statewait - block until state changes // state - current agent state
// status - human-readable status
// plan - agent's plan (survives compaction) // plan - agent's plan (survives compaction)
// ctl - agent control commands // ctl - agent control commands
// //
// # Blocking Reads // # Blocking Reads
// //
// Files like statewait and event block until their condition is met. // Files like event block until their condition is met.
// This is the primary coordination mechanism — clients read these files // This is the primary coordination mechanism — clients read these files
// in a loop to react to state changes. // in a loop to react to state changes.
// //

View File

@ -883,13 +883,6 @@ func buildAgentChildren(a *agent.Agent, s *session.Session) []virtfs.FsNodeDecl
return []byte(a.State() + "\n"), nil return []byte(a.State() + "\n"), nil
}), }),
), ),
virtfs.FileNode("statewait", 0444,
virtfs.Doc("Blocks until state changes; returns new value"),
virtfs.BlockOnce(func() ([]byte, string, error) {
st := a.State()
return []byte(st + "\n"), st, nil
}, a.SignalCh),
),
virtfs.FileNode("status", 0444, virtfs.FileNode("status", 0444,
virtfs.Doc("Human-readable status: current activity and elapsed time"), virtfs.Doc("Human-readable status: current activity and elapsed time"),
virtfs.Read(func() ([]byte, error) { virtfs.Read(func() ([]byte, error) {

View File

@ -191,7 +191,7 @@ Use `client_9p` for all 9P operations. Your session ID is `${OLLIE_SESSION_ID}`.
| `fifo` | r/w | Prompt queue. Write: enqueue. Read: dequeue. | | `fifo` | r/w | Prompt queue. Write: enqueue. Read: dequeue. |
| `chat` | read | Conversation (filtered text, streamable) | | `chat` | read | Conversation (filtered text, streamable) |
| `chat.raw` | read | Full conversation with block markers | | `chat.raw` | read | Full conversation with block markers |
| `statewait` | read | Blocks until state changes; returns new value | | `state` | r/w | Current state; subscribe via event stream |
| `cfg` | r/w | Agent config (key=value: backend, model, cwd, temperature, etc.) | | `cfg` | r/w | Agent config (key=value: backend, model, cwd, temperature, etc.) |
| `ctl` | rdwr | Control: stop, compact, clear, inject, agent, model, tools, tool_load, tool_unload, cwd, name, peeradd, peerdel, peers, proc | | `ctl` | rdwr | Control: stop, compact, clear, inject, agent, model, tools, tool_load, tool_unload, cwd, name, peeradd, peerdel, peers, proc |
| `peer/` | dir | Peer agent links. Write to `peer/{name}` to send a message to that peer. | | `peer/` | dir | Peer agent links. Write to `peer/{name}` to send a message to that peer. |

View File

@ -90,7 +90,7 @@ echo "name=coding cwd=$PWD" | ollie-9p write session/myproj/agent/new
| `agent/{aname}/feed` | r/w | Change-detecting input stream. | | `agent/{aname}/feed` | r/w | Change-detecting input stream. |
| `agent/{aname}/chat` | r | Filtered streaming chat output. | | `agent/{aname}/chat` | r | Filtered streaming chat output. |
| `agent/{aname}/chat.raw` | r | Full streaming output with markers. | | `agent/{aname}/chat.raw` | r | Full streaming output with markers. |
| `agent/{aname}/statewait` | r | Blocking state-change read. | | `agent/{aname}/state` | r | Current agent state (idle, calling, thinking, paused). |
| `agent/{aname}/log` | r | Conversation snapshot. | | `agent/{aname}/log` | r | Conversation snapshot. |
| `agent/{aname}/plan` | r/w | Persistent planning scratch space. | | `agent/{aname}/plan` | r/w | Persistent planning scratch space. |
| `agent/{aname}/cfg` | r/w | Agent configuration. | | `agent/{aname}/cfg` | r/w | Agent configuration. |

View File

@ -9,7 +9,8 @@ editor or IDE
├─ file, selection, cursor, project, or diff ├─ file, selection, cursor, project, or diff
└─ 9P client → agent/.../prompt └─ 9P client → agent/.../prompt
├─ chat / chat.raw ├─ chat / chat.raw
├─ statewait ├─ state
├─ status
└─ ctl └─ ctl
``` ```
@ -49,7 +50,7 @@ The editor does not need to display agent output or reload files itself. Use the
| Operation | 9P integration | | Operation | 9P integration |
|---|---| |---|---|
| Explain file or selection | Write a read-only prompt; read `chat`. | | Explain file or selection | Write a read-only prompt; read `chat`. |
| Fix, refactor, document, or add tests | Write a mutating prompt; read `statewait`; reload the buffer. | | Fix, refactor, document, or add tests | Write a mutating prompt; subscribe to state events; reload the buffer on completion. |
| Review a diff | Send the diff as fenced `diff` context. | | Review a diff | Send the diff as fenced `diff` context. |
| Inline completion | Send a bounded prefix/suffix request to root `generate`. | | Inline completion | Send a bounded prefix/suffix request to root `generate`. |
| Agent control | Write a command to `ctl`. | | Agent control | Write a command to `ctl`. |

View File

@ -22,7 +22,7 @@ flowchart TB
The daemon constructs an `fs.Tree`. The tree is the session collection; there is no separate manager abstraction. Session operations are package functions over the tree. A session owns its agents, context, cancellation, and lifecycle. An agent owns history and runs the model/tool loop. The daemon constructs an `fs.Tree`. The tree is the session collection; there is no separate manager abstraction. Session operations are package functions over the tree. A session owns its agents, context, cancellation, and lifecycle. An agent owns history and runs the model/tool loop.
The daemon exposes the 9P namespace directly. Reads and writes are the protocol: writing `session/new` creates a session, writing an agent's `prompt` submits work, reading `chat` observes history, and reading `statewait` blocks until a state transition. The daemon exposes the 9P namespace directly. Reads and writes are the protocol: writing `session/new` creates a session, writing an agent's `prompt` submits work, reading `chat` observes history, and subscribing to the `event` stream monitors state transitions.
### Agent loop ### Agent loop
@ -43,7 +43,7 @@ Ollie deliberately does not build the following into the agent runtime:
- **Native MCP client support.** Use executable or metadata-only tools. An external bridge can invoke an MCP client when required. - **Native MCP client support.** Use executable or metadata-only tools. An external bridge can invoke an MCP client when required.
- **Embedded tool frameworks.** Tools live outside the agent loop; toolsrv owns discovery, loading, execution, sandboxing, and process state. See [`architecture-tools.md`](architecture-tools.md) and [`architecture-toolsrv.md`](architecture-toolsrv.md). - **Embedded tool frameworks.** Tools live outside the agent loop; toolsrv owns discovery, loading, execution, sandboxing, and process state. See [`architecture-tools.md`](architecture-tools.md) and [`architecture-toolsrv.md`](architecture-toolsrv.md).
- **Plan-and-execute workflow engines.** Ollie does not own planners, task graphs, schedulers, retries, compensation, or durable workflow state. A system such as [Beads](https://github.com/steveyegge/beads) can expose those capabilities through a tool. - **Plan-and-execute workflow engines.** Ollie does not own planners, task graphs, schedulers, retries, compensation, or durable workflow state. A system such as [Beads](https://github.com/steveyegge/beads) can expose those capabilities through a tool.
- **External coordination protocols.** Ollie does not expose a workflow engine, actor framework, master coordinator, or public message bus. It does have an internal session event bus for observers. Inter-agent communication uses peer links (`peer/` directory) for topology-controlled messaging within a session. External coordination uses sessions, agents, `prompt`, `chat`, `statewait`, `feed`, and `ctl`. - **External coordination protocols.** Ollie does not expose a workflow engine, actor framework, master coordinator, or public message bus. It does have an internal session event bus for observers. Inter-agent communication uses peer links (`peer/` directory) for topology-controlled messaging within a session. External coordination uses sessions, agents, `prompt`, `chat`, `event`, `feed`, and `ctl`.
- **Separate frontend control planes.** UIs, editor integrations, shell clients, and automation are 9P clients. They do not maintain a parallel session store or frontend-specific API. See [`architecture-9p.md`](architecture-9p.md). - **Separate frontend control planes.** UIs, editor integrations, shell clients, and automation are 9P clients. They do not maintain a parallel session store or frontend-specific API. See [`architecture-9p.md`](architecture-9p.md).
- **Distributed agent state.** Remote execution moves toolsrv and tool execution, not the agent loop, prompts, history, or model calls. See [`architecture-remote.md`](architecture-remote.md). - **Distributed agent state.** Remote execution moves toolsrv and tool execution, not the agent loop, prompts, history, or model calls. See [`architecture-remote.md`](architecture-remote.md).
- **A competing memory store.** [OptMem](https://github.com/VictorTaelin/OptMem) owns persistent memory; Ollie exposes it through tools. - **A competing memory store.** [OptMem](https://github.com/VictorTaelin/OptMem) owns persistent memory; Ollie exposes it through tools.

View File

@ -45,7 +45,7 @@ public:
bool mkdir(const QString &path); bool mkdir(const QString &path);
bool rename(const QString &oldPath, const QString &newName); bool rename(const QString &oldPath, const QString &newName);
// Streaming API - for continuous reads (chat, statewait, etc.) // Streaming API - for continuous reads (chat, event, etc.)
int open(const QString &path); // Returns fid handle, -1 on error int open(const QString &path); // Returns fid handle, -1 on error
int readFid(int fid, char *buf, int bufLen); // Returns bytes read, 0 on EOF, -1 on error int readFid(int fid, char *buf, int bufLen); // Returns bytes read, 0 on EOF, -1 on error
void closeFid(int fid); void closeFid(int fid);

View File

@ -70,7 +70,7 @@ void Lib9pStreamer::Worker::run()
return; return;
} }
if (n == 0) { if (n == 0) {
// EOF — normal for statewait/event after returning data // EOF — normal for event stream after returning data
emit finished(true, QString()); emit finished(true, QString());
return; return;
} }

View File

@ -64,7 +64,7 @@ void Ollie9pClient::setActiveSessionId(const QString &id)
qDebug() << "setActiveSessionId" << id << "prev session" << m_activeSessionId << "prev agent" << m_agentId; qDebug() << "setActiveSessionId" << id << "prev session" << m_activeSessionId << "prev agent" << m_agentId;
// Session selection must be side-effect free for per-agent streams. // Session selection must be side-effect free for per-agent streams.
// Even re-selecting the same session should clear agent selection and stop // Even re-selecting the same session should clear agent selection and stop
// statewait/chat so the UI can stay in a pure "session selected" state. // chat so the UI can stay in a pure "session selected" state.
stopStreams(); stopStreams();
m_activeSessionId = id; m_activeSessionId = id;
m_agentId.clear(); m_agentId.clear();