ollie/doc/IDEAS.md

8.9 KiB

Ideas

Automation & Scripting

Scripting and automation — shell scripts that submit prompts, poll state until idle, then read chat for the result. No SDK, no HTTP client, just file I/O. Works in any language that can write to a file.

Multiplexing sessions — run several agents in parallel, each in their own session directory, and fan work out to them from a shell script. Coordinate by watching their state files.

Hooks and watchers — poll state in a loop to trigger actions when the agent finishes, or use tail -f chat to react to new output. Build lightweight event-driven pipelines with standard shell tools.

Tooling & Integration

Unix tools — pipe chat into grep, awk, sed. Diff two sessions' outputs. Log conversations with cp. Search history across sessions with grep -r.

Editor integration — any editor that can read/write files gets agent access for free. In acme or sam, you write to prompt and read back chat with no plugin required. Fits naturally into the Plan 9 workflow.

Lightweight TUI alternatives — watch cat state as a status bar, tail -f chat in one pane, prompt submission in another. Compose a working interface entirely from standard tools.

Network & Remote Access

Remote access — 9P is a network protocol. Export the namespace over the network and access agent sessions from another machine using the same file interface, with no additional daemon or API layer.

Multi-Agent Workflows

Each session exposes a reply file containing only the assistant text from the most recently completed turn. Writing to prompt clears reply as a side-effect. Together these make agent-to-agent handoffs expressible as plain file operations — no message queue, no orchestration framework, no shared memory.

A shell script moves text between sessions. The agents communicate only through what the script explicitly passes between them, so information flow is fully under your control. Each agent can use a different backend, model, or tool configuration. A human can intervene at any point by writing directly to a session's prompt.

Example: develop → review → test

Three sessions with specialized agent configs run a feedback loop. The reviewer ends its response with LGTM (proceed) or PTAL (revise). The tester ends with Approved (done) or Rejected (revise). A shell script reads the verdict with grep and routes accordingly.

#!/bin/sh
workdir=${1:?usage: $0 <workdir>}
cd ~/mnt/ollie

# Count existing sessions so we can identify the newly created ones by offset.
n=$(ls s/ | wc -l)

echo "new agent=developer workdir=$workdir" > ctl
echo "new agent=reviewer  workdir=$workdir" > ctl
echo "new agent=tester    workdir=$workdir" > ctl

dev=$(ls s/ | sort | sed -n "$((n+1))p")
rev=$(ls s/ | sort | sed -n "$((n+2))p")
tst=$(ls s/ | sort | sed -n "$((n+3))p")

wait_reply() {
    while [ "$(wc -c < s/$1/reply)" -eq 0 ]; do sleep 1; done
}

echo "implement a function that parses a JSON config file" > s/$dev/prompt

while true; do
    wait_reply $dev
    code=$(cat s/$dev/reply)

    # review phase
    printf "Review the following code. End your response with LGTM if it is ready, or PTAL if it needs revision.\n\n%s" "$code" > s/$rev/prompt
    wait_reply $rev

    if ! grep -qi "LGTM" s/$rev/reply; then
        { echo "revise this code based on the feedback below."
          echo "--- code ---";     echo "$code"
          echo "--- feedback ---"; cat s/$rev/reply
        } > s/$dev/prompt
        continue
    fi

    # test phase
    printf "Test the following code. End your response with Approved if all tests pass, or Rejected if they do not.\n\n%s" "$code" > s/$tst/prompt
    wait_reply $tst

    if grep -qi "Approved" s/$tst/reply; then
        echo "done."
        echo "$code"
        break
    fi

    { echo "fix the failures reported below."
      echo "--- code ---";        echo "$code"
      echo "--- test report ---"; cat s/$tst/reply
    } > s/$dev/prompt
done

Each agent only sees what the script explicitly sends it. Each can use a different backend, model, or tool configuration. A human can intervene at any point by writing directly to a session's prompt. The boundary between fully automated and human-in-the-loop is just whether the script pauses to ask.

Sub-Agents

