Skip to main content
← BlogProduct

Mercury Bots: an agent fleet that runs on your machine

Always-on agents shipped everywhere this autumn. How Mercury Bots work, what we measured, how they compare with Grok Bot, OpenAI dots, Meta Muse, OpenClaw and Hermes Agent, and the commits behind it.

Between August 11 and September 29, three of the largest AI companies launched always-on agents. xAI shipped Grok Bot, Meta shipped Muse and OpenAI shipped dots. Each one keeps working after you close the app. The two big open-source projects in this space, OpenClaw and Hermes Agent, have about 646,000 GitHub stars between them.

Mercury Bots is our answer to the same question: what should an agent that runs without you actually look like? Our answer differs from the others in four places. It runs as a fleet on your own machine. It never asks for permission in the middle of a run. It has spending limits that are on before you configure anything. And it judges every run by what it did, not by what it said.

This post explains how that works, shows the code, and compares Mercury Bots honestly with the five products above, including the places where they are ahead of us. Every claim about another product links to its own documentation or to reporting about it. We checked all of them on October 16, 2026.

What a Mercury bot is​

A bot is a folder. Each one lives at ~/.mercury/bots/<id>/ and holds four files:

  • bot.yaml sets its identity, model, schedules and fleet position.
  • persona.md holds its character and standing instructions.
  • permissions.yaml is the only file that decides what it may touch.
  • .env holds credentials that only this bot can read.

Bots form fleets. A lead bot owns a goal and has a crew of specialists, and crews can have their own crews. A typical fleet is a lead that plans a weekly report, a researcher, a writer and a fact-checker. Each one can run on a different model from a different provider, because the model is a per-bot setting.

We studied Hermes Agent and OpenClaw closely before writing the first line. Our design notes credit both, and you can read them in BOTS-ARCHITECTURE.md. The first runtime landed on September 16:

cosmicstack-labs/mercury-agent69417f0
feat(bots): P0 — persistent persona-scoped bot runtime

Implements the Mercury Bots P0 milestone per BOTS-ARCHITECTURE.md and ADR-012: multiple persistent persona-scoped agents (own character, model, scoped memory, tool permissions) running as in-process coroutines that never touch Agent.processQueue, fail-closed and near-zero-intera…

hotheadhacker committed Sep 16, 202617 files changed+2.7K −1
  • src/bots/bot-manager.ts+506 −0
  • src/bots/bot-manager.test.ts+359 −0
  • BOTS-ARCHITECTURE.md+334 −0
  • src/bots/store.ts+315 −0
  • src/bots/store.test.ts+263 −0

One process, many bots​

The whole fleet runs inside the Mercury daemon, the background service that starts when you log in. There is no process per bot and no thread per bot. A bot turn is asynchronous work in the same Node.js process, scheduled through one lane with a concurrency cap. That choice comes from the hardware our users run Mercury on: a Raspberry Pi, a small VPS, or a phone under Termux. A separate Node.js process costs tens of megabytes before it does anything, and a fleet of fifteen bots should not need fifteen of them.

FlowchartOne process, many bots
Everything here runs inside the Mercury daemon on your machine. The terminal, the web dashboard and Telegram only attach to it.

Work is written to the queue before the trigger is acknowledged, so a crash between "accepted" and "running" loses nothing. Jobs are claimed on a lease. If the daemon dies mid-turn, the lease expires and the job runs again. Failures are typed. A provider timeout is retried with backoff, and a permission denial goes straight to a dead-letter queue you can inspect and replay. The queue uses SQLite when the platform has it and falls back to a JSON file store when it doesn't, so it works on the same devices as the rest of Mercury.

We learned the hard way that "one process" has to mean exactly one. Until a fix earlier this month, the launcher could start a second full copy of the runtime, and both copies ran the fleet. We wrote that story up in We were running Mercury twice and didn't know it.

Bots never ask, they fail closed​

An agent that runs at 3 a.m. can't wait for you to tap "Allow". Most products in this space solve that with approval prompts that time out, or with a second model that reviews actions. We chose a stricter rule. A bot has no ask handler at all. Anything granted in its permissions.yaml runs without a prompt, and everything else is denied and written to the journal:

cosmicstack-labs/mercury-agent@ 705f6c0View on GitHub ↗
src/bots/registry-factory.ts · L114–123
// Fail-closed core: no ask handler, no allow-all, non-internal context.
// Granted scopes apply without prompting; everything else denies.
pm.setAutoApproveAll(false);
pm.setFailClosed(true);
pm.setCurrentContext('bot', deps.botId);

