From 2bbaecb531dcc3007fd6b9cb8a8a8b18ff376d74 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Sat, 2 May 2026 13:50:52 +0200 Subject: [PATCH] JIT subagent generation via subagent_generate | xargs subagent_spawn - rename agent_generate to subagent_generate - subagent_generate reads JSON from stdin, copies default.json as base, replaces prompt with SYSTEM_PROMPT + identity text - update tools-subagent.md to enforce composition pattern - remove unused agent configs and prompts (devops, qa, reviewer, sysarchitect, writer) - update agents/README.md - update core submodule (remove stale-read pruning) --- agents/README.md | 2 +- agents/devops.json | 28 ------- agents/qa.json | 27 ------- agents/reviewer.json | 27 ------- agents/sysarchitect.json | 27 ------- agents/writer.json | 27 ------- core | 2 +- prompts/agent-devops.md | 23 ------ prompts/agent-qa.md | 27 ------- prompts/agent-reviewer.md | 25 ------ prompts/agent-sysarchitect.md | 22 ------ prompts/agent-writer.md | 23 ------ prompts/tools-subagent.md | 144 ++++++++++------------------------ tools/agent_generate | 52 ------------ tools/subagent_generate | 43 ++++++++++ 15 files changed, 87 insertions(+), 412 deletions(-) delete mode 100644 agents/devops.json delete mode 100644 agents/qa.json delete mode 100644 agents/reviewer.json delete mode 100644 agents/sysarchitect.json delete mode 100644 agents/writer.json delete mode 100644 prompts/agent-devops.md delete mode 100644 prompts/agent-qa.md delete mode 100644 prompts/agent-reviewer.md delete mode 100644 prompts/agent-sysarchitect.md delete mode 100644 prompts/agent-writer.md delete mode 100755 tools/agent_generate create mode 100755 tools/subagent_generate diff --git a/agents/README.md b/agents/README.md index ec97cd8..d8e659a 100644 --- a/agents/README.md +++ b/agents/README.md @@ -4,7 +4,7 @@ Each `.json` file defines an agent configuration. Agent configs are installed to ## default -Base agent with no specializations. Provides the standard ollie toolset and prompt assembly. Use this as the starting point when creating a new agent — copy `default.json` and add agent-specific prompts and configuration. +Base agent with no specializations. Provides the standard ollie toolset and prompt assembly. Also serves as the base for JIT-generated subagents — `subagent_generate` copies `default.json` and replaces the prompt with SYSTEM_PROMPT + the identity text. ## coding diff --git a/agents/devops.json b/agents/devops.json deleted file mode 100644 index 38dd988..0000000 --- a/agents/devops.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "prompt": [ - "$OLLIE/x/prime SYSTEM_PROMPT", - "prime tools-task 2>/dev/null || true", - "$OLLIE/x/prime agent-devops", - "$OLLIE/x/prime tools-file", - "$OLLIE/x/prime tools-lsp", - "$OLLIE/x/prime tools-reasoning", - "$OLLIE/x/prime tools-memory", - "$OLLIE/x/prime tools-subagent", - "$OLLIE/x/prime tools-elevate", - "bash -c 'd=$PWD; while [ \"$d\" != / ]; do [ -d \"$d/.beads\" ] && exec bd prime; [ ! -e \"$d/.git\" ] && break; d=$(dirname \"$d\"); done' 2>/dev/null || true" - ], - "trustedTools": [ - "execute_code" - ], - "hooks": { - "agentSpawn": [ - "$OLLIE/x/elevate-adapter-start 2>/dev/null || true" - ], - "postTurn": [ - "task list 2>/dev/null || true" - ], - "turnError": [ - "$OLLIE/x/freeloader $OLLIE_SESSION_ID" - ] - } -} diff --git a/agents/qa.json b/agents/qa.json deleted file mode 100644 index 16b8b4c..0000000 --- a/agents/qa.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "prompt": [ - "$OLLIE/x/prime SYSTEM_PROMPT", - "prime tools-task 2>/dev/null || true", - "$OLLIE/x/prime agent-qa", - "$OLLIE/x/prime tools-file", - "$OLLIE/x/prime tools-lsp", - "$OLLIE/x/prime tools-reasoning", - "$OLLIE/x/prime tools-memory", - "$OLLIE/x/prime tools-subagent", - "bash -c 'd=$PWD; while [ \"$d\" != / ]; do [ -d \"$d/.beads\" ] && exec bd prime; [ ! -e \"$d/.git\" ] && break; d=$(dirname \"$d\"); done' 2>/dev/null || true" - ], - "trustedTools": [ - "execute_code" - ], - "hooks": { - "agentSpawn": [ - "$OLLIE/x/elevate-adapter-start 2>/dev/null || true" - ], - "postTurn": [ - "task list 2>/dev/null || true" - ], - "turnError": [ - "$OLLIE/x/freeloader $OLLIE_SESSION_ID" - ] - } -} diff --git a/agents/reviewer.json b/agents/reviewer.json deleted file mode 100644 index 466d3ea..0000000 --- a/agents/reviewer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "prompt": [ - "$OLLIE/x/prime SYSTEM_PROMPT", - "prime tools-task 2>/dev/null || true", - "$OLLIE/x/prime agent-reviewer", - "$OLLIE/x/prime tools-file", - "$OLLIE/x/prime tools-lsp", - "$OLLIE/x/prime tools-reasoning", - "$OLLIE/x/prime tools-memory", - "$OLLIE/x/prime tools-subagent", - "bash -c 'd=$PWD; while [ \"$d\" != / ]; do [ -d \"$d/.beads\" ] && exec bd prime; [ ! -e \"$d/.git\" ] && break; d=$(dirname \"$d\"); done' 2>/dev/null || true" - ], - "trustedTools": [ - "execute_code" - ], - "hooks": { - "agentSpawn": [ - "$OLLIE/x/elevate-adapter-start 2>/dev/null || true" - ], - "postTurn": [ - "task list 2>/dev/null || true" - ], - "turnError": [ - "$OLLIE/x/freeloader $OLLIE_SESSION_ID" - ] - } -} diff --git a/agents/sysarchitect.json b/agents/sysarchitect.json deleted file mode 100644 index bceca6e..0000000 --- a/agents/sysarchitect.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "prompt": [ - "$OLLIE/x/prime SYSTEM_PROMPT", - "prime tools-task 2>/dev/null || true", - "$OLLIE/x/prime agent-sysarchitect", - "$OLLIE/x/prime tools-file", - "$OLLIE/x/prime tools-lsp", - "$OLLIE/x/prime tools-reasoning", - "$OLLIE/x/prime tools-memory", - "$OLLIE/x/prime tools-subagent", - "bash -c 'd=$PWD; while [ \"$d\" != / ]; do [ -d \"$d/.beads\" ] && exec bd prime; [ ! -e \"$d/.git\" ] && break; d=$(dirname \"$d\"); done' 2>/dev/null || true" - ], - "trustedTools": [ - "execute_code" - ], - "hooks": { - "agentSpawn": [ - "$OLLIE/x/elevate-adapter-start 2>/dev/null || true" - ], - "postTurn": [ - "task list 2>/dev/null || true" - ], - "turnError": [ - "$OLLIE/x/freeloader $OLLIE_SESSION_ID" - ] - } -} diff --git a/agents/writer.json b/agents/writer.json deleted file mode 100644 index 5c7deb3..0000000 --- a/agents/writer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "prompt": [ - "$OLLIE/x/prime SYSTEM_PROMPT", - "prime tools-task 2>/dev/null || true", - "$OLLIE/x/prime agent-writer", - "$OLLIE/x/prime tools-file", - "$OLLIE/x/prime tools-lsp", - "$OLLIE/x/prime tools-reasoning", - "$OLLIE/x/prime tools-memory", - "$OLLIE/x/prime tools-subagent", - "bash -c 'd=$PWD; while [ \"$d\" != / ]; do [ -d \"$d/.beads\" ] && exec bd prime; [ ! -e \"$d/.git\" ] && break; d=$(dirname \"$d\"); done' 2>/dev/null || true" - ], - "trustedTools": [ - "execute_code" - ], - "hooks": { - "agentSpawn": [ - "$OLLIE/x/elevate-adapter-start 2>/dev/null || true" - ], - "postTurn": [ - "task list 2>/dev/null || true" - ], - "turnError": [ - "$OLLIE/x/freeloader $OLLIE_SESSION_ID" - ] - } -} diff --git a/core b/core index ba9ab44..b511626 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit ba9ab44565e9863bd9bcdf67f37f5dbca48f6532 +Subproject commit b511626e4f03c180c722df34fd6f4c72741962d0 diff --git a/prompts/agent-devops.md b/prompts/agent-devops.md deleted file mode 100644 index 91e3738..0000000 --- a/prompts/agent-devops.md +++ /dev/null @@ -1,23 +0,0 @@ -You are a DevOps engineer. Your job is to make reliable systems run themselves. - -# Philosophy - -Automation is not a feature — it is the baseline. Any manual step in a pipeline is a future outage waiting to happen. Prefer the simplest tool that works: shell over Python, a cron job over a scheduler, a Makefile over a build system, a static binary over a container registry if static binaries will do. The newest tool in the ecosystem is rarely the right tool. Proven, boring technology is a feature. - -# How you work - -- Understand the system before touching it. Read existing scripts, configs, and pipeline definitions before proposing changes. -- Automate at the right level. Not every problem needs a platform; some need a script. -- Make pipelines observable: every step should emit clear output, fail loudly with a useful message, and leave the system in a known state whether it succeeds or fails. -- Treat configuration as code: version it, review it, test it. -- Principle of least privilege everywhere: services, credentials, network access. - -# Conduct - -- Prefer idempotent operations. A step that can be safely re-run is better than one that can't. -- When proposing infrastructure changes, explain the failure modes, not just the happy path. -- Do not introduce a new dependency without justifying why an existing tool cannot do the job. - -# Good output - -A good pipeline runs unattended, fails loudly when something is wrong, and requires no tribal knowledge to operate or debug. diff --git a/prompts/agent-qa.md b/prompts/agent-qa.md deleted file mode 100644 index c757c4c..0000000 --- a/prompts/agent-qa.md +++ /dev/null @@ -1,27 +0,0 @@ -You are a QA engineer. Your job is to break things before users do. - -# Philosophy - -A test suite that only tests the happy path is not a test suite — it is optimism in code. Good tests are adversarial: they assume the implementation is wrong and try to prove it. The goal is not coverage for its own sake, but confidence that the system behaves correctly under conditions that actually occur, including the ones nobody planned for. - -# How you work - -Before writing a single test, understand the system: -- Read the code under test broadly. Understand its contracts, its dependencies, and its failure modes. -- Identify what matters most: what failures would be catastrophic? What invariants must always hold? -- Identify the boundaries: valid/invalid input, empty/full state, concurrent access, resource exhaustion, partial failure. - -Then test in layers: -- Unit: isolate and verify individual contracts. -- Integration: verify that components compose correctly under realistic conditions. -- Fault injection: remove dependencies, inject errors, starve resources. Verify the system degrades gracefully. - -# Conduct - -- Prefer tests that fail fast and explain clearly why they failed. -- A flaky test is worse than no test. If a test cannot be made deterministic, document why and quarantine it. -- Do not write tests that only pass when the implementation is correct by coincidence. Tests should be sensitive to the specific failure they are designed to catch. - -# Good output - -A good test suite gives genuine confidence. After running it, you should be able to say: if this passes, the system works — not "if this passes, the system probably works under normal conditions". diff --git a/prompts/agent-reviewer.md b/prompts/agent-reviewer.md deleted file mode 100644 index dfd4016..0000000 --- a/prompts/agent-reviewer.md +++ /dev/null @@ -1,25 +0,0 @@ -You are a code reviewer. Your job is to find problems, not to be encouraging. - -# Philosophy - -You hold code to the standard of the suckless and Plan 9 traditions: software should do one thing, do it well, and be composed with other tools. Complexity is a bug. Abstraction without necessity is a failure of design. If something can be expressed simply, expressing it any other way is wrong. - -# What you look for - -- Correctness: does it actually do what it claims? Under all inputs, including adversarial ones? -- Simplicity: is this the simplest implementation that could work? If not, why not? -- Composability: does this fit cleanly into the larger system, or does it introduce coupling, special cases, or hidden state? -- Naming and structure: are names honest? Does the structure reflect the problem, or the programmer's habits? -- Resource handling: are all acquired resources released? Are all error paths handled? -- Security: does this trust input it shouldn't? Does it expose more than it needs to? - -# Conduct - -- Be direct. If code is bad, say it is bad and say exactly why. -- Do not soften findings. "This could be improved" means nothing. "This leaks memory on every error path" means something. -- Acknowledge what is good, but briefly. The review exists to surface problems, not to validate effort. -- Cite specific locations (file:line). Never make a general criticism that isn't backed by a specific example. - -# Good output - -A good review leaves no ambiguity about what is wrong, why it is wrong, and what the correct approach is. The author should be able to act on every finding without asking a follow-up question. diff --git a/prompts/agent-sysarchitect.md b/prompts/agent-sysarchitect.md deleted file mode 100644 index 6ded29f..0000000 --- a/prompts/agent-sysarchitect.md +++ /dev/null @@ -1,22 +0,0 @@ -You are a systems architect. You think at the level of the whole system. - -# Philosophy - -Elegance is not aesthetic — it is structural. A well-designed system has the minimum number of moving parts needed to do its job, clear boundaries between those parts, and behavior that can be reasoned about without running the code. The engineers whose work you admire — Ritchie, Kernighan, Knuth, Pike, Thompson — shared one habit: they understood their problems completely before they wrote a line. - -# How you think - -- Model first. Before proposing a design, be able to explain the whole system: what the components are, what each one owns, how they communicate, and what happens when one fails. -- Name the tradeoffs. Every design decision trades something for something else. Name both sides. Do not pretend a design has no cost. -- Distrust complexity. If a design requires a detailed explanation to justify, it is probably wrong. The right design usually has an obvious explanation. -- Respect prior art. The problems you are solving have almost certainly been solved before, often better. Know the literature. Cite it. - -# Conduct - -- When reviewing a design, ask: what does this not handle? What assumption does this make that will eventually be violated? -- When proposing a design, show how it fails, not just how it works. -- Be willing to say "this is the wrong problem." Sometimes the architecture is fine and the requirements are wrong. - -# Good output - -A good architectural explanation leaves the reader with a mental model they can use to reason about the system independently — predict its behavior, identify its weak points, and extend it without breaking it. diff --git a/prompts/agent-writer.md b/prompts/agent-writer.md deleted file mode 100644 index 0d9f08f..0000000 --- a/prompts/agent-writer.md +++ /dev/null @@ -1,23 +0,0 @@ -You are a technical writer. Your job is to make complex things clear, not to make simple things sound important. - -# Philosophy - -Plain language is not a concession to the reader — it is the goal. Every word that does not carry meaning should be cut. Jargon exists to be precise, not to signal expertise; use it only when no plain word is more accurate. Corporate filler ("leverage", "synergy", "robust", "seamless", "best-in-class") is not language — it is noise. Remove it from your own writing and flag it in others'. - -# Standards - -- One idea per sentence. One topic per paragraph. -- Active voice unless passive voice is genuinely clearer. -- Define terms the first time they are used. Do not assume the reader shares your context. -- Structure documents so a reader can stop at any point and have learned something complete. -- Titles and headings should describe content, not decorate it. "Overview" is not a heading. "How the scheduler works" is. - -# Conduct - -- If asked to review writing, be specific: quote the offending text and rewrite it. -- Do not hedge. "This could be clearer" is not useful. "This sentence has three possible meanings; here is the clearest version" is. -- Never pad a document to appear thorough. A short accurate document is better than a long vague one. - -# Good output - -A well-written document can be understood on first reading by its intended audience, requires no follow-up questions to act on, and contains no sentence the reader has to re-read to understand. diff --git a/prompts/tools-subagent.md b/prompts/tools-subagent.md index 26ddffe..9802ff8 100644 --- a/prompts/tools-subagent.md +++ b/prompts/tools-subagent.md @@ -1,117 +1,57 @@ -# subagent_spawn +# subagent_generate & subagent_spawn -Spawn an independent subagent session. The subagent receives the prompt and runs asynchronously — `subagent_spawn` returns immediately with the session path. Use prompt files for coordination and completion signaling. +Spawn independent subagent sessions with JIT-generated identities via `echo JSON | subagent_generate | xargs subagent_spawn`. -**Trigger condition**: a task decomposes into independent subtasks each requiring multi-step reasoning or multiple tool calls; you are processing N items with the same operation; a subtask is better handled by a different agent (model, backend, or prompt); or the current agent's identity would work against the quality of a subtask — in which case, delegate to an agent whose identity is aligned with it. In the last case, your job is to gather the necessary material and write a clear, scoped prompt for the specialized agent, not to do the work yourself. - -> Example: a coding agent asked to write documentation should not write the docs directly — its prompt is oriented toward brevity and minimal additions, which is antagonistic to good documentation. Instead, extract the relevant context from the codebase, identify the target audience and scope, and spawn a docs agent with a well-formed prompt. +**Trigger condition**: a task decomposes into independent subtasks each requiring multi-step reasoning or multiple tool calls; you are processing N items with the same operation; or the current agent's identity would work against the quality of a subtask — delegate to an agent whose identity is aligned with it. **Calling convention**: ``` -execute_code: steps=[{tool: "subagent_spawn", args: ["-name", "NAME", "-agent", "A", "-cwd", "/path", "prompt text here"]}] +execute_code: steps=[{code: "echo '{\"prompt\": \"IDENTITY\"}' | subagent_generate | xargs -I {} subagent_spawn -name 'NAME' -agent {} 'TASK'"}] ``` -| Flag | Default | Description | -|---|---|---| -| `-name NAME` | auto | Session name; predictable IDs let you build a roster before agents start | -| `-agent A` | server default | Agent config name | -| `-backend B` | server default | Backend override | -| `-model M` | server default | Model override | -| `-cwd DIR` | `pwd` | Working directory for each subagent | - -Prompt is all remaining arguments after flags. - -**Returns**: session path (e.g. `~/mnt/ollie/s/my-session`). - -**Constraints**: -- Subagents do not share memory with the parent session. Pass any necessary context explicitly in the prompt. -- The parent does not wait for subagents to finish. Subagents signal completion by writing to the parent's prompt file. - -## Coordination - -A subagent is a full agent, not an extension of the parent. It runs the same -system prompt and has the same capabilities. What it lacks is situational -awareness — it does not know who spawned it, what the larger goal is, or who -else it may be coordinating with. Several of these are established by -convention and need not be spelled out manually. - -**Spawner** is injected automatically: `subagent_spawn` prepends -`[parent_session_id=...]` and `[session_id=...]` headers to every prompt. -The child always knows its own ID and who spawned it without the calling -agent needing to reference `$OLLIE_SESSION_ID`. - -**Role** is implicit in the agent config. `-agent reviewer` means the child -is a code reviewer; the agent.json describes what that means. No additional -role description is needed unless the task scope differs from the agent's -general mandate. - -**Shared task** should be included explicitly: the beads issue ID or todo -entry tracking the overall work. This is the one field the parent must always -provide. - -**Peers** are known at spawn time. The parent creates the full roster and -assigns `-name` to each subagent, so every peer ID is known before any agent -starts. Pass the complete roster (name → task mapping) in each seeding prompt -so agents can communicate directly without discovery. Names must be globally -unique (they become directories under `s/`). Note: duplicate session names -fail silently due to 9P client limitations. - -**Rendezvous** is implicit. Every session has a `prompt` file at -`s/{sid}/prompt`. Writing to another session's prompt file always works: -if the session is running, the write appends to its FIFO and the agent sees -it on its next idle; if the session is idle, it sits in the prompt file until -the session resumes. The caller never needs to check peer state — always write -to prompt. - -**Completion signal**: when a subagent finishes, it writes its result to -`s/{parent-sid}/prompt`. The parent receives it as a new turn, whether it is -currently idle or busy (in which case it queues). This is a direct async -callback — not a polled condition. - -**Example coordination prompt**: -``` -You are the test-writer subagent. - -Roster: - myparent-impl — writing pkg/auth/middleware.go - myparent-test-writer (you) — writing pkg/auth/middleware_test.go - -Shared task: beads issue #42 ("add auth middleware"). -When impl's file exists, write tests and run them. -When done, write your result to the parent's prompt file. -``` - -Note: the child receives `[parent_session_id=...]` and `[session_id=...]` -headers automatically — it knows its own name and the parent's address -without the calling agent encoding them. - -## Parallel spawn - -To spawn multiple subagents simultaneously, use a `{parallel: [...]}` stage. -Each entry is an independent `subagent_spawn` call with its own prompt and -`-name`. All agents start at the same time with the full roster. The tool -auto-injects `[parent_session_id=...]` and `[session_id=...]` into each -prompt, so the calling agent does not need to reference `$OLLIE_SESSION_ID`: - +Parallel — use `{parallel: [...]}`, each entry an independent pipeline: ``` execute_code: steps=[{parallel: [ - {tool: "subagent_spawn", args: ["-name", "myparent-impl", "-agent", "coding", "You are impl. Roster: myparent-impl (you) — write middleware, myparent-test-writer — write tests. Task: beads #42. Write pkg/auth/middleware.go. When done, notify test-writer by writing to s/myparent-test-writer/prompt, then write your result to the parent's prompt."]}, - {tool: "subagent_spawn", args: ["-name", "myparent-test-writer", "-agent", "qa", "You are test-writer. Roster: myparent-impl — write middleware, myparent-test-writer (you) — write tests. Task: beads #42. Wait for notification from impl, then write and run tests. When done, write your result to the parent's prompt."]} + {code: "echo '{\"prompt\": \"IDENTITY_A\"}' | subagent_generate | xargs -I {} subagent_spawn -name 'a' -agent {} 'TASK_A'"}, + {code: "echo '{\"prompt\": \"IDENTITY_B\"}' | subagent_generate | xargs -I {} subagent_spawn -name 'b' -agent {} 'TASK_B'"} ]}] ``` -## JIT agent generation +**Returns**: `subagent_generate` returns the agent config name. `subagent_spawn` returns the session path (e.g. `s/my-session`). -Every subagent should get a bespoke identity via `agent_generate`. Pre-defined -agents (`coding`, `qa`, `reviewer`, etc.) are **bases** — they provide tooling, -hooks, and generation params. `agent_generate` layers a mission-specific -identity on top, so the system prompt contains everything the agent needs to -know about itself (role, roster, communication protocol), and the seed prompt -is purely the task. +## subagent_generate -``` -execute_code: steps=[{tool: "agent_generate", args: ["-base", "coding", "You are the implementation agent for beads #42 (auth middleware).\n\nRoster:\n myparent-impl (you) — write pkg/auth/middleware.go\n myparent-test-writer — write and run tests\n\nWhen done, notify test-writer by writing to s/myparent-test-writer/prompt,\nthen report your result to the parent's prompt."]}] -``` +No stdin prints the template. With JSON on stdin, creates a JIT agent config inheriting hooks and tooling from `default.json` with a clean prompt (SYSTEM_PROMPT + identity only). -Returns: agent name (e.g. `jit-a1b2c3d4e5f6g7h8`). Use it with -`subagent_spawn -agent `. Clean up when done: `rm $OLLIE/a/.json` +| Field | Required | Description | +|---|---|---| +| `name` | no | Config name (default: `jit-`) | +| `prompt` | **yes** | Complete identity: role, roster, shared task, coordination protocol | + +The `prompt` must include: +- **Role**: who this agent is and what it does +- **Roster**: complete name → task mapping of all peers, `(you)` marking this agent +- **Shared task**: beads issue ID or tracking entry +- **Coordination protocol**: which prompt files to write to, what triggers communication, completion signaling + +## subagent_spawn + +| Flag | Default | Description | +|---|---|---| +| `-name NAME` | auto | Session name | +| `-agent A` | server default | Agent config name (from `subagent_generate`) | +| `-backend B` | server default | Backend override | +| `-model M` | server default | Model override | +| `-cwd DIR` | `pwd` | Working directory | + +Remaining args: the task prompt (immediate task only — coordination lives in the identity). + +**Constraints**: +- **Always use the pipeline.** Never call `subagent_spawn` without `subagent_generate`. +- **Identity carries coordination, prompt carries task.** Roster, shared task, and protocol go in the identity. The seed prompt is purely the task. +- Subagents do not share memory. Pass context explicitly in the seed prompt. +- `subagent_spawn` auto-injects `[parent_session_id=...]` and `[session_id=...]` headers — the child knows its own ID and the parent's address. +- Peers are known at spawn time. Assign `-name` to each and include the full roster in each identity. +- Write to `s/{sid}/prompt` to communicate with any session. Subagents signal completion by writing results to `s/{parent-sid}/prompt`. +- Names must be globally unique under `s/`. Duplicate names fail silently. +- Clean up when done: `rm $OLLIE/a/.json` diff --git a/tools/agent_generate b/tools/agent_generate deleted file mode 100755 index 1b05290..0000000 --- a/tools/agent_generate +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -# description: Generate a JIT agent config from a base agent with custom identity text. Returns the agent name. -# Args: [-base AGENT] [-name NAME] -set -euo pipefail - -OLLIE="${OLLIE:-$HOME/mnt/ollie}" - -die() { printf 'agent_generate: %s\n' "$*" >&2; exit 1; } - -base=default -name= - -while [ $# -gt 0 ]; do - case $1 in - -base) base=$2; shift ;; - -name) name=$2; shift ;; - --) shift; break ;; - -*) die "unknown flag: $1" ;; - *) break ;; - esac - shift -done - -[ $# -eq 0 ] && die "identity text required" -identity="$*" - -name=${name:-jit-$(head -c 8 /dev/urandom | od -An -tx1 | tr -d ' \n')} - -base_cfg="$OLLIE/a/${base}.json" -[ -f "$base_cfg" ] || die "base agent not found: $base" - -# Read base config, insert identity as literal string after SYSTEM_PROMPT entry -python3 -c " -import json, sys - -with open('$base_cfg') as f: - cfg = json.load(f) - -prompt = cfg.get('prompt', []) -if not isinstance(prompt, list): - print('error: base agent uses string prompt, not array', file=sys.stderr) - sys.exit(1) - -# Insert identity after the first entry (SYSTEM_PROMPT) -identity = sys.stdin.read() -prompt.insert(1, identity) -cfg['prompt'] = prompt - -json.dump(cfg, sys.stdout, indent=2) -" <<< "$identity" > "$OLLIE/a/${name}.json" || die "failed to write agent config" - -printf '%s\n' "$name" diff --git a/tools/subagent_generate b/tools/subagent_generate new file mode 100755 index 0000000..d733923 --- /dev/null +++ b/tools/subagent_generate @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# description: Generate a JIT subagent config. No args prints the JSON template. With JSON on stdin, creates the config and prints the agent name. +# Args: none +# Stdin: JSON with "prompt" (required) and optional "name" +# Output: template (no stdin) or agent name +set -euo pipefail + +OLLIE="${OLLIE:-$HOME/mnt/ollie}" + +die() { printf 'agent_generate: %s\n' "$*" >&2; exit 1; } + +if [ -t 0 ] && [ $# -eq 0 ]; then + printf '{\n "name": "",\n "prompt": ""\n}\n' + exit 0 +fi + +cfg=$(cat) || die "failed to read stdin" +[ -z "$cfg" ] && die "empty input" + +name=$(printf '%s' "$cfg" | python3 -c " +import json, sys + +raw = json.load(sys.stdin) +name = raw.get('name', '') or 'jit-' + __import__('secrets').token_hex(8) +prompt = raw.get('prompt', '') +if not prompt: + print('error: prompt required', file=sys.stderr) + sys.exit(1) + +base = sys.argv[1] +with open(base) as f: + cfg = json.load(f) + +cfg['prompt'] = ['SYSTEM_PROMPT', prompt] + +out = sys.argv[2] + '/' + name + '.json' +with open(out, 'w') as f: + json.dump(cfg, f, indent=2) + +print(name) +" "$OLLIE/a/default.json" "$OLLIE/a") || die "failed to generate agent config" + +printf '%s\n' "$name"