A session can spawn ephemeral child sessions to handle a focused task, then tear them down when done. The parent uses execute_code to write new to ctl, send an initial context and task to the child's prompt, poll until reply is ready, read the result, and write kill <id> to ctl. From the child's perspective it is just a normal session; it has no knowledge of its own ephemerality.

Sub-agents as function calls — the parent primes the child with exactly the context it needs (excerpts from its own chat, relevant beads, local file contents) and receives a single focused reply. Failures are isolated; a child that errors or stalls can be killed and retried without affecting the parent. The pattern composes naturally: a sub-agent can itself spawn sub-agents, building a call tree bounded only by the number of sessions open at once.

spawn_subagents.py reads a JSON spec from stdin, fans out the work concurrently, and returns a JSON array of results — a single execute_tool call from the parent's perspective. Note: this requires adding Python language support to execute_code, execute_tool, and execute_pipe, which is low effort.

#!/usr/bin/env python3
"""spawn_subagents.py — fan out tasks to ephemeral olliesrv sub-agents.

Input  (stdin):  JSON array of {"task": str, "agent": str, "context": str}
Output (stdout): JSON array of {"agent": str, "reply": str}

Install in ~/.local/share/ollie/tools/ to make available via execute_tool.
"""

import json, os, sys, threading, time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path

BASE         = Path.home() / "mnt" / "ollie"
POLL         = 0.5
_spawn_lock  = threading.Lock()


def session_ids():
    return {e.name for e in (BASE / "s").iterdir() if e.is_dir()}

def spawn_session(agent="default", workdir=""):
    with _spawn_lock:
        before = session_ids()
        (BASE / "ctl").write_text(f"new agent={agent} workdir={workdir}\n")
        while True:
            new = session_ids() - before
            if new:
                return new.pop()
            time.sleep(0.1)

def kill_session(sid):
    (BASE / "ctl").write_text(f"kill {sid}\n")

def wait_reply(sid):
    path = BASE / "s" / sid / "reply"
    while True:
        if path.stat().st_size > 0:
            return path.read_text().strip()
        time.sleep(POLL)

def run_subagent(spec):
    agent   = spec.get("agent", "default")
    workdir = spec.get("workdir", "")
    context = spec.get("context", "")
    task    = spec.get("task", "")
    sid     = spawn_session(agent, workdir)
    try:
        prompt = f"{context}\n\n{task}".strip() if context else task
        (BASE / "s" / sid / "prompt").write_text(prompt)
        return {"agent": agent, "reply": wait_reply(sid)}
    finally:
        kill_session(sid)

def main():
    specs   = json.load(sys.stdin)
    results = [None] * len(specs)
    with ThreadPoolExecutor(max_workers=len(specs)) as pool:
        futures = {pool.submit(run_subagent, s): i for i, s in enumerate(specs)}
        for f in as_completed(futures):
            results[futures[f]] = f.result()
    json.dump(results, sys.stdout, indent=2)
    print()

if __name__ == "__main__":
    main()

The parent agent constructs the spec and makes a single tool call:

[
  { "task": "review this code for correctness", "agent": "reviewer", "context": "..." },
  { "task": "check this code for security issues", "agent": "security", "context": "..." }
]

Results come back as:

[
  { "agent": "reviewer", "reply": "..." },
  { "agent": "security", "reply": "..." }
]

Self-Generating Workflows

Since agents have access to execute_code, a session can write and execute a workflow script without any human involvement. Given a task and knowledge of the filesystem layout, an agent can decompose the work, spawn sessions, write the coordination script, and run it — all in a single turn. The README you are reading is essentially its system prompt. Conductor functionality, for free.

Persistent Memory

Task tracking — Agent sessions are ephemeral by nature: context windows fill, processes crash, work gets lost. 9beads addresses this by exposing a structured, version-controlled task database as a 9P filesystem. Any session can read the task list, claim work, track dependencies, and mark completion through ordinary file operations. If a session is interrupted, another can pick up exactly where it left off. Task lists are scoped per-project, keeping them focused and bounded. Combined with olliesrv, agents get both a communication surface and a memory layer, with no special integration required between them.