Concepts
The four primitives of Meshfleet, and how they fit together.
Fleet
A fleet is a group of agents working on a related goal. Fleets are identified by a UUID and have a lifecycle: running → complete | failed.
You create a fleet by calling spawn_fleet with N agent specs. The fleet is "running" until all agents finish (each one completing with complete or failed). At that point the fleet itself transitions to complete or failed based on whether any agent failed.
const { fleet_id, agent_ids } = await callTool("spawn_fleet", {
agents: [
{ role: "Explorer", prompt: "Map the auth layer", agent: "codebase-onboarding-engineer" },
{ role: "Analyst", prompt: "Review architecture", agent: "oracle" },
{ role: "Engineer", prompt: "Implement a fix", agent: "backend-architect" },
],
}); Agent
An agent is a single opencode run child process. Each agent:
- Belongs to exactly one fleet
- Has a role label (free-form, for display)
- Has a prompt (sent to
opencode run) - Optionally uses a premade agent (a
.mdfile in.opencode/agents/) for specialized behavior - Has its own inbox for receiving P2P messages
- Reports its own status:
running→complete|failed
Premade agents
By default, opencode run uses the build agent (the standard OpenCode agent). To use a specialized personality, pass an agent field in the spec — Meshfleet passes --agent <name> to opencode run.
// Without premade: uses the default build agent
{ role: "Helper", prompt: "..." }
// With premade: uses the agent defined in .opencode/agents/frontend-developer.md
{ role: "Helper", prompt: "...", agent: "frontend-developer" } List all available premade agents with list_agents. Dynamically attach one to a running fleet with attach_agent.
P2P messaging
Agents don't have to be coordinated by you. They can send messages to each other through a shared inbox.
There are five message types, each with a different semantic:
| Type | Use it for |
|---|---|
handoff | Passing context to the next agent in a pipeline. Payload: { context, next_step, artifacts }. |
question | Asking a clarifying question. Payload: { question, context }. Use a correlation_id to link the answer. |
result | Reporting a final outcome. Payload: { summary, data, files_changed }. Usually addressed to the orchestrator. |
alert | Broadcasting a problem. Payload: { severity, error, agent_id, fleet_id }. Anyone in the fleet can respond. |
request_help | Escalating when stuck. Payload: { blocker, attempted, agent_id }. Targets a specific peer with relevant skills. |
Messages are appended to the recipient's inbox. The recipient polls get_inbox on their own schedule (no push). After processing, they ack_message to remove it from their inbox. The default payload cap is 64KB — large data should go in files, not payloads.
Capability routing
Instead of you deciding which agent gets which task, agents can advertise what they're good at and route_work can match.
An agent's capability is a record: { role, skills: string[], model?, context_window? }. When you call route_work("audit authentication security"), Meshfleet scores every registered capability by keyword overlap and returns the best matches.
Scoring is deliberately simple: it counts how many of the words in your description appear in the role name or skills. No embeddings, no external API. Fast, deterministic, and good enough for most cases.
When you spawn_fleet with a premade agent, Meshfleet auto-registers its capability by extracting keywords from the agent's frontmatter description. No manual register_capability call needed.
How the pieces fit together
The mental model is simple:
- Fleets are how you organize work.
- Agents are how work gets done.
- P2P messages are how agents coordinate.
- Capabilities are how agents advertise and you discover.
The orchestrator (you, or another AI agent) spawns fleets and sets policy. The agents inside the fleet self-organize. Meshfleet just keeps the trains running.