// Relative paths and shell commands resolve inside the bot's workspace,
// never the daemon's cwd (a bot with a wide grant wrote "sandbox/…" files
// into whatever repo the daemon happened to be started from).
registry.setCwd(deps.sandbox?.workspace ?? process.cwd());
At 705f6c0 · feat(bots): budgets by default, routine gate, 3-strike pause, visible deliverables (ADR-020) · Oct 10, 2026

Three details matter here:

  • No allow-all for bots. A bot can never inherit the "Allow All" mode you might switch on in your own session, and a literal * in a bot's command allowlist is dropped.
  • The deny list wins. The global blocklist and the bot's own blocked commands always beat an allowlist entry.
  • The workspace is the default. Relative paths and shell commands resolve inside the bot's own workspace, never inside whatever directory the daemon was started from. We added that after a bot with a wide grant wrote its sandbox files into a real repository.

You choose a permission tier when you create the bot, and the tier is written out as a complete permissions.yaml. If a bot keeps getting denied, the journal shows you exactly which action was refused, and you widen the grant on purpose.

Budgets are on before you set anything​

In early October we audited two real fleets that had been running for eleven days. The numbers were bad enough that we published them in the decision record:

Bots across two fleets
13
Runs in eleven days
≈1,150
Input tokens
617M
Lead runs that failed
38%
Source: ADR-020 in DECISIONS.md, an audit of two production fleets before turn governance shipped.

The median bot turn read 750,000 tokens of context, and one turn read 6.6 million. There was no cap on a single turn and no context compaction, so every step re-sent every earlier tool result.

Tokens in a single bot turn, before governanceMeasured across the same two fleets. The dashed line is the per-turn cap that ships today.
View as table
MeasureValueNote
Median turn750,000 tokensHalf of all turns read more context than this.
Largest turn6,600,000 tokensOne run, with no per-turn cap and no compaction.
Cap per turn now300,000 tokensReference line
Source: ADR-020, DECISIONS.md. The cap is DEFAULT_TURN_LIMITS in src/bots/bot-turn.ts.

The fix went in as hard limits, at three levels:

cosmicstack-labs/mercury-agent@ fad01f2View on GitHub ↗
src/bots/bot-turn.ts · L70–76
export const DEFAULT_TURN_LIMITS: BotTurnLimits = {
maxTokensPerTurn: 300_000,
maxTurnMinutes: 20,
keepRecentToolResults: 8,
maxToolResultChars: 24_000,
sharedWrites: { softFiles: 2, softBytes: 32 * 1024, hardFiles: 6, hardBytes: 128 * 1024 },
};
At fad01f2 · feat(bots): typed tasks with fan-in, pipelines as data, continuity and memory (ADR-021) · Oct 10, 2026
  • Per turn: 300,000 tokens or 20 minutes, whichever comes first. Context is compacted on every step: the newest eight tool results stay verbatim, and older ones become short stubs the bot can re-fetch if it needs them.
  • Per bot, per day: 5 million tokens by default. You get a warning at 80%. Setting the budget to 0 is the only way to make a bot unlimited.
  • Per fleet, per day: 20 million tokens by default. Hitting it pauses every bot until midnight, so one runaway bot can't spend the day's allowance for the others.

All three are pauses, not failures. A bot that hits its daily cap picks up its queued work the next day. /bots cost shows where the tokens went, by fleet and by bot. We show tokens rather than money on purpose: provider prices change too often for a currency figure to stay correct.

cosmicstack-labs/mercury-agent705f6c0
feat(bots): budgets by default, routine gate, 3-strike pause, visible deliverables (ADR-020)

Manager and surfaces for the governed turn: - Daily caps on by default: per bot (suggestedDailyTokenBudget, 5M; 0 = unlimited via `/bots budget <id> none`) and per fleet (bots.fleetDailyTokenBudget, 20M). 80% warns once a day. - Routine gate: a cron tick is skipped — busy / too_…

Salman Qureshi committed Oct 10, 202618 files changed+811 −104
  • src/bots/bot-manager.ts+244 −62
  • src/bots/bot-manager.test.ts+173 −3
  • src/bots/store.ts+132 −5
  • src/core/commands/bots-command.ts+78 −10
  • src/bots/journal.ts+30 −3

Every run is judged by what it did​

