diff --git a/doc/ARCHITECTURE.md b/doc/ARCHITECTURE.md index 83a2aee..d42b3e2 100644 --- a/doc/ARCHITECTURE.md +++ b/doc/ARCHITECTURE.md @@ -62,22 +62,49 @@ Frontend All tool calls go through one `tools.Dispatcher.Dispatch` path. Every registered server is a `tools.Server`; the dispatcher routes by name and is agnostic to how any server is implemented. -## Built-in Tools +## execute_code -One built-in tool via `execute.Server`: +`execute_code` is the only built-in tool. It is a unified primitive that handles simple execution, sequential composition, and parallel fan-out through a single `steps` array of pipeline stages. -| Tool | What it does | -|---|---| -| `execute_code` | Runs a pipeline of stages sequentially, piping stdout → stdin. Each stage is `{code}`, `{tool, args}`, or `{parallel:[...]}` for concurrent fan-out. Single stage = degenerate case, raw output. | +Each stage is one of: +- `{code, language}` — inline code (default: bash) +- `{tool, args}` — named script from `OLLIE_TOOLS_PATH`; language detected from shebang +- `{parallel: [...]}` — concurrent fan-out; each element is a `{code}` or `{tool}` step; outputs concatenated in submission order -`OLLIE_TOOLS_PATH` defaults to `~/.config/ollie/tools`. The directory can be a symlink or a mountpoint — `{tool}` steps in `execute_code` resolve scripts from this path. +Stages run sequentially, each stage's stdout fed to the next stage's stdin. A single simple stage is the degenerate case and returns raw output directly. + +``` +steps=[{code: "ls"}] — simple execution +steps=[{code: "grep error log"}, {code:"wc"}] — sequential pipeline +steps=[{parallel:[{code:"wc a"},{code:"wc b"}]}] — parallel fan-out +steps=[{parallel:[...]}, {code:"sort"}] — fan-out then transform +``` + +`OLLIE_TOOLS_PATH` defaults to `~/.config/ollie/tools`. `{tool}` steps resolve scripts from this path. ## Tool Scripts -Capabilities beyond `execute_code` are implemented as tool scripts in `OLLIE_TOOLS_PATH`. Scripts are invoked via `execute_code` `{tool}` steps. The agent discovers available scripts via `t/idx`. +All capabilities beyond `execute_code` itself are implemented as tool scripts in `OLLIE_TOOLS_PATH`, invoked via `{tool}` steps. The agent discovers available scripts via `t/idx`. Examples from the default install: `file_read`, `file_write`, `file_edit`, `file_glob`, `file_grep`, `memory_remember`, `memory_recall`, `reasoning_think`, `task_plan`, `task_complete`, `subagent_spawn`. +## Sub-agents + +Sub-agents are implemented as a tool script (`subagent_spawn`) on top of the `b/` batch namespace. The script writes a job spec to `b/new`, polls `b/{id}/status` per job in background subshells, joins on all, and concatenates results. The `b/` namespace (backed by `BatchStore`) handles agent lifecycle, cancellation, and result storage. + +For heterogeneous parallel delegation (N agents, N different prompts), a `{parallel:[...]}` stage composes with `subagent_spawn` — each parallel step is one shell invocation with a distinct prompt: + +``` +execute_code {parallel} stage + ├── subagent_spawn "prompt A" → b/{id-0}/result + ├── subagent_spawn "prompt B" → b/{id-1}/result + └── subagent_spawn "prompt C" → b/{id-2}/result + ↓ + concatenated in submission order +``` + +The architecture boundary is clean: `execute_code` is the only built-in tool in `core`; everything else is a tool script; `b/` is a 9P namespace. No layer knows about the others. + ## Planning `task_plan` saves a markdown checklist to the plan directory (`pl/` in ollie-9p) as a `__todo.md` file. The agent then: @@ -89,27 +116,6 @@ Examples from the default install: `file_read`, `file_write`, `file_edit`, `file `task_complete` takes the plan filename and a step title (substring match, case-insensitive). It errors if no matching unchecked step is found, so silent skips are caught. -Everything else — listing plans, inspecting a specific step, bulk operations — goes through `file_*` scripts or inline `execute_code`. - -## Parallel Execution and Sub-agents - -`execute_code` is a sequential pipeline: stages run in order, stdout piped to the next stage's stdin. A `{parallel:[...]}` stage fans out concurrently within the pipeline, concatenating results in submission order before passing them downstream. A single simple stage is the degenerate case and returns raw output. - -Sub-agents are implemented as a tool script (`subagent_spawn`) on top of the `b/` batch namespace. The script writes a job spec to `b/new`, polls `b/{id}/status` per job in background subshells, joins on all, and concatenates results. The `b/` namespace (backed by `BatchStore`) handles agent lifecycle, cancellation, and result storage. - -For heterogeneous parallel delegation (N agents, N different prompts), `execute_code` parallel steps compose with `subagent_spawn`: each step is one shell invocation of the script with a distinct prompt, all running concurrently. This is the fork-join pattern for agent-level work: - -``` -execute_code parallel steps - ├── subagent_spawn "prompt A" → b/{id-0}/result - ├── subagent_spawn "prompt B" → b/{id-1}/result - └── subagent_spawn "prompt C" → b/{id-2}/result - ↓ - joined in submission order -``` - -The architecture boundary is preserved: `execute_code` is the only built-in tool in `core`; everything else is a tool script; `b/` is a 9P namespace. No layer knows about the others. - ## Typical Consumer Setup ```go diff --git a/doc/OBSERVATIONS.md b/doc/OBSERVATIONS.md index 04fe06a..9f1d3c1 100644 --- a/doc/OBSERVATIONS.md +++ b/doc/OBSERVATIONS.md @@ -18,4 +18,4 @@ **Passive memory** — Every session's chat history accumulates at `s/{id}/chat`, searchable with `grep`. An agent doesn't need to actively decide what to remember. This extends across sessions: an agent can search another session's chat for answers that have already been worked through. -**Sub-agents** — `subagent_spawn` implements fork-join over the `b/` namespace: spawn N specialized agents with a shared prompt, wait for all to complete, collect results in submission order. For heterogeneous parallel delegation (N agents, N different prompts), combine with `execute_code` parallel steps — each step calls `subagent_spawn` with a different prompt, all running concurrently. +**Sub-agents** — `subagent_spawn` implements fork-join over the `b/` namespace: spawn N specialized agents with a shared prompt, wait for all to complete, collect results in submission order. For heterogeneous parallel delegation (N agents, N different prompts), use a `{parallel: [...]}` stage in `execute_code` — each parallel step calls `subagent_spawn` with a different prompt, all running concurrently. diff --git a/prompts/02_tools.md b/prompts/02_tools.md index 4dc4339..85fa925 100644 --- a/prompts/02_tools.md +++ b/prompts/02_tools.md @@ -27,4 +27,4 @@ Each stage is one of: Supported inline languages: `bash`, `python3`, `perl`, `lua`, `awk`, `sed`, `jq`, `ed`, `expect`, `bc`. -All parallel steps in a stage should produce the same output schema so their concatenated output is coherent for the next stage. Load the `execute-pipe` skill for pipeline patterns and the ETL pattern for disparate schemas. +All parallel steps in a stage should produce the same output schema so their concatenated output is coherent for the next stage. Load the `execute-pipe` skill for pipeline patterns and the ETL pattern for disparate output schemas. diff --git a/prompts/08_subagents.md b/prompts/08_subagents.md index 694e9b5..a46e949 100644 --- a/prompts/08_subagents.md +++ b/prompts/08_subagents.md @@ -12,7 +12,7 @@ Spawn when: - A subtask is better handled by a different agent (different model, backend, or system prompt) - You want to isolate a long-running or risky operation from the current session -Use `execute_code` parallel steps instead when the subtasks are single shell commands. Subagents are for agent-level work; parallel code steps are for parallel shell execution. +Use a `{parallel: [...]}` stage in `execute_code` instead when the subtasks are single shell commands. Subagents are for agent-level work; parallel stages are for parallel shell execution. ## How to use @@ -38,17 +38,17 @@ Prompt is all remaining arguments after flags. ## Heterogeneous fan-out -To run N subagents with N different prompts, combine with `execute_code` parallel steps — each step is one `subagent_spawn` shell invocation: +To run N subagents with N different prompts, use a `{parallel: [...]}` stage — each parallel step is one `subagent_spawn` shell invocation: ``` -execute_code: steps=[ +execute_code: steps=[{parallel: [ {code: "subagent_spawn 'Summarize core/pkg/agent/loop.go'"}, {code: "subagent_spawn 'Summarize core/pkg/backend/anthropic.go'"}, {code: "subagent_spawn 'Summarize 9p/internal/p9/server.go'"} -], timeout=300 +]}], timeout=300 ``` -Three concurrent subagents, three different prompts. Results appear under `=== step N ===` headers from `execute_code`. +Three concurrent subagents, three different prompts. Results are concatenated in submission order. Set `timeout` to at least 300 seconds when subagents are doing non-trivial work — the default 30 seconds is not enough.