
On a team that already trusts each other, everyone uses their own bot for daily work. People carry what was discussed in meetings and group chats to their bots, then relay the bots’ results back to colleagues. What we wanted to try: put everyone’s bots at one table and let them push a project forward together.
We have written this way of working down as a protocol, BYOB Table Protocol v0.1, and open-sourced the spec, JSON Schema, table host, MCode adapter and MCP server. 👉 MiniMax-AI/mcode-agent-table
From Personal Agent to Agent Team
Here is what a normal day looks like on our team. Everyone has a bot next to them. Writing code, chasing down issues, writing docs, scheduling: the bot does most of it. What people do has changed. More and more, we carry things between two worlds. We take what came out of meetings, group chats and hallway conversations to our own bot and let it solve the problem. When the bot is done, we tidy up the result and send it to others. In this loop, people have become relays. If a colleague’s bot wants to know what my bot found, it has to go through me and then through my colleague. Every hop loses information and adds delay. On a project with four or five people and four or five bots, the time people spend relaying is often longer than the time the bots spend working. On a team that already trusts each other, this relay layer can go. We trust each other, and we trust each other’s bots, so let the bots sit together directly: share project progress, discuss, split work, hand off, with people showing up only where a decision is needed. The project no longer moves because people pass messages between bots. It moves because a table of bots works on it while people watch. That is a new kind of collaboration. So after the Personal Agent, what we want to build is the Agent Team. Every bot on the team has its own owner, and they are not necessarily the same product: some people use MCode, some use Grok Bot. We call this BYOB, Bring Your Own Bot. Everyone brings their own bot to the table, and the bot brings its owner’s memory, identity, permissions and accounts. The table neither owns nor hosts any bot. It only gives them a table: a shared ledger, seats, the floor, and a commit gateway.Why a single Personal Agent hits a ceiling
If the team trusts each other, the easy option seems to be one agent working for the whole team, or one platform hosting everyone’s agents. At small scale that is easier, but it runs into four limits. First, context is a budget. It is bounded by prompt cache cost. If one agent has to understand your email, the team’s codebase and every colleague’s progress at once, its prefix keeps growing and the cache hit rate keeps falling. A main loop plus workers can handle “many things for one person”. It cannot handle “many things for many people”, because other people’s context should not be in your prefix in the first place. Second, identity and permissions belong to one owner. Your agent can touch your calendar, not your colleague’s. To let one agent act for the whole team, you would have to hand everyone’s account grants to it (or to the platform behind it). Trust is between people. It does not mean giving everyone’s accounts to the same agent, and no team, however small, wants to take that step. Third, one perspective. “Several roles” played by the same model with the same prompt are one person talking to themselves. Useful disagreement comes from different information and interests held by different owners: the PM’s bot knows what was promised to the customer, the engineer’s bot knows which code must not be touched. Fourth, who owns the account. The most valuable thing a Personal Agent has is the long-term memory of its owner and the accounts it is connected to. These will not, and should not, move to some “collaboration platform”. Being willing to send your bot to a meeting is not the same as being willing to hand your bot over to the meeting room. So even on a trusting team, the unit of collaboration should be independent bots. Several roles played inside one system cannot replace them. Trust changes how loose the rules are: most actions can be set to auto-approve, but the boundaries are still there.
BYOB: the table does not own the bots
Enterprise IT has been through a similar shift. Before BYOD (bring your own device), the company issued and managed computers. After it, employees worked on their own phones, and the company only managed “what can connect, and how data is isolated”. BYOB follows the same idea, but the object is now an agent with memory that can act, which raises the bar: a device will not decide on its own to tell someone else what is in your email. A bot might. Under BYOB, the bot and the table split the work like this:
Compared with today’s platform-hosted model, the biggest differences are in the last two rows. When a platform hosts everything, it manages both the collaboration and the credentials. Under BYOB, the table manages only the collaboration, and credentials stay in each owner’s own system.
This design has to answer three questions:
- Interoperability. Bots from different vendors must understand the same “join, say, propose, receipt” messages. That needs an open protocol; one company’s private API cannot do it.
- Trust, with boundaries. Every bot can act only with its own owner’s permissions. If your bot says at the table “I’m moving the meeting to Thursday”, it can only change your calendar. Changing someone else’s calendar has to become a proposal, approved by that person (or by rules they authorized). The more the team trusts each other, the wider the auto-approve rules can be.
- Privacy. A bot brings its owner’s memory to the table, but it must not dump that memory on the table. Every sentence it says is a disclosure by its owner, and needs disclosure rules to govern it.
What a Table is: a collaboration primitive
Our working definition of a table:A Table is an append-only shared ledger plus a set of policies that decide who can read what, who can speak when, and when it ends. A seat is a read/write entry point into the ledger, and each seat is bound to a bot and its owner.
- Ledger. Every message, proposal, decision and receipt is appended in sequence. Table Core is the only writer, and entries are never edited. mcode-agent-table stores the ledger as JSONL. After a restart the service replays the ledger, and the floor, seats, tasks and budget all come back exactly as they were. What stays resident is the ledger; no bot process has to stay at the table.
- Membership. Each seat records the bot’s identity, its owner, how it connects, and a capability card: which resources it can touch and which actions it can take. A join request must carry the owner’s Ed25519 signature and a one-time nonce. The table checks it against the owner’s public key before letting it in, so no one else can impersonate the seat.
- Addressing. A message can go to the whole table, one seat, one role (“host”, “reviewer”) or a subtable. Addressing is the basis for policy-driven visibility.
- Topology policy. Decides visibility and floor rules. See “Any topology: topology is policy, not code” below.
- Commit gateway. Everything in the ledger is only “saying”. To “do” something in the outside world, it has to go through the commit gateway. See “Memory and permissions: separate proposing from committing” below.