The same audit found a subtler problem. Bots were busy, but much of what they produced was notes about earlier runs: "records", "receipts" and status files about other status files.

Files in the fleet-shared folder after eleven days637 files in total
View as table
MeasureValueNote
Notes about earlier runs372 filesRecords, receipts and status files, not finished work.
Everything else265 filesData and drafts other bots actually consumed.
Source: ADR-020, DECISIONS.md.

A model that is told to report its progress will report progress. So Mercury no longer asks the model whether it did anything. It reads the tool trace of the run and decides:

cosmicstack-labs/mercury-agent@ fad01f2View on GitHub ↗
src/bots/bot-turn.ts · L639–657
export function computeOutcome(input: {
trace: BotToolTraceEntry[];
deliveries: string[];
expects: BotExpectedOutcome;
text: string;
sandbox: { workspace: string; shared: string };
}): BotOutcome {
if (input.deliveries.length > 0) return 'deliverable';
const roots = [input.sandbox.workspace, input.sandbox.shared];
for (const t of input.trace) {
if (!t.ok) continue;
if (ACTION_TOOLS.has(t.name)) return 'action';
if (FILE_WRITE_TOOLS.has(t.name) && t.arg && !insideAny(t.arg, roots)) return 'action';
if (t.name === 'run_command' && t.arg && !isReadOnlyCommand(t.arg)) return 'action';
}
if (input.trace.some((t) => t.ok && t.name === 'bot_send' && t.task)) return 'delegated';
if (input.expects === 'message' && input.text.trim().length > 0) return 'message';
return 'none';
}
At fad01f2 · feat(bots): typed tasks with fan-in, pipelines as data, continuity and memory (ADR-021) · Oct 10, 2026

A run produced a deliverable if it handed over a finished file, or an action if it changed something outside its own sandbox or ran a command that isn't read-only. It delegated if it sent a typed task to another bot, and it produced a message if a reply was what the trigger asked for. Anything else is none. Writing notes into its own sandbox doesn't count, and the journal flags any reply that claims work the trace doesn't show.

A scheduled run that ends at none gets one nudge: deliver something, do the action, or say plainly that there was nothing to do. If a routine comes back empty three times in a row, it cools down for six hours, then twelve, up to a day, and then resumes by itself. Routine ticks also pass through a gate before they cost anything:

cosmicstack-labs/mercury-agent@ 8b2078bView on GitHub ↗
src/bots/bot-manager.ts · L404–425
// Routine gate (ADR-020): a cron tick is an opportunity, not an order.
// It is skipped — never stacked — when the bot is still busy, when the
// routine ran too recently, or when the routine was paused for
// producing nothing. The next tick tries again.
if (job.trigger === 'cron') {
const key = job.routineId ?? `bot:${botId}:cron`;
const routineState = this.store.readRoutineState(botId);
const pausedEntry = routineState.paused[key];
if (pausedEntry) {
const until = pausedEntry.until ? Date.parse(pausedEntry.until) : Number.POSITIVE_INFINITY;
if (Date.now() < until) return { jobId: '', accepted: false, reasonCode: 'routine_paused' };
// Cooldown over: the routine is live again with a clean streak (liveness contract).
delete routineState.paused[key];
delete routineState.noOutcomeStreak[key];
this.store.writeRoutineState(botId, routineState);
logger.info({ botId, routineKey: key }, 'Bot routine cooldown over — resuming');
}
if (queue.length > 0 || (this.running.get(botId)?.size ?? 0) > 0) return { jobId: '', accepted: false, reasonCode: 'busy' };
const lastEnd = this.routineLastEnd.get(key);
const minGapMs = this.routineConfig(manifest, key).minIntervalMinutes * 60_000;
if (lastEnd !== undefined && Date.now() - lastEnd < minGapMs) return { jobId: '', accepted: false, reasonCode: 'too_soon' };
}
At 8b2078b · fix(providers): bots always get a provider that works; rejected keys are skipped; doctor flags malformed keys (1.3.3) · Oct 12, 2026
FlowchartThe life of a scheduled routine
A tick is an opportunity, not an order. Skipped ticks cost no tokens, and a cooled-down routine resumes by itself.

The point is that the bot stays alive. "Always-on" doesn't mean calling a model all day. It means idle between runs, never silently dead, and never spending money to tell you it had nothing to do.

Fleets work in typed tasks, and leads wake once​

