From e176e7ef7febf850a3e26cb5498935fb0ea45652 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Tue, 14 Apr 2026 14:27:47 +0200 Subject: [PATCH] =?UTF-8?q?9p:=20add=20s/idx=20=E2=80=94=20live=20session?= =?UTF-8?q?=20index=20for=20agent=20cooperation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Synthetic read-only file at ollie/s/idx. Each line is tab-separated: {id} {state} {cwd} {backend} {model} Generated on demand from live session state so it's always current. Enables agents to discover peers by cwd or name without polling individual session files. Co-Authored-By: Claude Sonnet 4.6 --- internal/p9/server.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/p9/server.go b/internal/p9/server.go index df39e2e..b7cd158 100644 --- a/internal/p9/server.go +++ b/internal/p9/server.go @@ -11,6 +11,7 @@ // pl/ (dir) plans // s/ (dir) session directory // new (r/w) read: KV template; write: create session +// idx (read) live index: name state cwd backend model (tab-separated) // {session-id}/ rm -r to kill session; mv to rename // ctl (write) session control: stop, kill, rn, compact, clear // prompt (write) submit a prompt to the agent @@ -267,6 +268,8 @@ func (s *Server) pathType(path string) string { return "dir" case len(parts) == 2 && parts[0] == "s" && parts[1] == "new": return "file" + case len(parts) == 2 && parts[0] == "s" && parts[1] == "idx": + return "file" case len(parts) == 1 && parts[0] == "a": return "dir" case len(parts) == 1 && parts[0] == "p": @@ -526,6 +529,23 @@ func (s *Server) read(cs *connState, fc *plan9.Fcall) *plan9.Fcall { return s.readSlice(fc, content) } + // s/idx is a live index of all sessions: name state cwd backend model. + if path == "/s/idx" { + var sb strings.Builder + s.mu.RLock() + for id, sess := range s.sessions { + sess.mu.RLock() + state := sess.core.State() + cwd := sess.core.CWD() + be := sess.core.BackendName() + model := sess.core.ModelName() + sess.mu.RUnlock() + fmt.Fprintf(&sb, "%s\t%s\t%s\t%s\t%s\n", id, state, cwd, be, model) + } + s.mu.RUnlock() + return s.readSlice(fc, []byte(sb.String())) + } + // backends is a static list of ollie-provided backends. if path == "/backends" { content := []byte(strings.Join(backend.Backends(), "\n") + "\n") @@ -1420,6 +1440,7 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte { } } else if path == "/s" { dirs = append(dirs, makeDir("new", "/s/new", false, 0666)) + dirs = append(dirs, makeDir("idx", "/s/idx", false, 0444)) s.mu.RLock() for id := range s.sessions { dirs = append(dirs, makeDir(id, "/s/"+id, true, plan9.DMDIR|0555)) @@ -1517,7 +1538,7 @@ func (s *Server) makeStat(path string) plan9.Dir { case "backend", "agent", "model", "cwd": mode = 0666 default: - if path == "/backends" || path == "/help" { + if path == "/backends" || path == "/help" || path == "/s/idx" { mode = 0444 } else if path == "/s/new" { mode = 0666