update readme

This commit is contained in:
Levi Neely 2026-04-10 03:16:45 +02:00
parent c00129a488
commit 1158efffe3
1 changed files with 1 additions and 100 deletions

101
README.md
View File

@ -202,106 +202,7 @@ A session can spawn ephemeral child sessions to handle a focused task, then tear
**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.
The coordination logic can be written in any language that reads and writes files. The following Go example fans out N sub-agents concurrently, waits for all replies, and collects the results:
```go
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
var base = filepath.Join(os.Getenv("HOME"), "mnt", "ollie")
func main() {
tasks := []string{
"summarize the key ideas in functional programming",
"summarize the key ideas in object-oriented programming",
"summarize the key ideas in logic programming",
}
for i, reply := range runSubAgents(tasks) {
fmt.Printf("=== agent %d ===\n%s\n", i+1, reply)
}
}
func runSubAgents(tasks []string) []string {
type result struct {
idx int
reply string
}
results := make([]string, len(tasks))
ch := make(chan result, len(tasks))
var wg sync.WaitGroup
for i, task := range tasks {
wg.Add(1)
go func(idx int, task string) {
defer wg.Done()
sid := spawnSession()
defer killSession(sid)
os.WriteFile(filepath.Join(base, sid, "prompt"), []byte(task), 0644)
ch <- result{idx, waitReply(filepath.Join(base, sid, "reply"))}
}(i, task)
}
go func() { wg.Wait(); close(ch) }()
for r := range ch {
results[r.idx] = r.reply
}
return results
}
// spawnSession is serialised to avoid a race between snapshot and detection.
var spawnMu sync.Mutex
func spawnSession() string {
spawnMu.Lock()
defer spawnMu.Unlock()
before := sessionIDs()
os.WriteFile(filepath.Join(base, "ctl"), []byte("new\n"), 0644)
for {
for id := range sessionIDs() {
if !before[id] {
return id
}
}
time.Sleep(100 * time.Millisecond)
}
}
func sessionIDs() map[string]bool {
entries, _ := os.ReadDir(base)
ids := make(map[string]bool)
for _, e := range entries {
if e.IsDir() {
ids[e.Name()] = true
}
}
return ids
}
func waitReply(path string) string {
for {
if info, err := os.Stat(path); err == nil && info.Size() > 0 {
data, _ := os.ReadFile(path)
return strings.TrimSpace(string(data))
}
time.Sleep(500 * time.Millisecond)
}
}
func killSession(sid string) {
os.WriteFile(filepath.Join(base, "ctl"), []byte("kill "+sid+"\n"), 0644)
}
```
Each goroutine spawns, primes, waits, and cleans up independently. The only serialised step is session creation, to avoid a race between snapshotting existing sessions and detecting the new one.
The same pattern works as a reusable agent tool. `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.
`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.
```python
#!/usr/bin/env python3