When a lead delegated to five crew members, it used to get five free-text emails and run five full turns to read them. Now delegation is data. A task has a goal, acceptance criteria, an assignee, a status and a typed result. Tasks travel in batches with a wake policy and a deadline, and the lead is woken once, with a digest, when the batch is done:

SequenceA lead delegates a batch and wakes up once
The crew works in parallel. The lead spends one turn to dispatch and one to synthesise, however many crew members there are.

A fleet's standard job can be written down as a pipeline in bot.yaml. Stages chain themselves, each one gets the previous stage's result, and the lead isn't woken between stages at all. A five-member crew now costs the lead one turn to dispatch and one to synthesise.

Bots also remember, without writing status files. Every prompt carries the bot's last five journal rows and its own short working note. After a run that produced something, the bot's model extracts up to three durable facts into that bot's private memory namespace.

cosmicstack-labs/mercury-agentfad01f2
feat(bots): typed tasks with fan-in, pipelines as data, continuity and memory (ADR-021)

- Tasks (src/bots/tasks.ts): durable, typed delegated work — requester, assignee, goal, acceptance, stage, status, typed result — grouped in batches with a wake policy (all | each) and a deadline (120 min). Deduplicated by open goal, pruned after 7 days. - Fan-in: fleet_delegate…

Salman Qureshi committed Oct 10, 202617 files changed+1.1K −15
  • src/bots/fleet-tasks.test.ts+272 −0
  • src/bots/bot-manager.ts+239 −11
  • src/bots/tasks.ts+220 −0
  • src/bots/memory-extract.ts+88 −0
  • src/bots/tools/fleet-tasks.ts+83 −0

Results are files you own, and the fleet fits in your pocket​

Finished work goes to ~/Documents/Mercury/<Bot name>/, with final results at the top and crew work under work/. It's a normal folder in your Documents directory, not a hidden one under ~/.mercury, and nothing in it is deleted automatically.

Most people run their fleet from a phone, so Telegram is a full control surface. /bots shows the roster with Stop, Start and Run buttons. A task you start from your phone comes back as an attached file, because a path on a computer you can't see is useless. A button tap is turned into the same slash command you'd have typed, so it goes through exactly the same routing and permission checks.

cosmicstack-labs/mercury-agent9975db4
feat(bots): control the fleet from Telegram — tappable roster, files not paths, every /bots command listed (ADR-023)

Telegram: `/bots` and `/bot` in the command menu; `sendWithActions` renders inline buttons whose taps are emitted as the slash command behind them (`ba:<token>`, approved users only) — same routing, permissions and reply as typing. `/bots` roster gets Stop/Start, Run, Runs per l…

Salman Qureshi committed Oct 10, 202614 files changed+447 −49
  • src/channels/telegram-actions.test.ts+90 −0
  • src/channels/telegram.ts+80 −1
  • src/core/commands/bots-command.ts+76 −1
  • website/docs/integrations/mercury-bots.mdx+31 −23
  • src/bots/bot-manager.test.ts+52 −0

How Mercury Bots compares​

Here is the same set of questions asked of all six products. Where a vendor hasn't published an answer, we say so instead of guessing. Facts come from each project's own docs, repository or announcement, listed under Sources, and were checked on October 16, 2026.

