Skip to content

feat(unstable): Intial RFD and schema for subagents - #1992

Merged
benbrandt merged 12 commits into
mainfrom
vbr/subagents-rfd
Sep 30, 2026
Merged

benbrandt merged 12 commits into
mainfrom
vbr/subagents-rfd

Conversation

@Ololoshechkin

Copy link
Copy Markdown

Add subagents RFD

@Ololoshechkin
Ololoshechkin requested a review from a team as a code owner August 20, 2026 00:39
@nikita-ashihmin

Copy link
Copy Markdown

Implementation feedback from AIR and the reference adapter branches:

The canonical capability and lifecycle shape works for both adapters. We only needed _meta.jetbrains.air.capabilities = ["nativeSubagentSessions"] as a temporary negotiation bridge because the released SDKs currently remove the draft subagents fields. We treat this metadata as a compatibility bridge, not as an alternative protocol. It may help to make SDK preservation of the draft fields an explicit rollout prerequisite.

Two edge cases still seem underspecified:

  1. The RFD defines orphan recovery during session/load, but it does not define the immediate Client state when a live connection ends or session/prompt returns without the required terminal update. Clients can otherwise choose failed, keep running forever, or show disconnected. Could the RFD require a local disconnected state for an unknown outcome and clarify whether the Client emits no wire update?

  2. A child can own pending session/request_permission or session/create_elicitation requests when the child or its parent is cancelled. Could the RFD require those requests to resolve or cancel before subagent_state_update, and define how a Client handles a response that races with the terminal update? This prevents a terminal child from retaining a live control or moving that control to the wrong history.

One smaller compatibility note: the current RFD requires task. AIR now treats the older description field only as a temporary input fallback. New adapters emit task.

@Ololoshechkin

Copy link
Copy Markdown
Author

@nikita-ashihmin please check again :)

@benbrandt

Copy link
Copy Markdown
Member

I would like to keep the RFD + protocol changes in separate PRs because it is easier to accept an RFD without having to do rounds on the Rust code at the same time to get a merge

@benbrandt benbrandt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is going in the right direction overall, but still have several questions I think we need to resolve.

Thanks for kicking this off!

Comment thread agent-client-protocol-schema/src/v1/client.rs Outdated
Comment thread agent-client-protocol-schema/src/v1/client.rs Outdated
Comment thread docs/rfds/subagents.mdx
Comment thread docs/rfds/subagents.mdx Outdated
Comment thread docs/rfds/subagents.mdx Outdated
Comment thread docs/rfds/subagents.mdx Outdated
Comment thread docs/rfds/subagents.mdx Outdated
Comment thread docs/rfds/subagents.mdx Outdated
Comment thread docs/rfds/subagents.mdx Outdated
Comment thread agent-client-protocol-schema/src/v1/client.rs Outdated
Ololoshechkin and others added 3 commits September 16, 2026 01:26
Address review feedback from #1992:

- Merge subagent_spawned and subagent_state_update into one upsert-style
  subagent_update following the v2 entity pattern; add explicit running state
- Make name, task, and capabilities optional; only subagentSessionId required
- Drop the Agent-side capability: the Client capability alone gates the updates
- Drop the per-child close capability; session/close is not valid for child IDs
- Stop implying children inherit the parent's execution context
- Allow SDKs to buffer updates for unannounced child sessions
- Explain why the local disconnected state cannot wait for late updates
- Add FAQ entries for naming, capability, and disconnected-state choices
- Define v2 support: capability-free, v2 patch semantics, open state enum

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gent_update

Replace SubagentSpawnedUpdate and SubagentStateUpdate with a single
SubagentUpdate where only subagentSessionId is required and omitted or
null fields mean unchanged. Add a running state to SubagentState, drop
the per-child close capability, and remove the agent-side subagents
session capability.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirror the v1 subagent types into v2 with v2 conventions: no capability
(unknown updates flow into OtherSessionUpdate), MaybeUndefined patch
semantics, and an open SubagentState enum that preserves unknown values.
Register subagent_update in the known-discriminator guards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Ololoshechkin

Copy link
Copy Markdown
Author

