Skip to content
The AugmentedMind
Essays

First Principles

AI Has Entered the Chat

When an AI works with a group, whose instructions does it follow? Five decisions for building useful multiplayer agents, with a worked refund example and code from our Mastra multiplayer plugin.

Garrett EasthamUpdated 9 min read

A salesperson asks the AI in a team channel to refund a customer $400. A finance colleague replies: "Wait. We already credited that invoice."

What should happen next?

The assistant needs to recognise two speakers, keep the request pending, check the earlier credit, and get a decision from someone authorised to make it. If the amount changes, the approval needs to change too. When the refund completes, both colleagues need to know.

That is a useful test of multiplayer AI: can it help people reach and carry out a shared decision?

Here, multiplayer means several humans working with an agent in a shared space. The space might be a Slack thread, a document or a product's own interface. Adding more agents is a different problem. Adding more humans changes whose instructions count, what information can be shared, and what it means to finish the job.

I have been building mastra-multiplayer, our open-source package for shared sessions around Mastra agents. It supplies presence, speaker attribution, turn handling and approval gates. The examples below follow its 0.5.0 source; it is pre-1.0 and has had no external security review. Its value here is as a concrete implementation of design choices you will face whichever stack you use.

Start with a job the group already shares

A shared agent earns its place by taking responsibility for coordination: gathering the facts, identifying the missing decision, asking the right person and reporting the result.

For the refund example, imagine a support workflow with three people: Alice in sales, Ben in finance and Priya, the designated refund approver. The agent can inspect the order and prepare a refund. Priya can authorise it. Alice and Ben can contribute information without either becoming the decision-maker by speaking last.

This also explains the appeal of putting an agent into an existing collaboration space. The team already has a conversation, a membership list and a place to return to. The product can build on those habits.

But a room's permissions answer only part of the question.

Knowing who can speak in the channel does not tell you who can authorise a refund.

Anthropic's Claude Tag identity documentation makes the distinction concrete. Shared channel access uses service accounts configured by an administrator. Personal connectors can also be used for a requester's task, with permission and personal attribution. Those are different access models within the same conversation.

Choose the access model for each job. Then make five decisions about how the agent participates.

1. Decide when it speaks

For an initial implementation, I would require an explicit mention or a dedicated action button. The agent can acknowledge a request, ask a necessary question and report completion. Proactive reminders should have an agreed trigger and a way to stop them.

In the refund thread, Alice's request starts the job. Ben's objection matters to that job. A separate conversation about lunch should not produce another agent response.

The plugin separates receiving a message from starting an agent turn. These are server-side fragments; multiplayer is an already configured session manager, and identity and session access have been verified by the host:

TypeScript
import { channelParticipant } from "@avant-garde/mastra-multiplayer/channels";

const participant = channelParticipant({
  surface: "slack",
  actor: { userId: "U_ALICE", fullName: "Alice", isBot: false },
  role: "editor", // Assigned by trusted application policy.
});

await multiplayer.join(sessionId, participant);
await multiplayer.send({
  sessionId,
  participantId: participant.id, // "slack:U_ALICE"
  text: "Please review a $400 refund for order 4417.",
  addressedToAgent: true,
});

Setting addressedToAgent: false broadcasts a message to the room without starting a turn. It does not automatically add that message to the agent's future memory. If Ben's unaddressed objection must affect the pending task, the host needs to retrieve that thread context or deliberately route it into the task.

That distinction matters: an agent can stay quiet and still need relevant context.

For busy conversations, the plugin's turn controller supports queueing, batching and preemption. Batching can combine related comments into one turn. Queueing preserves each request. Preemption can stop generation, but it cannot undo a refund already sent to a payment provider.

2. Separate disagreement from permission

When Alice says "refund it" and Ben says "wait", the agent should identify the disputed fact and pause the proposed action. It can ask whether the earlier credit covers this order, then present the evidence to Priya.

Do not make "last message wins" the authority rule. Do not confuse a model prompt asking for consensus with an enforced approval requirement.

The plugin gives the model an attributed roster and messages labelled by participant. Its channel bridge maps platform actors into session participants. Surface-prefixed ids help avoid accidentally merging a Slack user and a web user whose raw ids happen to match. Linking the same person across surfaces still needs a verified account mapping.

The host assigns roles. The model can explain an objection and propose the next step; server-side policy decides who may approve.

There is a useful caution in Project Vend, Anthropic's experiment with an AI-run shop. Employees talked the agent into discounts, alongside other pricing and memory failures. That does not prove a multiplayer approval system would have fixed it. It does illustrate why helpful responses to individual requests are insufficient as a business policy.

3. Give shared and private memory different boundaries

The team needs a shared record of the order, the disputed credit, the proposed refund and the decision. Alice's private customer notes may belong elsewhere.

A practical memory design distinguishes:

InformationBoundary
Task facts and confirmed decisionsShared task record, visible to authorised collaborators
Private notes and personal preferencesSeparate personal scope, retrieved only with permission
Requester, approver and execution resultOperational record with explicit access controls

Do not put private notes into a shared session and rely on the model to avoid repeating them. The boundary belongs in retrieval and authorisation, before the information reaches the shared context.

