Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions R/subagent.R
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,13 @@ resolve_subagent_id <- function(input) {
#'
#' Called once per child just after [worker_init()]. Creates a
#' `new_session()` configured with the subagent's provider/model/tools
#' and stores it where [subagent_turn_prompt()] can find it. Subagents
#' deny all tool approvals by default so a subagent can't run bash
#' without the parent opting in.
#' and stores it where [subagent_turn_prompt()] can find it. The
#' child's `approval_cb` denies by default: subagents have no
#' interactive approval channel back to the parent or user, and tool
#' permissions are fixed at spawn time via `tools_filter` (derived
#' from the parent's `preset` or explicit `tools` argument to
#' [subagent_spawn()]). There is no way to grant additional capability
#' mid-run.
#'
#' @param provider LLM provider name (see [new_session()]).
#' @param model Optional model override.
Expand Down Expand Up @@ -325,14 +329,27 @@ subagent_session_key <- function(parent_key) {
#' registry set up. Stores the handle in the package-level registry
#' keyed by subagent id.
#'
#' Permissions: subagents have no interactive approval channel back
#' to the parent or user. The child's `approval_cb` denies by default
#' and there is no mid-run escalation path. Whatever capability the
#' child needs must be granted at spawn time through `preset` or
#' `tools`. If a task may need shell, write, or network capability,
#' pick a preset that includes it (or pass an explicit `tools` list);
#' otherwise the child should report that it is blocked rather than
#' retry.
#'
#' @param task Task description (stored for bookkeeping; not yet fed
#' into an agent loop).
#' @param model Optional model override (reserved for later use).
#' @param tools Optional explicit tool filter (character vector).
#' Overrides `preset` when provided.
#' @param preset Preset name: `"investigate"` (read/search only, default),
#' `"work"` (investigate + bash + write/edit), or `"minimal"`
#' (read_file + grep_files only).
#' Overrides `preset` when provided. Fixed for the lifetime of the
#' child — cannot be expanded after spawn.
#' @param preset Preset name (fixed for the lifetime of the child).
#' `"investigate"` (default): `read_file`, `grep_files`, `r_help`,
#' `web_search`, `fetch_url`. `"work"`: investigate + `bash`,
#' `write_file`, `replace_in_file`, `list_files`, `git_status`,
#' `git_diff`, `git_log`, `run_r`. `"minimal"`: `read_file`,
#' `grep_files`.
#' @param parent_session Parent session object; read for
#' nested-spawning control and session-key derivation.
#' @param config Config list.
Expand Down
20 changes: 16 additions & 4 deletions man/subagent_spawn.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ into an agent loop).}
\item{model}{Optional model override (reserved for later use).}

\item{tools}{Optional explicit tool filter (character vector).
Overrides `preset` when provided.}
Overrides `preset` when provided. Fixed for the lifetime of the
child — cannot be expanded after spawn.}

\item{preset}{Preset name: `"investigate"` (read/search only, default),
`"work"` (investigate + bash + write/edit), or `"minimal"`
(read_file + grep_files only).}
\item{preset}{Preset name (fixed for the lifetime of the child).
`"investigate"` (default): `read_file`, `grep_files`, `r_help`,
`web_search`, `fetch_url`. `"work"`: investigate + `bash`,
`write_file`, `replace_in_file`, `list_files`, `git_status`,
`git_diff`, `git_log`, `run_r`. `"minimal"`: `read_file`,
`grep_files`.}

\item{parent_session}{Parent session object; read for
nested-spawning control and session-key derivation.}
Expand All @@ -31,4 +35,12 @@ Subagent ID (character).
Starts a fresh `callr::r_session` with corteza loaded and its tool
registry set up. Stores the handle in the package-level registry
keyed by subagent id.
Permissions: subagents have no interactive approval channel back
to the parent or user. The child's `approval_cb` denies by default
and there is no mid-run escalation path. Whatever capability the
child needs must be granted at spawn time through `preset` or
`tools`. If a task may need shell, write, or network capability,
pick a preset that includes it (or pass an explicit `tools` list);
otherwise the child should report that it is blocked rather than
retry.
}
10 changes: 7 additions & 3 deletions man/subagent_turn_init.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ Invisible TRUE.
\description{
Called once per child just after [worker_init()]. Creates a
`new_session()` configured with the subagent's provider/model/tools
and stores it where [subagent_turn_prompt()] can find it. Subagents
deny all tool approvals by default so a subagent can't run bash
without the parent opting in.
and stores it where [subagent_turn_prompt()] can find it. The
child's `approval_cb` denies by default: subagents have no
interactive approval channel back to the parent or user, and tool
permissions are fixed at spawn time via `tools_filter` (derived
from the parent's `preset` or explicit `tools` argument to
[subagent_spawn()]). There is no way to grant additional capability
mid-run.
}
\keyword{internal}
12 changes: 11 additions & 1 deletion vignettes/configuration.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,19 @@ All keys shown with type and default, current as of corteza 0.6.3. Most defaults
| `subagents.max_concurrent` | integer | `3` | Max parallel subagents |
| `subagents.timeout_minutes` | integer | `30` | Subagent kill timeout |
| `subagents.allow_nested` | boolean | `false` | Allow nested subagents |
| `subagents.default_tools` | string[] | `["base::readLines", "base::writeLines", "bash", "grep_files"]` | Tools available to subagents |
| `subagents.default_tools` | string[] | `["read_file", "grep_files", "r_help", "web_search", "fetch_url"]` | Tools available to subagents when no preset is given |
| `subagents.base_port` | integer | `7851` | Starting port for subagent MCP servers |

**Presets.** `/spawn --preset <name>` picks a fixed tool list at spawn time:

- `investigate` (default): `read_file`, `grep_files`, `r_help`, `web_search`, `fetch_url` (read/search/help, plus network reads).
- `minimal`: `read_file`, `grep_files`.
- `work`: investigate + `bash`, `write_file`, `replace_in_file`, `list_files`, `git_status`, `git_diff`, `git_log`, `run_r`.

`/spawn --tools <comma-list>` overrides the preset with an explicit tool list.

**Permission model.** Subagents have no interactive approval channel back to the parent or user. A child's tool list is fixed at spawn time — via the `--preset` or `--tools` flags on `/spawn`, or the `preset` / `tools` arguments to `subagent_spawn()`. There is no mid-run path to escalate to a riskier capability: the child's approval callback denies by default, and the parent cannot grant new tools after the fact. If a task may need shell, write, or network capability, choose a preset or explicit tool list that includes it at spawn time; otherwise the child should report that it is blocked.

### Workspace

| Key | Type | Default | Description |
Expand Down
13 changes: 13 additions & 0 deletions vignettes/retroactive-extraction.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@ The LLM uses normal tool selection to decide between
`query_subagent(id, "what did you find at line 42?")` and
`spawn_subagent("...")` for new work.

## Permission model

Holder subagents (the ones the archival runtime creates) carry
`tools = character(0)`: they hold transcript context for
`query_subagent`, nothing more. Worker subagents spawned via
`spawn_subagent(task)` inherit only the tools the parent grants at
spawn time. There is no interactive approval channel back to the
parent or user, and no way to grant capability mid-run — the child's
`approval_cb` denies by default. If a worker may need shell, write, or
network capability, pick a preset or explicit tool list that includes
it when calling `spawn_subagent()`; otherwise the child should report
that it is blocked rather than retry.

## Recursion

A subagent finishing its own query re-evaluates the same triggers
Expand Down