Shared agents: the moment a second person can see it, your model of the user is wrong.
Drop a working single-user agent into a team channel and three assumptions fail in the same instant — that there is one intent to serve, one permission set to act under, and one person accountable for what it did. Teams design for the second one, because permission leakage is the failure that sounds like a breach. The one that actually kills adoption is the third: when an action is recorded as "the agent did it", nobody on the team owns it, and a shared agent that nobody owns settles at the trust level of its most sceptical member. Bind every run to one asking human, show that binding in the surface, and most of the rest follows.
Name the three assumptions you are breaking.
A single-user agent has a principal. It knows whose goal it is pursuing, whose credentials it is spending, and whose name goes next to the result. A shared agent — in a team channel, a shared document, a family device, a support queue with several agents on it — has an audience instead, and an audience is not a principal.
- Intent is contested. Two people in the same thread can want opposite things, and the more recent message is not the more authoritative one. "Cancel that" from a colleague who joined the thread ten seconds ago is not the same instruction as "cancel that" from the person who started the run.
- Authority is heterogeneous. Everyone in the room can see the agent, and they do not all have the same access to what it can reach. The agent's capability set is now a function of who is asking, which single-user designs never have to represent.
- Accountability is unassigned. An action logged as the agent's is an action with no human next to it. This is the assumption people forget to break, and it is the one that turns "the agent is useful" into "nobody wants to be the one who let it run".
This is not the same problem as multi-tenancy, which is about keeping separate customers isolated. Here everyone is legitimately in the same space and the isolation you need runs within a tenant — between colleagues who can all see each other, which means the enforcement point cannot be the workspace boundary.
Every run has exactly one human principal, and it is the asker.
The single most load-bearing decision in this design is to refuse the ambient reading of a shared space. A run is not "the channel's" run. It belongs to the person whose message started it, that binding is fixed for the life of the run, and every downstream call carries it.
- Permissions are the intersection, never the union. The agent may do what it is scoped to do and what the asker is permitted to do, and nothing else. The tempting failure is a service account that holds the union of everything the team can reach — it makes the demo work and it makes the agent a privilege-escalation device that any member can invoke. See agent identity and permissions.
- Read scope follows the asker too, not just write scope. If Alice cannot open the salary spreadsheet, the agent must not summarise it for her because Bob linked it in the channel last week. Retrieval over shared context is the quiet leak, and it never looks like an access-control decision at the moment it happens.
- Mid-run instructions from a third party are proposals, not commands. They may be surfaced to the principal; they may not silently redirect the run. The pause/steer/abort vocabulary in interruption and handoff is the right model, with one addition: who is allowed to use each verb.
- Give abort to more people than steer. Anyone in the room should be able to stop a run they can see going wrong; almost nobody should be able to quietly change its goal. These are asymmetric for a reason — stopping is safe and reversible, redirecting is neither.
- Escalations go to the principal, not to the channel. An approval request broadcast to a room is an approval request addressed to nobody, and it will be answered by whoever is least loaded rather than whoever is responsible. The deadlock analysis in async agent UX gets worse, not better, with more people watching.
Attribution collapse is the failure that ends the pilot.
Ask a team six weeks into a shared-agent deployment what went wrong, and you rarely hear about a leak. You hear that nobody is sure who asked for the thing that got sent, that two people redid the same work, and that a decision in the thread cannot be traced to a person who intended it. The audit log usually has the answer. That does not help, because the failure is in the surface, not in the storage.
- Render the principal in the message, every time. "Drafting the customer reply — for @alice" costs one line and removes the ambiguity at the moment it would otherwise form. An agent message with no human name attached is an orphan the moment it scrolls.
- The artifact carries the attribution, not just the chat. The ticket comment, the calendar invite, the commit, the outbound email — whatever leaves the shared surface must say on its face which human it was acting for. Chat history is where attribution is looked for; artifacts are where it is needed.
- Claim work visibly to prevent duplication. In a shared space two people will ask the same thing within a minute of each other. An agent that announces "already working on this for @bob, started 40s ago" prevents the duplicate run and, more importantly, prevents two divergent results landing in the same thread.
- Make the receipt a first-class object. What was asked, by whom, what was done, under whose authority, and what it touched — one linkable record per run. Decision receipts and audit trails give you the mechanism; the UX requirement is that a team member can reach it in one click from the message, without asking an administrator.
- Trust in a shared agent is set by its least-trusting observer. Anyone who can see the agent act can veto it socially, and one uncomfortable action in front of an audience costs more than several private ones. This is the multiplier that makes designing for trust stricter here than in a private tool.
Decide when the agent may speak, and default to invited.
A shared agent has a floor-holding problem that a private one does not. Respond to everything and it owns the channel and trains people to move the real conversation elsewhere. Respond only when explicitly named and it misses most of the work it could have done. This trade-off is real and there is no setting that avoids it, so make it a deliberate, visible, per-space choice rather than a hard-coded default.
- Explicit invocation is the correct default. Start narrow. A shared agent that is trusted enough to be invited more often is a much better position than one being muted.
- Offer, then act. Between silent and speaking there is a low-cost middle: an ephemeral, single-person suggestion ("I can pull those numbers — want me to?") visible only to the person it is offered to. It gathers the signal about when unprompted help is welcome without spending the channel's attention.
- Threads are the containment unit. Keep the run's chatter in a thread and post only the outcome to the channel. This single convention does more for shared-agent tolerability than any amount of prompt tuning.
- Silence needs a status surface. A long run that says nothing is indistinguishable from a broken one, and in a shared space several people will each poke it. One updating status message beats a stream of progress posts — the artifact-as-status-surface argument from async agent UX applies directly.
- Per-space settings, not per-user. The unit people reason about is the room. Letting each member configure the agent's noisiness in a shared room produces an agent that behaves inconsistently in front of witnesses, which reads as unpredictability.
A shared context window is an injection surface, and the attacker has a badge.
The security model of a private agent assumes untrusted content arrives from outside — web pages, documents, emails. In a shared space, the content the agent reads is written by people who are legitimately inside, plus anyone they invited, plus anything pasted in from elsewhere. The blast radius of one crafted message is everything the agent can reach on behalf of whoever invokes it next.
- Channel history is untrusted input, delimited and labelled as such. The same discipline as prompt injection 101, with the uncomfortable difference that the person who wrote the text is on your payroll and may have pasted it in innocently.
- Guests, external members and forwarded threads are the sharp edge. Most shared workspaces allow external participants, and most teams forget which channels have them. If your agent reads a channel, enumerate who can write to that channel — the answer is routinely wider than anyone in it believes.
- Instructions never come from history; only from the current principal's message. Prior messages are evidence about the world, not directives. This is a parsing rule you enforce in code, because it is not one a model reliably enforces for you.
- Shared memory is poisonable and persistent. An agent that learns from the channel can be taught something false by one message and repeat it to the whole team for months. If you keep team-level memory, keep provenance on every entry and make it reviewable — see memory poisoning defenses.
- Exfiltration is one message away by construction. The agent can already post to the room. An injected instruction that gets it to summarise a private document into a shared channel needs no network egress and no vulnerability — it is the agent doing its job to the wrong audience.
Memory, and how you know it is working.
Shared agents accumulate context that nobody explicitly owns: preferences, conventions, prior decisions, in-jokes about how this team does things. That memory is the reason a shared agent gets better than a private one — and it is a governance object the moment a person leaves the team.
- Separate personal memory from space memory, and never merge them silently. What Alice told the agent about her own preferences is hers; what the team agreed about naming conventions belongs to the space. Writing the first into the second is a privacy incident that looks like a feature. The distinction in agent memory is the mechanism; here it is also a boundary.
- Space memory needs an owner, a review surface and a deletion story. Who can see what the agent has learned about this team, who can edit it, and what happens to it when a member leaves or the project closes. If the answer is "an engineer with database access", it is not a shared feature yet.
- Evaluate on the team, not the requester. Per-user satisfaction misses the whole failure class in this playbook. Track duplicate-run rate, share of runs with an identifiable principal, mute and opt-out rate per space, and the fraction of escalations answered by the actual principal rather than a bystander.
- Watch the bystander metric especially. When approvals are increasingly clicked by whoever is nearest rather than whoever asked, the accountability chain has already broken and the log still looks perfect.
- Test with an adversarial colleague, not an adversarial attacker. The realistic red-team here is a well-meaning teammate who pastes a customer email containing instructions, or an external guest in a channel nobody audited. Build that slice from real messages.
If you do one thing before shipping into a shared space, do this: make every run carry a single human principal, enforce permissions as the intersection of that person's access and the agent's scope, and print the name in the message the room can see. It fixes the leak people worry about and the attribution failure that actually ends pilots, and it costs one field on your run object. An agent in a room full of people is not a more powerful assistant — it is the same assistant with the question "on whose behalf?" now unanswered, and your job is to answer it in code and on screen.
Related: approval and confirmation UX for where the gates go, transparency and explainability for what the room needs to see, email and calendar agents for the most common shared-surface deployment, and shared memory and blackboards for the machine-to-machine version of the same contention problem.