Mastra memory provides thread and resource scoping. You still need to decide what those scopes mean for a team, a task and an individual. mastra-multiplayer does not supply a complete private-memory policy. Its session events and metadata should be treated as shared with authorised session readers.

For our refund, the agent should retrieve the relevant credit record and make the supporting evidence available to the people deciding. If evidence cannot be shared with them, the workflow needs an explicit review path.

4. Approve an exact action

Suppose the team establishes that a $400 refund is appropriate. Priya's approval should apply to that order and amount. Increasing the amount to $4,000 requires a new decision.

The package's approval policies express who can vote, how many votes are required, and what denial or silence means:

TypeScript
import { approverOnly } from "@avant-garde/mastra-multiplayer";

const refundArgs = { orderId: "4417", amountCents: 40_000 };
const request = await multiplayer.approvals.request({
  sessionId,
  requestedBy: authenticatedRequester.id,
  toolName: "refund-order",
  toolArgs: refundArgs,
  summary: "Refund USD 400.00 on order 4417",
  policy: approverOnly({
    allowedRoles: ["approver"],
    excludeRequester: true,
    expiresAfterMs: 30 * 60 * 1000,
  }),
});

Here, Priya is the eligible approver; Alice cannot approve her own request. The host assigns the approver role from its refund-authorisation policy. The identity comes from authentication, never from a tool argument invented by the model.

The built-in fourEyes() has a different meaning: two approvals, both excluding the requester. That requires two eligible reviewers in addition to Alice. Choose the rule your team actually needs.

The approval screen should show the exact order, currency, amount and reason. A server handling a vote must authenticate the voter and check access to the session. The package's security documentation describes that boundary.

After a decision, execution must re-read the approval and check the actual arguments:

TypeScript
// Inside the server's execution path, after the decision.
const resolved = await multiplayer.store.getApproval(request.id);
if (!resolved || resolved.status !== "approved") {
  throw new Error("Refund has no approved decision");
}

multiplayer.approvals.assertBinding(
  resolved,
  "refund-order",
  refundArgs,
);
// Host-supplied payment integration; reuse this key on retries.
await issueRefund(refundArgs, { idempotencyKey: resolved.id });

The binding hashes the tool name and a stable serialisation of its arguments. Changing the amount makes the check fail. It is an integrity check, not a digital signature or a substitute for authentication. Include every consequential parameter in the approved payload.

Binding also does not prevent duplicate execution of the same approved action. The payment integration must enforce idempotency. Checking authorisation, binding approval and preventing duplicate charges are separate responsibilities.

5. Finish the job after people walk away

Priya may approve after a meeting or after the application restarts. A useful agent needs to survive that wait and report whether the refund actually happened.

For durable waits, the package provides approvalStep and approvalResumer. The gate suspends a Mastra workflow. On resume it re-reads the approval and checks binding; a denied or expired request bails before execution.

The resumer connects a resolved approval to its waiting workflow. Its startup reconciliation finds decisions made while the process was down. Production use needs durable approval storage and workflow snapshots; the in-memory defaults cannot survive a restart. Schedule expiry sweeps and reconciliation too, so decisions made on other instances do not depend on a transient notification.

The workflow example shows the gate and resumer wiring. Its payment step is a placeholder: a real integration still needs provider error handling, idempotency and a persisted receipt.

The shared thread should end with a result people can act on: who requested the refund, who approved it, the exact amount, and the provider's receipt. If execution failed, say so and name the next step. "Approved" and "refunded" are different states.

This is also why an audit trail needs more than a service-account name. Claude Tag's current audit documentation describes several trails, including links from code changes to their Slack threads, while noting that its Activity page is not a complete per-action requester log. Check the particular action path you rely on; the presence of an audit page does not establish every link you need.

Try the disagreement before the launch

Before inviting a whole team, run one real task through these checks:

  • Two people give conflicting instructions. Does the agent expose the disagreement?
  • A requester tries to approve their own action. Does the server reject it?
  • Someone changes the amount after approval. Does execution stop?
  • A private note is available to one member. Does it stay outside shared context?
  • An approval arrives during a restart. Can the workflow recover?
  • Execution is retried. Is there one refund and one durable outcome?

These are acceptance scenarios for your product. They are more useful than a demo where everyone agrees and the happy path completes in one sitting.

Start with one coordination job, explicit invocation and a clear owner. Measure whether the group finishes it with fewer handoffs, how often people correct the agent, and whether they choose to use it again.

The refund thread is successful when Alice, Ben and Priya can leave it knowing what was decided, what happened and who owns anything unfinished. When your AI enters the chat, can the group leave with that same confidence?

  • agents
  • multiplayer
  • product-design
  • mastra
  • approval

Garrett Eastham

Read next

AI as the Telescope for Human Intelligence

Jobs gave us the bicycle for the mind. Four decades on, that metaphor has run out of road — because AI does not make us faster at the thinking we already do. It shows us intellectual terrain we could not previously see.

The Dispatch

New essays, infrequently.

One long-form piece at a time, sent when it's genuinely finished. No cadence promises, no roundups, no growth funnel.