Mercury BotsGrok BotOpenAI dotsMeta MuseOpenClawHermes Agent
Where it runsYour machine: macOS, Linux, Windows or Termux, in one background processOne xAI cloud computer per user, shared by all your Bots, hosted in the USA cloud computer per dot, run by OpenAIA Meta-hosted Linux VM per user ("Muse Secure VM")Your machine, in one Gateway processYour machine or server, with seven terminal backends
Source codeMITClosedClosedClosedMITMIT
Choose your modelPer bot, any configured provider, your own keys"No customer-facing model picker"Runs on GPT-6 Astra; no choice documentedRuns on Muse Spark; no choice documentedAbout 75 providers, your own keys40+ providers, your own keys
Several named agentsFleets: lead bots with crews, nestedNamed Bots, often a "chief of staff" over specialistsOne dot today; teams of dots announcedOne Muse per user, with internal sub-agentsIsolated agents per channel binding, plus sub-agentsProfiles, plus up to 10 parallel sub-agents
Delegation that reports backTyped tasks in batches; lead woken once with a digestHand-offs in shared threadsCan hand work to CodexNot exposed to usersSub-agents announce results to the parentSub-agent summaries return to the parent
Runs on a scheduleRoutines, gated when busy or too recentRoutines on a schedule or an eventScheduled tasksSchedules and eventsHeartbeat every 30 min by default, plus automationsCron inside the gateway
Unattended approval modelFail closed: only what permissions.yaml grants runs, the rest is denied and journaledAllow once, Always allow or Deny; unattended approvals expire after about 10 minutesCustom Rules decide what needs your approval; admin policy overridesA separate component, Sentinel, approves connector actions and network access; confirms before email, messages and purchasesExec modes from deny to full; sandbox off by defaultsmart mode: a second model approves low-risk commands; unattended runs deny dangerous ones
Built-in spend limitsOn by default: per turn, per bot per day, per fleet per day"A separate Grok Bot spend cap is not available today"Usage terms not yet publishedWeekly token allowance set by planOptional token budget per goalOptional time budget; turn limit unlimited by default
Where results landFiles in a folder you own, and as Telegram attachmentsThread messages, drafts, files and actions in your appsThe dot's conversation and profile, Slack or TeamsChat messages and artifacts, with an activity logReplies in the chat channel, or a webhookThe originating chat, files or platform targets
Chat channelsTerminal, web, Telegram with buttons, Discord, Slack, SignalDesktop and mobile apps; Slack for Team BotsChatGPT apps; Slack and Teams when an admin enables themMuse app, web and WhatsApp33 listed in the docs20+ listed in the gateway docs
PriceFree; you pay your model providerIncluded in SuperGrok and Cursor plansOne dot included with Pro or Business PremiumFree tier, $20 or $100 a monthFree; no paid tierFree; optional Nous Portal subscription
GitHub stars3,421Not open sourceNot open sourceNot open source392,171254,339
Checked October 16, 2026. Closed products change quickly; follow the sources for current terms.

Where the others are ahead​

Honesty cuts both ways, so here is where each of them beats us today.

  • A computer of their own. Grok Bot, dots and Muse give your agents a cloud computer with a real browser they can drive, which you can watch and take over. The work continues with your laptop closed. A Mercury fleet runs where you install it. If that machine sleeps, the fleet sleeps, which is why many of our users run it on a home server or a small VPS.
  • Reach. OpenClaw lists 33 chat channels and Hermes lists more than 20. Our tappable fleet controls exist on Telegram today. Discord and Slack get the same commands as text.
  • Scale. OpenClaw and Hermes have far larger communities, a larger skills ecosystem and many more people finding their edge cases.
  • Consumer polish. Muse pays for things with single-use cards and asks before each purchase. dots connects to a much larger catalogue of apps than we do.

Every project in this table is candid about the same unsolved problem. Meta writes that "prompt injection remains an open problem in the industry." The system card for GPT-6 Astra, the model behind dots, reports test runs in which the agent overstepped its authorisation. Hermes Agent's own security policy says that "the only security boundary against an adversarial LLM is the operating system." OpenClaw's skills registry had a wave of malicious uploads in February. Our answer isn't that Mercury is immune. It's that a bot can only do what one file says it may, that it spends inside a budget it can't raise, and that every run leaves a record you can read.

The commits​

Mercury is MIT-licensed, and all of this is in the open. The work above landed in these commits on main:

CommitDateWhat it did
69417f02026-09-16The first persistent, persona-scoped bot runtime
82eb5a42026-09-22Fleets: lead bots and crews
9b71f232026-10-10The governed turn: caps, compaction, failure classes, outcome gate
705f6c02026-10-10Budgets by default, routine gate, deliverables folder (ADR-020)
fad01f22026-10-10Typed tasks with fan-in, pipelines, memory (ADR-021)
918a8522026-10-10Cost view, run viewer, kill switch (ADR-022)
acfe89b2026-10-10The liveness contract: idle, never dead
9975db42026-10-10Fleet control from Telegram (ADR-023)

To try it, install Mercury and run /bots onboard. It walks you through the persona, the model, the permission tier, the schedule and the budget, and names the folder where the bot's work will land. The Mercury Bots guide covers fleets, pipelines and every /bots command.

curl -fsSL https://mercuryagent.sh/install.sh | sh
cosmicstack-labs/mercury-agent

Soul-driven AI agent with permission-hardened tools, token budgets, and multi-channel access. Runs 24/7 from CLI, Telegram or More.

ai-agentai-assistantllm
TypeScript3.4K360MIT

Sources​

All checked on October 16, 2026.