subagent: inherit parent context via parent= parameter

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().
This commit is contained in:
Levi Neely 2026-08-14 15:12:16 +02:00
parent c25e2e708c
commit 617bb5586a
4 changed files with 25 additions and 6 deletions

View File

@ -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 {

View File

@ -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
}

View File

@ -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}

View File

@ -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.