Thanks for the thorough review, Ben! Pushed a rework addressing all threads (182be75, ee65f9c, a06ecf2):

  • subagent_spawned + subagent_state_update are now a single upsert-style subagent_update following the v2 entity pattern (only subagentSessionId required; explicit running state)
  • Agent-side capability and per-child close are gone
  • Execution context is no longer implied to be inherited
  • SDKs may buffer updates for unannounced children
  • The v2 schema now carries the same type: capability-free, v2 patch semantics (null clears), open state enum — plus a new “Subagents in ACP v2” section in the RFD

On splitting RFD and protocol changes: happy to do that — I'd keep this PR as the RFD and move the schema/Rust changes into a follow-up PR once you're happy with the shape. Say the word and I'll split it.

@nikita-ashihmin heads-up: the wire shape changed (single subagent_update, optional fields, no per-child close), so the adapter branches will need a small migration. Your three earlier points survived the rework: SDK preservation is still an explicit rollout prerequisite, the local disconnected rule and the pending-request race handling are unchanged (now with added rationale), and task remains the canonical field — but note it is now optional, so fallback code shouldn't assume it is always present.

Separate reusable child sessions from individual tool-call operations.

Add non-owning session references, object cancellation capabilities, and
work-state reporting for both protocol versions. Define ownership, routing,
best-effort replay, and provider cost semantics in the RFD and draft guides.

Regenerate unstable schemas and add serialization and compatibility tests.
@benbrandt benbrandt changed the title Add subagents RFD feat(unstable): Intial schema and RFD for subagents Sep 25, 2026
Preserve main's RFD status categories and retain subagents in Draft.
Regenerate schema artifacts from the merged Rust definitions to include
both main's capabilities and the subagent session model.
@benbrandt benbrandt changed the title feat(unstable): Intial schema and RFD for subagents feat(unstable): Intial RFD and schema for subagents Sep 25, 2026
The v1 client only uses Usage in the subagent work-state payload.
Require both unstable_subagents and unstable_end_turn_token_usage so
usage-only feature combinations do not fail CI with warnings denied.

Validated all 95 depth-two feature configurations with CARGO_BUILD_WARNINGS=deny.
Replace tool-call session references with message upserts and chunks, with optional participant metadata and mirrored v2 child state snapshots.

Align new v1 patches with tri-state semantics and refine subagent recovery and documentation.
Shorten the v1 and v2 schema summaries, remove redundant nesting guidance, and keep the v2 protocol guide self-contained.

Regenerate reference documentation and add Ben Brandt as RFD co-author.
@benbrandt
benbrandt enabled auto-merge (squash) September 30, 2026 10:42
@benbrandt
benbrandt merged commit d2631c6 into main Sep 30, 2026
14 checks passed
@benbrandt
benbrandt deleted the vbr/subagents-rfd branch September 30, 2026 10:46
@acp-release-bot acp-release-bot Bot mentioned this pull request Sep 30, 2026
@agourakis82

Copy link
Copy Markdown

Real-world confirmation this works end to end, from an independent (non-JetBrains) client, in case it's useful signal for moving this RFD along.

We're building a multi-agent client that juggles several ACP agent sessions at once (Claude, Codex, Kimi, each over SSH/stdio) and wanted exactly this: a standard way to see an agent's own subagent/Task dispatch instead of flattened tool_calls with vendor-specific _meta (e.g. claudeCode.parentToolUseId).

Found clientCapabilities.subagents has no effect yet against claude-agent-acp@0.84.0 — the published SDK schema doesn't carry unstable_subagents, so it gets silently dropped (same root cause as agentclientprotocol/claude-agent-acp#1195). Declaring the AIR back-channel instead:

{ "clientCapabilities": { "_meta": { "jetbrains": { "air": { "version": 1, "capabilities": ["nativeSubagentSessions"] } } } } }

does work today and produces exactly the shape this RFD describes — tested dispatching two parallel Task subagents against a real claude-agent-acp process:

{"sessionUpdate": "subagent_spawned", "subagentSessionId": "...", "name": "Contar até 3", "task": "...", "capabilities": {}}
{"sessionUpdate": "subagent_state_update", "subagentSessionId": "...", "state": "completed"}

So the design works in practice; what's missing for a non-AIR client is just the canonical field reaching a published SDK build. Not asking for anything specific here beyond leaving this as a data point — happy to help test once unstable_subagents is exposed, or to open a focused issue if that's more useful than a PR comment.

@benbrandt

Copy link
Copy Markdown
Member

This is in progress and the adapters need to be updated

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.

4 participants