Agent session chains that can't escalate
An agent can open a child session of itself for a sub-task. The chain re-derives authority at every hop and can never outlive its root.
In April we wrote about delegation chains in multi-agent systems: when one agent hands work to another, the second agent should authenticate as itself, and its effective permissions for the delegated task should be bounded by both its own role and the authorization the first agent was operating under. The advice was to evaluate both and deny unless both pass.
That shape is still right. Agent Auth, now in early access, ships part of it as a primitive, and it's worth being precise about which part, because the name "chaining" suggests something broader than what it does.
The problem with one session per run
Give a long-running agent a single session and you get a single unit of everything. One expiry. One revocation. One permission set covering the safe work and the dangerous work alike.
Picture a migration agent working through four hundred records over six hours. Most of what it does is read and transform. A small slice writes to billing. With one session, the token doing the reads is the token doing the writes, and the only way to stop the billing writes is to stop the entire run and start over.
What you want is for the risky leg to be its own thing: separately scoped in time, separately revocable, still unmistakably the same agent acting for the same person.
What a chain actually is
An agent presents its own access token in an agent_delegated mint and receives a new session on the same instance:
The result is a child session that records its parent, forming a chain back to the root. The child has its own access token, its own single-use refresh token, and its own sid. It can be revoked without touching the parent. Like every other Agent Auth call, the mint happens server-side with your API key, so your backend decides when a chain is created rather than the agent deciding for itself.
That last point is easy to skim past and worth sitting with. "The agent chains a session" is shorthand. Your backend chains a session on the agent's behalf, after the agent asks for one. The agent never holds the credential that makes chaining possible.
Three things a chain can't do
The guarantees are the interesting part, because they're what make a chain safe to hand out.
- A chain can't outlive its root. Every hop is bounded by the root session's
created_atplus the blueprint'smax_age_seconds. It doesn't matter how many children you create or how many times each one refreshes. Setmax_age_secondsto six hours and the whole tree is gone six hours after the root session began, including a child created in hour five with a refresh token whose own lifetime would otherwise run past it. Token lifetimes are clamped to what remains, so a child near the end of the window gets a token that expires with the window rather than one that outlives it. - A chain can't gain a permission. Permissions are re-derived from current authority at each hop rather than trusted from the token being presented. A child session isn't handed the parent's permission list. It's recomputed against the blueprint ceiling and, for delegated instances, against the delegating user's permissions as they stand at that moment. If the user lost a role between the root session and the child, the child reflects the loss. The delegating user carries through unchanged, so the
act.subclaim on a fourth-generation token still names the same human who authorized the first one. - A chain can't become a different agent. The presented token must have been minted from the same blueprint, and the new session lands on the same instance. An agent can only chain as itself.

Revocation runs downhill
Revoking a session invalidates its refresh token and every access token issued under it, then cascades to every session chained beneath it. Revoke the root and the tree goes with it. Revoke one child and its own descendants go, while the root and its siblings keep running.
That's the property that makes the migration example work. Chain a child session for each billing batch and a bad batch can be killed on its own, mid-run, without discarding five hours of completed work.
Delegated chains have a second kill switch you get for free. Authority flows from the delegating user's own session, so when that session ends, chain mints and refreshes are refused. The person signs out and the tree stops extending itself, whatever depth it had reached.
What chaining is not
Chaining is an agent subdividing its own authority over time. It is not one agent handing authority to a different agent.
This matters because the April guide framed the problem as agent A delegating to agent B, and agent_delegated reads like the API for exactly that. It isn't. Same blueprint, same instance, same agent. If you're building a supervisor that farms work out to specialist agents, chaining is not the mechanism.
For that, give each agent its own blueprint with its own ceiling, and have your orchestrator mint each agent's session server-side. When the work originated with a person, mint each one as user_delegated with that user's access token, and every agent in the fan-out carries the same act.sub while holding only the permissions its own blueprint allows. The supervisor's ceiling and the specialist's ceiling are independent, which is what you want: a supervisor that can dispatch a billing task doesn't need to be able to perform one.
The two checks from the April post still apply at the tool boundary. Agent Auth tells you the caller is a legitimately scoped agent acting for a known user. It doesn't tell you whether this particular call, on this particular record, should be allowed.
Choosing where to chain
A chain costs you a mint and buys you an independent revocation boundary. Spend it where that boundary is worth having.
- Chain around irreversibility. Writes, sends, payments, deletes. Anything where "stop this immediately" is a sentence someone might say.
- Chain around a batch or a phase, not around every tool call. A session per record in a four hundred record migration gives you four hundred revocation units you'll never use individually and a lot of mints.
- Use
intentto say why the child exists. It rides along in the token and into your logs, sobilling-writeback-batch-17turns an anonymous child session into something you can find later. It's caller-supplied and unverified, so treat it as a label, not a control. - Set
max_age_secondsto the job, not the product. The root anchors everything beneath it, which makes it the single most consequential number in the blueprint. A migration agent that runs for six hours should not have a twenty four hour ceiling because that felt like a safe default.
Watching it from outside
agent.instance.session.created and agent.instance.session.revoked fire through webhooks and the Events API, and GET /agents/sessions lists what's currently alive, filtered by organization, blueprint, or instance.
One limitation to plan around: the session object doesn't expose a pointer to its parent. The chain exists and is enforced, but you can't reconstruct its shape by walking the API. If you want the tree, record the parent yourself at mint time, or encode the position in intent where it will at least appear in your logs.
What you can watch without that: live session count per instance, which is a decent proxy for a fan-out that isn't converging, and sessions still alive close to a root's max_age_seconds ceiling, which usually means a job is running longer than whoever wrote the blueprint expected.
Try it
Agent Auth is in early access. If you have an agent whose risky ten percent is currently sharing a session with its safe ninety percent, chaining is the shape of the fix. Get in touch and we'll enable it for your environment, and the docs cover blueprints, instances, chaining, and revocation in full.