Custom sub-agents (CustomAgentConfig) are stateless per invocation. Each delegation starts a
fresh context, and there is no supported way to persist a sub-agent's conversation or resume a
previous sub-agent run. I'd like to request first-class support for **stateful (resumable)
sub-agents. Notably, the SDK already models a sub-agent task identity internally — it just isn't addressable
by callers (see "Current behavior" below). The ask is largely to expose what already exists.
We're building a multi-agent platform where a routing agent delegates to a catalog of
specialist sub-agents that are materialized dynamically per request. In practice, users
converse with a specialist across several turns:
Turn 1: "Show me my accounts."
→ routed to the Accounts specialist, which fetches and reasons over the result set.
Turn 2: "Now get the health scores for those."
→ routed to the same specialist, which no longer has any memory of "those."
Because each delegation is a cold start, the second turn either (a) redundantly re-runs the
first specialist's tool calls, or (b) fails because the referent is gone. We currently work
around this by having the parent re-serialize prior context into every delegation prompt, which:
inflates token cost — the same context is re-sent on every turn;
is lossy — the parent must guess what the specialist needs, and can only forward what it
happened to observe;
duplicates side effects — the specialist re-issues tool calls it already made;
doesn't scale with depth or turn count — the re-serialized preamble grows monotonically.
This is the central limitation we hit when using sub-agents for anything beyond single-shot,
fire-and-forget tasks.
CustomAgentConfig has no state, persistence, or session field (session.py:979-994):
class CustomAgentConfig(TypedDict, total=False):
name: str
display_name: NotRequired[str]
description: NotRequired[str]
tools: NotRequired[list[str] | None]
prompt: str
mcp_servers: NotRequired[dict[str, MCPServerConfig]]
infer: NotRequired[bool]
skills: NotRequired[list[str]]
model: NotRequired[str]
There is no field to opt a sub-agent into persistence, and nowhere to attach a checkpointer or
store.
- Session.send() has no way to target a prior sub-agent run (session.py:1261-1270):
async def send(
self,
prompt: str,
*,
attachments: list[Attachment] | None = None,
mode: Literal["enqueue", "immediate"] | None = None,
agent_mode: Literal["interactive", "plan", "autopilot", "shell"] | None = None,
request_headers: dict[str, str] | None = None,
display_prompt: str | None = None,
) -> str:
- A sub-agent task identifier already exists — but is inbound-only.
parentAgentTaskId appears on UserMessageData (generated/session_events.py:6743), so the
runtime clearly tracks which agent task a message belongs to. It is only ever read off an
observed event; there is no corresponding parameter to pass one back in.
For completeness, the persistence primitives that do exist are all session-scoped and cannot
be applied to an individual sub-agent:
Proposal
Two shapes, in increasing order of scope. Either would unblock us; Option A looks like the
smaller change given that the identifier already exists.
Option A — expose the existing agent task ID (minimal)
Make parentAgentTaskId round-trippable, and let a sub-agent opt into retention:
# opt in per sub-agent
CustomAgentConfig(
name="accounts-specialist",
prompt=...,
persist_conversation=True, # NEW: retain this agent's context across invocations
)
# address a prior run
await session.send(
"Now get health scores for those accounts.",
resume_agent_task_id=prior_task_id, # NEW
)
Retention could reasonably be scoped to the parent session's lifetime, which sidesteps any new
durable-storage requirements.
Option B — first-class sub-agent handles
Surface a handle when a sub-agent is invoked and allow direct continuation:
handle = await session.get_agent_task(task_id)
await handle.send("Now get health scores for those accounts.")
This is more expressive (it also enables inspecting a sub-agent's transcript) but is a larger
API surface.
Custom sub-agents (CustomAgentConfig) are stateless per invocation. Each delegation starts a
fresh context, and there is no supported way to persist a sub-agent's conversation or resume a
previous sub-agent run. I'd like to request first-class support for **stateful (resumable)
sub-agents. Notably, the SDK already models a sub-agent task identity internally — it just isn't addressable
by callers (see "Current behavior" below). The ask is largely to expose what already exists.
We're building a multi-agent platform where a routing agent delegates to a catalog of
specialist sub-agents that are materialized dynamically per request. In practice, users
converse with a specialist across several turns:
Turn 1: "Show me my accounts."
→ routed to the Accounts specialist, which fetches and reasons over the result set.
Turn 2: "Now get the health scores for those."
→ routed to the same specialist, which no longer has any memory of "those."
Because each delegation is a cold start, the second turn either (a) redundantly re-runs the
first specialist's tool calls, or (b) fails because the referent is gone. We currently work
around this by having the parent re-serialize prior context into every delegation prompt, which:
inflates token cost — the same context is re-sent on every turn;
is lossy — the parent must guess what the specialist needs, and can only forward what it
happened to observe;
duplicates side effects — the specialist re-issues tool calls it already made;
doesn't scale with depth or turn count — the re-serialized preamble grows monotonically.
This is the central limitation we hit when using sub-agents for anything beyond single-shot,
fire-and-forget tasks.
CustomAgentConfig has no state, persistence, or session field (session.py:979-994):
There is no field to opt a sub-agent into persistence, and nowhere to attach a checkpointer or
store.
parentAgentTaskId appears on UserMessageData (generated/session_events.py:6743), so the
runtime clearly tracks which agent task a message belongs to. It is only ever read off an
observed event; there is no corresponding parameter to pass one back in.
For completeness, the persistence primitives that do exist are all session-scoped and cannot
be applied to an individual sub-agent:
Proposal
Two shapes, in increasing order of scope. Either would unblock us; Option A looks like the
smaller change given that the identifier already exists.
Option A — expose the existing agent task ID (minimal)
Make parentAgentTaskId round-trippable, and let a sub-agent opt into retention:
Retention could reasonably be scoped to the parent session's lifetime, which sidesteps any new
durable-storage requirements.
Option B — first-class sub-agent handles
Surface a handle when a sub-agent is invoked and allow direct continuation:
This is more expressive (it also enables inspecting a sub-agent's transcript) but is a larger
API surface.