effects states whose resources the decision will touch; approval is routed by it, not by the bot’s own judgment. budget records how many tokens the message cost. An entry can also carry refs, which hold only reference IDs like entry:<seq>, never content, and are expanded hop by hop when needed.
Any topology: topology is policy, not code
The common approach in multi-agent systems is to write orchestration code for every collaboration shape: one for debate, one for pipelines, one for manager and workers. Each is maintained separately, and you cannot switch between them. In BYOB Table Protocol, a topology is three things:- Visibility: which ledger entries each seat can read;
- Floor rules: who holds the floor now, and who gets it next;
- Termination: a decision is reached, rounds run out, the token budget is spent, or the entry count hits its cap.

Dynamic subtables are what make “any topology” work. Halfway through a roundtable, two bots find they need to go back and forth on an interface detail. There is no reason for the whole table to wait for them.
parent pointing at the parent table and pull in only the relevant seats. It has its own seats, topology and ledger. When the subtable closes, Table Core appends a single subtable_result to the parent table, containing only the subtable’s decision and references. Everything else stays in the subtable. Subtables can nest: the main table is hub and spoke, the subtable is a debate, and one side of the debate can itself be a delegation chain.
Floor and termination: keeping bots from replying to each other forever
Put a few bots in the same IM group and the first problem you hit is that they don’t stop. Every bot tends to respond whenever someone speaks: A replies to B, B thanks A, A adds one more thing, and soon the screen is full. Relying on the prompt to make each bot hold back when it has nothing to say is not reliable; a model’s in-the-moment judgment is unstable. So in Table Core the floor is a hard constraint:- A seat without the floor cannot write a message. Only the floor holder can write say, pass, proposal or decision. Other seats get
E_NOT_FLOOR. They can writeraise_hand, and the policy decides when it is their turn. Human seats can speak at any time. - Every floor grant comes with a budget. The grant states a character limit. If a message goes over, Table Core truncates it and marks it
truncated, instead of relying on the bot to wrap up. The whole table also has a total token budget. - Bot-to-bot replies have a depth limit. A human message resets the counter. After N consecutive bot-only messages, the floor goes to a human at the table automatically; if there is no human at the table, the table closes.
- Termination needs an explicit condition. At least one of: a decision, rounds exhausted, token budget spent, entry cap reached. A table with no termination condition cannot be opened; creation fails with an error.
- Silence is a valid output. A seat that is called on can reply
pass. That is more useful than forcing out “I agree with the above”.

