mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-29 01:42:21 +00:00
## Summary
This PR makes agent execution a strict two-backend contract: API-backed
stages use Fabro-owned model/provider auth, while ACP-backed stages
launch a user-supplied stdio process that owns its own auth and tools.
That removes the legacy CLI backend and prevents ACP execution from
accidentally resolving or forwarding provider credentials.
## Changes
- Replaces the old `api`/`cli`/`acp` backend model with `AgentBackend {
api, acp }`, with `backend=\"cli\"` rejected and migrated toward
explicit ACP process configuration.
- Splits ACP process configuration into `acp.command` for shell command
strings and `acp.config` for JSON stdio configs, while rejecting legacy
`acp_command`.
- Restricts ACP to `agent` nodes and rejects API-only attributes such as
`model`, `provider`, `reasoning_effort`, `max_tokens`, and `speed` on
ACP nodes.
- Deletes the workflow CLI runtime, CLI credential resolver surface, CLI
live smoke tests, and `agent.cli.*` event handling.
- Updates ACP events and projections to report process identity
(`command`, optional `config_name`) rather than provider/model metadata.
- Updates import/stylesheet propagation, CLI workflow smoke coverage,
server steering tests, and web model extraction for the new
event/backend contract.
## Validation
- `cargo check -p fabro-auth -p fabro-acp -p fabro-workflow -p fabro-cli
--all-targets`
- `cargo nextest run -p fabro-auth -p fabro-acp -p fabro-validate -p
fabro-store -p fabro-workflow --lib`
- `cargo nextest run -p fabro-acp`
- `cargo nextest run -p fabro-cli --test it
workflow::acp::acp_backend_workflow`
- `cargo nextest run -p fabro-workflow --test it
codergen_without_backend_simulated`
- `cargo nextest run -p fabro-workflow --test it
import_e2e_through_engine`
- `cargo nextest run -p fabro-workflow --test it stylesheet_application`
- `cargo nextest run -p fabro-server
steer_with_active_acp_stage_returns_non_steerable_conflict`
- `cargo nextest run -p fabro-server
active_acp_stage_marker_clears_on_terminal_paths`
- `cargo nextest run -p fabro-types
agent_backend_accepts_only_api_and_acp`
- `cd apps/fabro-web && bun test app/routes/run-stages.test.ts`
- `cd apps/fabro-web && bun run typecheck`
- `cargo +nightly-2026-04-14 fmt --check --all`
- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D
warnings`
---
[](https://github.com/EveryInc/compound-engineering-plugin)
🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
---------
Co-authored-by: Peter Bell <4843+PeterBell@users.noreply.github.com>
99 lines
3.7 KiB
Text
99 lines
3.7 KiB
Text
---
|
|
title: "Sub-agents"
|
|
description: "Delegate subtasks to child agent sessions"
|
|
---
|
|
|
|
An agent can spawn **sub-agents** to delegate work to independent child sessions. Each sub-agent gets its own LLM session and tool access, runs concurrently with the parent, and returns its result when finished.
|
|
|
|
<Note>
|
|
Sub-agents are only available with the [API backend](/core-concepts/agents#api-backend-default) (the default). Agents using the [ACP backend](/core-concepts/agents#acp-backend) cannot spawn Fabro sub-agents.
|
|
</Note>
|
|
|
|
## Tools
|
|
|
|
Sub-agent management is exposed through four built-in tools:
|
|
|
|
| Tool | Description |
|
|
|---|---|
|
|
| `spawn_agent` | Create a new sub-agent with a task prompt |
|
|
| `send_input` | Send follow-up input to a running sub-agent |
|
|
| `wait` | Block until a sub-agent completes and return its result |
|
|
| `close_agent` | Cancel and remove a running sub-agent |
|
|
|
|
These tools are registered automatically when the session starts. They inherit the parent's permissions.
|
|
|
|
## Session isolation
|
|
|
|
Each sub-agent runs in its own session:
|
|
|
|
- **Own conversation** -- the child starts with a fresh LLM history
|
|
- **Own tool access** -- the child can use the same tools as the parent
|
|
- **Concurrent execution** -- the parent can continue working before calling `wait`
|
|
|
|
The parent can spawn multiple sub-agents and synchronize with them later.
|
|
|
|
## Depth limits
|
|
|
|
Sub-agents can themselves spawn sub-agents, creating a hierarchy. `max_subagent_depth` limits how deep that tree can grow. By default the depth limit is `1`.
|
|
|
|
If a child tries to exceed the limit, `spawn_agent` returns an error immediately.
|
|
|
|
## Error handling
|
|
|
|
Sub-agent failures do not automatically fail the parent stage. The parent receives the failure through `wait` and decides how to respond.
|
|
|
|
Common cases:
|
|
|
|
- **Hits `max_turns`** -- returns normally with its last output
|
|
- **Panics or errors** -- returned as a failed `wait` result
|
|
- **`spawn_agent` fails** -- returned immediately as a tool result
|
|
|
|
## Event forwarding
|
|
|
|
Sub-agent observability now uses **session linkage**, not wrapper events.
|
|
|
|
Parent-owned lifecycle events:
|
|
|
|
| Event | When |
|
|
|---|---|
|
|
| `agent.sub.spawned` | A sub-agent was created |
|
|
| `agent.sub.completed` | A sub-agent finished successfully |
|
|
| `agent.sub.failed` | A sub-agent failed |
|
|
| `agent.sub.closed` | A sub-agent was cancelled |
|
|
|
|
Forwarded child activity:
|
|
|
|
- Child tool calls, assistant messages, warnings, and other non-noisy session events are forwarded into the parent run's event stream as normal agent events.
|
|
- The forwarded event keeps the child's `session_id`.
|
|
- `parent_session_id` points to the child's immediate parent session.
|
|
|
|
Example envelope:
|
|
|
|
```json
|
|
{
|
|
"event": "agent.tool.started",
|
|
"session_id": "ses_child",
|
|
"parent_session_id": "ses_parent",
|
|
"node_id": "code",
|
|
"properties": {
|
|
"tool_name": "read_file"
|
|
}
|
|
}
|
|
```
|
|
|
|
Nested sub-agents preserve the immediate parent-child relationship. A grandchild forwarded through multiple parents still carries its own `session_id`, and `parent_session_id` remains set to the grandchild's direct parent rather than the root.
|
|
|
|
High-volume streaming events such as text deltas and tool output deltas are filtered out of the forwarded stream to reduce noise.
|
|
|
|
## When to use sub-agents
|
|
|
|
Sub-agents are most useful for:
|
|
|
|
- **Parallel research** -- inspect multiple code paths at once
|
|
- **Isolation** -- try a risky approach without polluting the parent's context
|
|
- **Divide and conquer** -- split an independent task into smaller pieces
|
|
- **Context management** -- offload work when the parent session is getting crowded
|
|
|
|
<Note>
|
|
Sub-agents run with no turn limit by default. Pass `max_turns` when you want predictable cost or time bounds. All active sub-agents are cleaned up automatically when the parent session closes.
|
|
</Note>
|