Skip to content

Multi-agent crews

When several agents share a corpus, the value goes up — a reviewer benefits from what a deployer learned — and one specific mistake destroys it.

One bearer per member. Not one for the crew.

Section titled “One bearer per member. Not one for the crew.”

The tempting shortcut is a single credential for the whole crew. It ruins the two things a shared corpus is for:

Shared credentialPer-member credential
Audit says “the crew” did itSays which agent did it
Every member has the union of all scopesEach has only what it needs
One compromised member reaches everythingBlast radius is one member’s scope
Cannot revoke one agentCan
Release crewbearer Abearer Bbearer Cplannerread org:*reviewerread repo:paymentsdeployerread+write service:checkoutVectadyne

Give each member the narrowest scope, clearance and capability set that lets it do its job. A planner that reads broadly does not need write capability; a deployer with a narrow scope cannot be talked into reading another team’s memory.

The mistake that survives review: an orchestrator holds one client and every member calls through it, so every call authenticates as the orchestrator. The scoping above evaporates and the audit trail records one actor doing everything.

Two ways to keep identity attached:

A client per member. Simplest and most robust. Each member holds its own configured client.

One process, narrowed per call. Where a single process legitimately acts for several agents, narrow with an actor block:

vd.recall("deploy history", actor={"agent": "deployer", "scope_key": "service:checkout"})

Narrowing is enforced by rejection: a scope outside the caller’s authority is a forbidden fault, and a clearance that widens is refused rather than clamped. So this narrows a real credential — it does not manufacture authority.

Framework state is where identity gets lost

Section titled “Framework state is where identity gets lost”

In frameworks that carry conversation state between agents, the credential is easy to drop on a handoff — the next agent inherits the conversation and quietly reverts to whatever client the orchestrator holds.

Carry the member’s identity in session state, alongside the messages, and re-establish it on every hop. Assert it in a test: run a two-hop handoff and check the second call authenticated as the second agent.

Also watch what each member is shown. Some frameworks default to passing the whole prior transcript to the next agent, which quietly hands member B everything member A retrieved — including memory B’s own credential would not have been allowed to see. Restrict what travels to what the next member needs.

Share: conclusions, decisions, lessons, outcomes — written with evidence.ingest and scoped properly. That is the point of a shared corpus.

Do not share: intermediate scratchpads, plans, or transcripts. They are noise for every other member and they cost tokens forever.

Every write carries the agent that made it:

vd.client.call_tool("evidence.ingest", evidence_args(
agent="deployer", # not "crew"
items=[...],
))

“The deployer learned this” is worth far more later than “something in the crew learned this”.