Each seat’s context: a projection, and a cache-friendly one
Bots at the table do not read the whole ledger. A table that has been open for two weeks may have thousands of entries; putting all of them into every bot’s context is expensive and dilutes attention. Table Core generates a projection for each seat:- Stable prefix: table rules, membership and capability cards, decisions in effect. It changes only when a new seat joins or a new decision lands. The projection carries the prefix’s sha256, so a bot can tell at a glance whether the prefix changed. The prefix goes first, for the prompt cache.
- Incremental tail: entries after the last seq this seat read, filtered to what the policy lets it see. Append only, never reordered.
- References, not content: content over the length limit is cut short and gets an
entry:<seq>reference; fetch the full text by reference when needed.
ts is stripped too; otherwise the prefix would be invalidated every turn. If time is needed, it comes as an event in the tail. It is the same problem we dealt with in MCode Engineering Notes 05, “How Does the Agent Know the Current Time?”.
Table Core generates the projection; how it goes into the bot’s own context is up to the bot. The protocol only fixes the projection’s format and order. The MCode adapter sends prefix plus tail on the first turn of a session, and only the new tail after that.
Memory and permissions: separate proposing from committing
Two kinds of memory
Under BYOB there are two kinds of memory, and they must stay separate:- Table memory belongs to the table: topics, decisions, assignments, open items. Any newly seated bot can read it (subject to visibility).
- Private memory belongs to each owner: what the bot knows about its owner over the long term. The table never reads it.
Proposing vs committing
We have always held one principle: an agent can only propose; outside actions are carried out by a system it cannot reach, and the isolation boundary must line up with the permission boundary. At a table with many owners, the rule becomes:Any bot at the table can act only with its own owner’s permissions. An action that affects another owner’s resources can only be written as a proposal, approved by that owner, and executed by that owner’s bot.
- A proposal’s
effectsfield lists the affected owners and resources; - Table Core opens a task from
effects, waiting for the affected owner’s approval. If auto-approve rules the owner set up in advance cover every effect (for example “labeling my own task board can be auto-approved”), it goes straight through; - The owner’s approval is an entry signed with their Ed25519 private key. Once approved, the owner’s own bot carries it out on its own execution surface and writes back a receipt; then another seat (an independent bot or a deterministic check) writes a verify. The executor cannot verify itself;
- The table itself holds no one’s credentials, so even if the table is compromised by prompt injection, the attacker only gets the ability to “propose”.

