From 617bb5586aebd4b121e2a5e51e49cfd2895f1c50 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Fri, 14 Aug 2026 15:12:16 +0200 Subject: [PATCH] subagent: inherit parent context via parent= parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sub-agents now clone the parent agent's conversation history into their initial context. The tool script passes parent=$OLLIE_UNAME automatically. Mechanically identical to session restore — uses RestoreHistoryFromMessages on the parent's Messages(). --- cmd/olliesrv/internal/fs/spec.go | 9 ++++++++- cmd/olliesrv/internal/fs/support.go | 12 +++++++----- data/tools/subagent_spawn | 1 + doc/evolution.md | 9 +++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/cmd/olliesrv/internal/fs/spec.go b/cmd/olliesrv/internal/fs/spec.go index 3617ec0..a6d598d 100644 --- a/cmd/olliesrv/internal/fs/spec.go +++ b/cmd/olliesrv/internal/fs/spec.go @@ -290,7 +290,14 @@ func buildSessionChildren( virtfs.FileNode("new", 0666, virtfs.Doc("Create agent. With prompt=, runs as sub-agent: blocks until done, returns reply."), virtfs.Rdwr(func(ctx context.Context, data []byte) ([]byte, error) { - params, prompt := parseAgentNewRequest(data) + params, prompt, parentID := parseAgentNewRequest(data) + + // Sub-agent inherits parent's context. + if prompt != "" && parentID != "" { + if parent := s.FindAgent(parentID); parent != nil { + params.History = agent.RestoreHistoryFromMessages(parent.Messages()) + } + } ag, err := session.CreateAgentWithParams(s.Name(), params) if err != nil { diff --git a/cmd/olliesrv/internal/fs/support.go b/cmd/olliesrv/internal/fs/support.go index 777e3df..e89fc90 100644 --- a/cmd/olliesrv/internal/fs/support.go +++ b/cmd/olliesrv/internal/fs/support.go @@ -84,11 +84,11 @@ var ( // - Space-separated key=value (legacy format from GUI) // // A bare word (no =) is treated as the profile name (legacy positional arg). -// Returns AgentParams and the prompt (empty if not a sub-agent request). -func parseAgentNewRequest(data []byte) (session.AgentParams, string) { +// Returns AgentParams, the prompt (empty if not a sub-agent request), and the parent agent ID. +func parseAgentNewRequest(data []byte) (session.AgentParams, string, string) { s := strings.TrimSpace(string(data)) if s == "" { - return session.AgentParams{Profile: "default"}, "" + return session.AgentParams{Profile: "default"}, "", "" } var tokens []string @@ -99,7 +99,7 @@ func parseAgentNewRequest(data []byte) (session.AgentParams, string) { } p := session.AgentParams{Profile: "default"} - var prompt string + var prompt, parent string for _, tok := range tokens { tok = strings.TrimSpace(tok) if tok == "" { @@ -122,10 +122,12 @@ func parseAgentNewRequest(data []byte) (session.AgentParams, string) { p.Profile = v case "prompt": prompt = v + case "parent": + parent = v } } else if p.Profile == "default" { p.Profile = tok } } - return p, prompt + return p, prompt, parent } diff --git a/data/tools/subagent_spawn b/data/tools/subagent_spawn index 592ca3e..883c5a3 100755 --- a/data/tools/subagent_spawn +++ b/data/tools/subagent_spawn @@ -32,6 +32,7 @@ escaped_prompt=$(printf '%s' "$prompt_text" | sed 's/\\/\\\\/g; s/$/\\n/' | tr - # Build the payload. payload="cwd=${cwd} profile=${profile} +parent=${OLLIE_UNAME} prompt=${escaped_prompt}" [ -n "$name" ] && payload="${payload} diff --git a/doc/evolution.md b/doc/evolution.md index 81f5ffc..cfd1ee2 100644 --- a/doc/evolution.md +++ b/doc/evolution.md @@ -1542,6 +1542,15 @@ landed as pure wiring because: No new abstractions. No new processes. No new protocols. Just connecting existing pieces with 42 lines of glue. +### Context inheritance + +Sub-agents inherit the parent's conversation history. When `parent=` is +specified in the payload, the handler clones the parent agent's messages +into the sub-agent's initial context via `RestoreHistoryFromMessages`. +This is mechanically identical to session restore — same function, same +data path. The sub-agent knows everything the parent knows without +rediscovering it. + ### Future work - **Shared toolsrv per host** — currently each session spawns its own toolsrv.