Skip to content
cd ..

Inter Agent Communication: Task Queues and RPC That Actually Work

// · 5 min read

The moment you have more than one AI agent, you need them to talk to each other. Not just chat, but actually coordinate. Assign work. Wait for results. Handle failures.

Most multi agent frameworks solve this with custom protocols that only work inside their own ecosystem. I wanted something that works across any setup, even across organizational boundaries. So I built the task system in AgenticMail on top of email.

Tasks as first class objects

Every task in AgenticMail has a lifecycle: created, assigned, claimed, in progress, completed, or failed. An agent creates a task by calling assignTask() with a description, the target agent, a priority, and an optional deadline. The task gets stored in the system and the target agent receives notification.

The key insight is that tasks are not emails. They’re structured objects with typed fields, status tracking, and access control. But email serves as the notification and fallback transport layer. If the target agent is connected via SSE (Server Sent Events), it gets an instant push notification. If it’s not connected, it gets an email with the task details.

This dual delivery approach means the system works whether both agents are on the same AgenticMail instance or on different servers in different organizations.

RPC with promise based completion

Sometimes you don’t want to fire and forget. You want to call another agent’s capability and wait for the result, like a function call across agent boundaries.

The rpc() method does exactly this. Under the hood, it creates a task, assigns it to the target agent, and returns a promise. That promise resolves when the target agent completes the task with a result.

const result = await agenticmail.rpc({
  targetAgent: "researcher@agents.example.com",
  capability: "web_search",
  payload: { query: "latest AI safety papers" },
  timeout: 30000
});

The implementation uses an in memory rpcResolvers map. When an RPC call is initiated, the system generates a unique task ID and stores a resolver function keyed to that ID. When the response comes back (either via SSE or through polling), the system looks up the resolver and calls it with the result.

There’s a polling fallback for cases where the SSE connection drops or the agents are on different instances. The caller periodically checks the task status via the API until it sees a completion or hits the timeout. It’s not as elegant as the push model, but it guarantees delivery.

Claim and complete shortcuts

For the target agent, the workflow is straightforward. It receives a task notification, decides whether to accept it, and calls claimTask() to indicate it’s working on it. When finished, it calls completeTask() with the result payload.

These are deliberately simple operations. A task claim is just a status update. A task completion is a status update plus a result payload. The simplicity matters because the agents calling these methods might be running on completely different tech stacks. As long as they can make HTTP requests (or send email), they can participate in the task system.

Capability based access control

Not every agent should be able to assign tasks to every other agent. The system supports capability declarations: each agent advertises what it can do (web search, document analysis, code generation, etc.) and the task system validates that the requested capability matches what the target agent offers.

This prevents an agent from being asked to do something it’s not equipped for, and it lets the system do capability based routing. Instead of addressing a specific agent, you can address a capability, and the system finds an available agent that supports it.

SSE events and email fallback

The event system is worth calling out specifically. AgenticMail uses Server Sent Events for real time notifications between agents on the same instance. Task assignments, status updates, RPC responses; they all flow through SSE when available.

But SSE requires a persistent connection. If an agent disconnects, reconnects, or lives on a different server entirely, the system falls back to email notifications. The email contains structured headers that let the receiving agent parse the task details programmatically, so it’s not just a human readable notification.

This layered approach (SSE when possible, email when necessary) means the task system degrades gracefully. Two agents on the same box get sub second coordination. Two agents in different continents get coordination that’s slower but still reliable, using the most universally supported transport protocol in existence.

Source Code

The RPC endpoint is where the synchronous coordination magic happens. It creates a task record, pushes an SSE event to the target agent, then long polls until the target agent completes the task or the timeout fires. The rpcResolvers map ties the promise lifecycle to the task ID.

router.post('/tasks/rpc', requireAuth, async (req, res, next) => {
  const { target, task, payload, timeout } = req.body || {};
  const targetAgent = await accountManager.getByName(target);
  const taskId = uuidv4();
  const timeoutMs = Math.min(Math.max((timeout || 180) * 1000, 5000), 300_000);

  req.socket.setTimeout(0); // Disable socket timeout for long poll

  db.prepare('INSERT INTO agent_tasks ...').run(taskId, assignerId, targetAgent.id, 'rpc', JSON.stringify({ task, ...(payload || {}) }));
  pushEventToAgent(targetAgent.id, { type: 'task', taskId, taskType: 'rpc', task });

  const completionPromise = new Promise((resolve) => {
    rpcResolvers.set(taskId, resolve);
    const pollInterval = setInterval(() => { /* poll DB every 2s */ }, 2000);
    setTimeout(() => { resolve({ status: 'timeout' }); }, timeoutMs);
  });

  const outcome = await completionPromise;
  res.json({ taskId, status: outcome.status, result: outcome.result });
});

View the full source on GitHub

That’s the beauty of building on email. The fallback is never “it doesn’t work.” The fallback is “it works, just a little slower.”

// share

// subscribe

New posts and updates straight to your inbox. No noise.

cd ..