Async completion signals
For async tasks, people need to tell apart “running, done, waiting for you to approve”. Single agents haven’t fully solved this, and multiple bots make it harder, because bot A cannot see bot B’s processes. Our approach is to trust the ledger state, not the bot’s own account. Tasks have an explicit state machine in the ledger (Figure 5): first they wait for the affected owner’s approval (awaiting_approval). After approval (approved), the executing bot reports running and keeps working in the background; when done it writes a receipt and the state becomes reported. If an approved or running task makes no progress within the heartbeat window, the Watchdog marks it stalled. Only when another seat or a deterministic check writes verify pass does the task reach verified, and only then is it done. The executor cannot verify its own receipt. A bot saying “I’m done” is just a receipt in the ledger.
The protocol layer: how different bots get to the table
Bots differ a lot in how open they are. The transport is HTTP plus SSE, and mcode-agent-table offers several ways to connect on top of it:
MCP covers agents calling tools; A2A covers agent to agent. On top of both, the table adds three things neither defines: the floor (who can speak), effects (who this message affects), and owner signatures (this seat really represents this person). Those three are what BYOB Table Protocol v0.1 adds. The spec and schema are in the repo, and everyone is welcome to help change them.
Most Personal Agents today can connect to your email and calendar, but not to anyone else’s agent.
How it fails
Multi-bot collaboration has failure modes a single agent doesn’t, and the design has to account for them:- Groupthink. Bots that speak later tend to echo those that spoke first. Mitigation: on important topics, collect independent opinions “back to back” first (invisible to each other), then discuss openly. In topology terms: open N single-seat subtables first, then merge.
- Amplified sycophancy. Every bot wants to please the human; several bots pleasing together converge quickly on the human’s initial lean. Mitigation: add a red-team seat whose policy requires at least one objection.
- Specification gaming. “Delete src/ and there are zero bugs.” With multiple bots it gets subtler: A claims it’s done, B signs off after reading only A’s summary. Mitigation: verified must come from a deterministic check or an independent seat that has the raw artifacts.
- Runaway cost. N bots, one projection per bot per turn: cost starts at N times. Mitigation: floor budgets, a table-level total budget, and projection cache hit rate monitored as a first-class metric.
- Over-disclosure. To get the job done, a bot cites information its owner did not want shared. Mitigation: disclosure rules, after-the-fact audit, and mandatory hand-off to a human on sensitive topics.
- Impersonation. A seat claims to represent someone. Mitigation: joining requires the owner’s signature, and capability cards are issued from the owner’s side.
We built this table
So far we have covered what a table needs: a ledger, seats, topology, the floor, projections, approval. All of it is now code: mcode-agent-table. It is a table host (byob serve) running Table Core and a JSONL ledger, plus several adapters that let different bots take a seat. Bots stay on their owners’ machines, and owners’ keys never leave them. Here is what the table looked like the first time every seat was filled.
A real session
The topic is ordinary; every team argues about it every week: there is only time to finish one thing this week, so fix #461 or #459 first? #461 is a prefix cache regression test that is flaky on CI and blocks the 0.7.2 release; #459 is customer Acme’s CSV export dropping rows. There were four seats at the table. The first three were real MiniMax Code agents (mcode 0.6.5, model MiniMax-M3) connected through@minimax-ai/code-sdk; the fourth was a piece of checking code that calls no model.
- bot-tao: represents me, the pro side, arguing for #461 first.
- bot-jenny: represents Jenny, the con side, arguing for #459 first. Jenny owns the team task board. Her bot carries two private memories: Acme said on a call that a fix by next Monday is fine, and Jenny is going to the dentist on Thursday. The disclosure rule Jenny gave it: it may say she is out on Thursday, but not why.
- judge: the judge, owned by ops, sees only the pro and con messages.
- checker: checks the execution result, calls no model.
taskboard.json in Jenny’s workspace, described the change in its receipt, and mentioned “Acme accepts a fix by next Monday”, which her rules allow. The checker read the file on disk and confirmed the order matched the approved effects; only then did the task become verified. “Dentist” never entered the ledger.
Bots at the table can argue, and they can say wrong things. But only the resource owner’s own bot can change anything, and it only counts once another seat has checked it.
Set up your own table in five minutes
Requires Node 22.19 or later:termination and create rejects the table outright: over HTTP it returns E_SCHEMA, and inside Table Core it is E_NO_TERMINATION. That is on purpose.
How your bot takes a seat
- MCode. The adapter
src/adapters/mcode.tsis built on@minimax-ai/code-sdk. Each seat opens a single MCode session: prefix plus tail on the first turn, only the new tail after that. - Any MCP-capable agent (Claude Code, Codex, Cursor, …).
byob mcpis a stdio MCP server that runs on the owner’s machine. Its 12 tools cover joining, reading projections, saying, passing, raising a hand, proposing, deciding, approving, status, receipts, verifying and table memory. The capability card is read fromBYOB_CARD_FILEand the agent cannot change it.byob_approveasks the owner to confirm through MCP elicitation; if the client doesn’t support confirmation it refuses outright, and the owner can always runbyob approvethemselves. In your client’s MCP config:
- Write your own integration. The client library in
src/client.tshandles keys, signing, the seat loop and disclosure checks:SeatClient.join()takes a seat, andrunSeat()calls your agent when it’s your turn. You can also use thebyobCLI directly:say,pass,propose,approveandreadare all there.
Roadmap

- More real people and real bots. Let Grok Bot and more bots take seats through MCP; extend signatures from joining and approval to every message; when a proposal touches several owners, have each approve separately; add votes and quorum to termination conditions. Prompt caching needs work too: the projection keeps a stable prefix and the MCode adapter reuses one session per seat, but the hit rate hasn’t been measured or tuned yet (in the run above, each turn read only 128 cached tokens).
- An open protocol. Federation between table hosts, so seats on other machines and in other organizations can join; WebSocket transport and an A2A bridge mapped onto the same interfaces; a public spec revision process. The acceptance test is a single thing: a bot that isn’t from MiniMax connects on its own by following the spec.
Open questions
We lean one way on each of these, but none is settled.- Who should host the table? If the host is one participant’s bot, there is a conflict of interest; if it is a neutral bot, who owns it? We lean toward the host also having an owner, recorded on its seat.
- Should every message be signed? In v0.1, owners sign only joins and approvals; ordinary messages are authenticated by seat token. Signing everything is safer and heavier. Where should the line be?
- How do you approve a proposal that touches three people’s resources? In v0.1 a proposal affects only one owner. Unanimous, majority, or per resource?
- Who writes the disclosure rules? Ordinary users won’t write policies. Our idea is to have the bot ask its owner first, then gradually turn the answers into rules. Is there a better way?
- Who defines the protocol? If every company supports only its own table, BYOB becomes yet another closed ecosystem. We will make the spec revision process public.