Skip to main content

A2A Communication

Info

Last Updated: 2026-01-08

What is A2A Communication?

A2A (Agent-to-Agent) enables agents in separate memory spaces to communicate via dual-write messaging. Messages are stored in BOTH sender and receiver spaces—complete isolation with coordination.


Quick Start

// Send message (stored in BOTH agents' spaces)
await cortex.a2a.send({
from: "finance-agent",
to: "hr-agent",
message: "Budget approved for Q4 hiring",
importance: 85,
});

// HR agent receives it in their memory
// Note: getInbox() is planned - see /roadmap/a2a-communication-enhancements
// Current workaround: use memory.search() with filters
const inbox = await cortex.memory.search("hr-agent", "*", {
source: { type: "a2a", recipient: "hr-agent" },
limit: 10,
});

How Dual-Write Works

Dual-Write Message Storage
Sender
Finance Agent(finance-space)
Sends: "Budget approved"
Message Written to Both Spaces
Message in finance-space
Outbox record with metadata
Sender's copy
Message in hr-space
Inbox record with metadata
Receiver's copy
Receiver
HR Agent(hr-space)
Receives in inbox
Messages are stored in BOTH sender and receiver memory spaces
Why Dual-Write?

Each agent maintains complete isolation while still being able to coordinate. No shared state—just message passing.


A2A vs Hive Mode

FeatureA2A (Collaboration Mode)Hive Mode
Memory SpacesSeparate (one per agent)Shared (multiple agents → one space)
CommunicationExplicit messagingDirect read/write
IsolationComplete isolationParticipant tracking
Use CaseAutonomous agents, enterprisePersonal AI, MCP tools
Best ForFinance ↔ HR workflowsCursor + Claude sharing
When to Use Which
  • A2A: Autonomous agents needing complete isolation (enterprise, compliance)
  • Hive Mode: Multiple tools sharing user context (personal AI, MCP)

Communication Patterns

One-to-one messaging:

const result = await cortex.a2a.send({
from: "finance-agent",
to: "hr-agent",
message: "What is the Q4 headcount budget?",
importance: 85,
userId: "user-123", // Optional: if about a user
contextId: "ctx-456", // Optional: link to workflow
metadata: {
tags: ["budget", "q4"],
priority: "urgent",
},
});

console.log(result.messageId); // "a2a-msg-123"

Message Parameters

ParameterTypeRequiredDefaultDescription
fromstringYesSender agent/space ID
tostring | string[]YesRecipient agent/space ID(s)
messagestringYesMessage content
importancenumberNo60Importance (0-100)
userIdstringNoUser this message relates to
contextIdstringNoLink to context chain workflow
metadataobjectNoCustom data (tags, priority, etc.)
replyTostringNoMessage ID being replied to

Reading Messages

Convenience Methods Planned

The convenience methods getInbox(), getSent(), and markRead() are planned for a future release. See A2A Communication Enhancements for details. Use the workaround below for now.

// Get received messages (workaround using memory.search)
const inbox = await cortex.memory.search("hr-agent", "*", {
source: { type: "a2a", recipient: "hr-agent" },
limit: 20,
// Filter unread messages if needed (check metadata.unread or similar)
});

inbox.forEach(msg => {
console.log(`From: ${msg.metadata?.fromAgent || msg.source?.sender}`);
console.log(`Message: ${msg.content}`);
console.log(`Importance: ${msg.importance}`);
});

// Note: markRead() is planned - for now, you can track read status
// in your application or update message metadata if needed

Common Patterns

Delegation with Context

// Create workflow context
const context = await cortex.contexts.create({
purpose: "Process refund for order #789",
memorySpaceId: "supervisor-space",
userId: "customer-abc",
});

// Delegate via A2A with context link
await cortex.a2a.send({
from: "supervisor-agent",
to: "specialist-agent",
message: "Please handle the customer refund",
importance: 85,
userId: "customer-abc",
contextId: context.id, // Links to workflow
});

// Specialist can access full context
const ctx = await cortex.contexts.get(context.id, { includeChain: true });

Team Announcements

await cortex.a2a.broadcast({
from: "manager-agent",
to: ["agent-1", "agent-2", "agent-3"],
message: "New policy: Refunds over $1000 require approval",
importance: 90,
metadata: {
tags: ["policy", "announcement"],
policyId: "POL-789",
},
});

Request-Response

// Responder handles incoming requests
async function handleRequests(agentId: string) {
// Workaround: use memory.search() instead of getInbox()
const requests = await cortex.memory.search(agentId, "*", {
source: { type: "a2a", recipient: agentId },
metadata: {
requiresResponse: true,
responded: false
},
});

for (const req of requests) {
const answer = await processRequest(req.content);
const fromAgent = req.metadata?.fromAgent || req.source?.sender;

await cortex.a2a.send({
from: agentId,
to: fromAgent,
message: answer,
replyTo: req.id,
});
}
}

Querying A2A Messages

Use standard memory search with source.type = 'a2a':

// All A2A for an agent
const allA2A = await cortex.memory.search("agent-1", "*", {
source: { type: "a2a" },
});

// Messages from specific agent
const fromFinance = await cortex.memory.search("hr-agent", "*", {
source: { type: "a2a" },
metadata: { fromAgent: "finance-agent" },
});

// High-importance A2A
const urgent = await cortex.memory.search("agent-1", "*", {
source: { type: "a2a" },
minImportance: 85,
});

// Semantic search across A2A
const budgetMsgs = await cortex.memory.search("agent-1", "budget approval", {
embedding: await embed("budget approval"),
source: { type: "a2a" },
});

Best Practices

Set Appropriate Importance
const IMPORTANCE = {
CRITICAL: 95, // Major decisions, approvals
URGENT: 85, // Time-sensitive requests
IMPORTANT: 75, // Key information
STANDARD: 60, // Regular collaboration (default)
FYI: 40, // Nice-to-know
NOTIFICATION: 30 // Low-priority
};
Tag Messages Well
await cortex.a2a.send({
from: "agent-1",
to: "agent-2",
message: "Approved budget increase",
metadata: {
tags: ["approval", "budget", "finance", "urgent"],
approvalType: "budget",
amount: 50000,
},
});
Link to Context Chains
// Always link A2A to context when part of workflow
await cortex.a2a.send({
from: "supervisor",
to: "specialist",
message: "Handle this request",
contextId: context.id, // Enables full traceability
});

Architecture

Under the Hood

A2A is a convenience layer on top of agent memory. Every A2A message is stored as a memory with source.type = 'a2a'. You can always query using the standard memory API.

What cortex.a2a.send() does:

  1. Creates/finds A2A conversation in ACID store
  2. Adds message to conversation (immutable)
  3. Stores in sender's vector memory (outbound)
  4. Stores in receiver's vector memory (inbound)
  5. Links both to ACID via conversationRef

This is ~50 lines of code reduced to 7 lines with the helper.


Next Steps