Skip to content

feat(core): let subagents launch nested subagents - #39

Merged
dezren39 merged 5 commits into
mainfrom
nested-subsessions
Oct 18, 2026
Merged

dezren39 merged 5 commits into
mainfrom
nested-subsessions

Conversation

@dezren39

@dezren39 dezren39 commented Oct 18, 2026 •

Copy link
Copy Markdown

Issue for this PR

No issue. This was requested directly: subsessions should be able to create subsessions.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

A subagent could not start a subagent of its own. Two things blocked it. experimental.subagent_depth defaulted to 1, so the subagent tool refused any call made from a child session. The built-in general agent also denied the subagent action, so it never saw the tool at all.

Now:

  • Depth limit. The default limit is 5 levels below the top-level session. A call past the limit fails with a message telling the model to do that work itself. experimental.subagent_depth still sets the limit, and 1 restores the old behavior. The limit was added upstream to stop runaway self-dispatch (Subagents can still recursively self-dispatch with global task permission (v1.17.20) anomalyco/opencode#36878), so a bound stays.
  • general. It can launch subagents. explore keeps its read-only allow list and still cannot.
  • Nested launches run in the foreground. This holds even when the model asks for background: true. A child's job completes when its own run ends, so a backgrounded grandchild would report back to the child after the child had already answered its parent. The root got the interim text and the real answer reached nobody. Running nested launches in the foreground keeps every ancestor busy until its descendants finish, so each answer includes their results. Parallel nested calls in one step still run concurrently, and top-level sessions keep background mode. The tool description says this.
  • mcpx leases. A nested subagent's lease names its parent's lease ID (mcpx-sub-…) instead of the parent's raw session ID, so the daemon can link the chain past depth 1.
  • Docs. Updated the agents, permissions and tools pages, plus the V1 config page. I also regenerated the OpenAPI documents. They were stale on main: shell_timeout was missing, and the services/www copies lagged the protocol document, which made check:generated fail.

Left alone:

  • After a restart, the TUI discovers only a root's direct children. data.test.tsx "syncs direct child session info with a navigated root" pins this.
  • ACP resets child tracking every turn.
  • Affinity headers are one hop.

None of these blocks nesting; they are follow-ups if they matter.

How did you verify your code works?

  • bun run script/test.ts test/tool-subagent.test.ts test/agent.test.ts from packages/core: 22 pass. test/tool-mcpx-features.test.ts: 13 pass. The new nested-foreground and mcpx-lineage regressions failed before their fixes.
  • Full core suite with the mcpx mask: 7003 pass, 23 skip, 2 fail across 7028 tests in 297 files. The two failures also fail on main: execute supports callable namespace tools (pretty JSON expectation) and the VCS untracked-file spawn count.
  • bun typecheck in packages/core and bun run check at the root: pass (36/36 tasks).
  • services/www: bun typecheck, bun run check:generated and bun run build pass, including link validation.
  • Live run: an isolated server from this branch ran in simulation mode, with a scripted model answering over the Drive backend protocol. Only model output was scripted. The root launched general ("Level one"). That subagent was offered subagent, and its own general ("Level two") ran a shell command that waited for permission. All three sessions stayed running. After the reply, each level finished with its answer.

Screenshots / recordings

The same live run drove OpenChamber: evidence.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR (the OpenAPI regeneration is noted above)

Summary by CodeRabbit

  • New Features
    • Subagents can now launch nested subagents in the foreground, up to five levels below a top-level session by default.
    • Configure the nesting limit with experimental.subagent_depth: 0 prevents subagent launches, and 1 prevents further nesting.
    • The built-in general agent can launch subagents; explore cannot.
  • Documentation
    • Updated agent, tool, and configuration guides to explain nested launches, depth limits, and built-in agent permissions.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4bd8913a-0d2c-4a33-a787-c87486c38303
📥 Commits

Reviewing files that changed from the base of the PR and between 78c63c6 and cca5571.

📒 Files selected for processing: 14
  • packages/core/src/plugin/agent.ts
  • packages/core/src/tool/plugin/mcpx-subagent.ts
  • packages/core/src/tool/plugin/subagent.ts
  • packages/core/test/agent.test.ts
  • packages/core/test/tool-mcpx-features.test.ts
  • packages/core/test/tool-subagent.test.ts
  • packages/protocol/openapi.json
  • packages/schema/src/config/experimental.ts
  • packages/web/src/content/docs/config.mdx
  • services/www/openapi.json
  • services/www/public/openapi.json
  • services/www/src/docs/content/agents.mdx
  • services/www/src/docs/content/permissions.mdx
  • services/www/src/docs/content/tools.mdx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📝 Walkthrough

Subagents can now launch nested foreground subagents by default, up to a depth limit of five. Nested session initialization resolves parent lease IDs to child session IDs. The OpenAPI configuration schema also adds an optional experimental shell timeout.

Changes: Nested Subagents

Layer / File(s) Summary
Depth settings and documentation
packages/schema/src/config/experimental.ts, packages/web/src/content/docs/config.mdx, services/www/src/docs/content/agents.mdx, services/www/src/docs/content/tools.mdx
The documented default depth changes to five. The documentation describes the effects of values 0, 1, and 5, and states that nested launches run in the foreground.
Agent permissions and nested launch execution
packages/core/src/plugin/agent.ts, packages/core/src/tool/plugin/subagent.ts, packages/core/test/agent.test.ts, packages/core/test/tool-subagent.test.ts, services/www/src/docs/content/permissions.mdx
The general agent can launch subagents but still denies questions; explore remains restricted. Nested launches use the foreground path, and tests cover default and configured depth limits.
Nested session parent resolution
packages/core/src/tool/plugin/mcpx-subagent.ts, packages/core/test/tool-mcpx-features.test.ts
Session initialization resolves a registered parent lease ID to its child session ID and uses the resolved ID in the lease and MCPX_PARENT_SESSION_ID. A test checks nested session links.

Shell timeout schema

Layer / File(s) Summary
Experimental shell timeout property
packages/protocol/openapi.json
The configuration schema adds optional experimental.shell_timeout, an integer with a minimum value of zero.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant SubagentTool
  participant initializeSubagentSession
  participant Lease
  SubagentTool->>SubagentTool: Check nesting depth and select foreground execution
  SubagentTool->>initializeSubagentSession: Initialize child session with parent lease ID
  initializeSubagentSession->>Lease: Resolve lease ID to childSessionID
  initializeSubagentSession-->>SubagentTool: Return session with resolved parent ID
Loading

Suggested reviewers: rekram1-node

Merge Risk: ⚪ Minimal · up to cca55

Nested subagent results remain deliverable even when a session is explicitly backgrounded. No identified issue prevents merging after normal checks.

Security architecture risk: 🟡 Moderate · up to cca55

Deeper delegation can make configured privileges reachable through an intermediate profile even when the initiating profile cannot access them directly. Session-wide restrictions still apply, but configurations relying on profile-specific restrictions need reassessment.

Retained concerns

  • Medium · security · inferred: The new default delegation path can reach a configured identity that an ancestor profile cannot launch directly. For example, an ancestor that allows general but denies another target only in its own agent rules can now reach that target through general, provided general permits it and no inherited session restriction blocks it. Each launch checks the immediate caller, and the selected descendant executes with its own policy rather than an ancestor-policy intersection. This composition predates the PR and is documented, but its reachability through general is newly enabled. Exposure depends on configured target privileges; this is not evidence of a session-wide denial bypass.
Security review details

Security Blast Radius

  • inferred — The newly reachable authority is bounded by configured non-primary target identities and inherited session controls. Model-controlled target selection can reach those identities' permitted tools and MCP capabilities through additional delegation hops. The inspected evidence does not establish cross-tenant access, new credentials, or infrastructure-level privileges; maximum external exposure depends on the configured targets and daemon enforcement.

Security Findings and Attack Paths

  • inferred — A conditional attack path is an influenced session launching an allowed general child, followed by that child selecting a more privileged configured target. An ancestor-only target denial is not rechecked at the second hop. The path fails if inherited session permissions deny the target, general's own delegation rules block it, the target is primary-only, or the depth limit prevents the launch. No deployed configuration demonstrating exploitation was supplied.

Trust Boundaries and Controls

  • observed — Delegation remains an explicit identity transition authorized against the immediate caller and selected target. Parent existence, direct-child continuation ownership, primary-target exclusion, and inherited session permissions remain enforced. These controls constrain the new path but do not make profile-specific policies transitive.

Resilience and Maintainability Implications

  • inferred — Recovery remains a coverage gap rather than an established new security failure. Restart enumerates suspended root sessions and persisted background children, while ordinary nested launches now use foreground jobs. Whether root replay reconstructs every interrupted nested generation without losing results or repeating sensitive effects was not proven. The underlying recovery topology predates this PR.

Hardening Proposals

  • proposed — For deployments requiring family-wide restrictions, place target and sensitive-action denials in inherited session policy or explicitly constrain every delegating profile. Add a policy-composition test covering an ancestor that permits general but denies a privileged target, so the intended transitive authority contract remains explicit.
  • proposed — Validate a multi-level chain across interruption, explicit backgrounding, and process restart, checking descendant cancellation, lease cleanup, result ownership, and repetition of externally visible effects.
Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly summarizes the main change: allowing subagents to launch nested subagents.
Description check Passed The description covers the issue, change details, rationale, verification results, evidence, and checklist. It also documents known unrelated test failures and follow-up limitations.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 7…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 7 files. (5 skipped: 5 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation, regression coverage, and documentation are consistent, with no unresolved blocking findings.

No open issues 🎉

What changed in this PR

Enables nested subagents while preserving bounded execution and reliable result delivery to parent sessions.

Changes:

  • Raises default nesting depth to five and allows general to launch subagents.
  • Forces nested launches into foreground execution and links mcpx parent leases.
  • Updates regression tests, documentation, and generated OpenAPI data.
File Description
services/​www/​src/​docs/​content/​tools.mdx Documents nested foreground execution.
services/​www/​src/​docs/​content/​permissions.mdx Updates general permissions.
services/​www/​src/​docs/​content/​agents.mdx Explains nesting limits and agent capabilities.
packages/​web/​src/​content/​docs/​config.mdx Corrects configuration placement and default.
packages/​schema/​src/​config/​experimental.ts Updates depth configuration description.
packages/​protocol/​openapi.json Adds missing shell_timeout schema.
packages/​core/​test/​tool-subagent.test.ts Covers nesting, foreground execution, and limits.
packages/​core/​test/​tool-mcpx-features.test.ts Tests nested lease lineage.
packages/​core/​test/​agent.test.ts Checks built-in subagent permissions.
packages/​core/​src/​tool/​plugin/​subagent.ts Implements depth and foreground behavior.
packages/​core/​src/​tool/​plugin/​mcpx-subagent.ts Links leases through parent lease IDs.
packages/​core/​src/​plugin/​agent.ts Allows general to launch subagents.

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants