# How to Coordinate Multi-Agent Work Using Leases, Signals, and Checkpoints in AgentMemory

> Learn to coordinate multi-agent work in AgentMemory using leases signals and checkpoints for safe scalable collaboration via a shared KV store. Maximize your agent system's efficiency today.

- Repository: [Rohit Ghumare/agentmemory](https://github.com/rohitg00/agentmemory)
- Tags: how-to-guide
- Published: 2026-05-10

---

**AgentMemory provides three core coordination primitives—leases for exclusive action locks, signals for asynchronous inter-agent messaging, and checkpoints for external condition gating—that enable safe, scalable multi-agent collaboration through a shared KV store.**

Coordinating autonomous agents requires robust primitives for exclusivity, communication, and external validation beyond simple task queues. The **agentmemory** repository (`rohitg00/agentmemory`) implements exactly such an orchestration layer, persisting all coordination state in a transactional SQLite-backed KV store under the `mem:*` namespace. This guide explains how to coordinate multi-agent work using leases, signals, and checkpoints to build deterministic workflows that prevent duplicate execution, facilitate real-time collaboration, and gate progress on external events like CI pipelines or human approvals.

## Understanding the Three Coordination Primitives

### Leases: Exclusive Action Locks

Leases function as distributed locks tied to specific action IDs, ensuring that only one agent can execute a given action at any time. According to the source code in [`src/functions/leases.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/leases.ts), a lease stores the `agentId`, an `expiresAt` timestamp, and a `status` field (`active` | `released` | `expired`). Agents acquire leases via the `mem::lease-acquire` function; if an active lease already exists for that action, the request fails, preventing duplicate work. A background task automatically cleans up expired leases, and agents can explicitly release locks early using `mem::lease-release`.

### Signals: Asynchronous Agent Messaging

Signals provide a lightweight, persistent message bus that allows agents to communicate without tight coupling. Stored in the KV store under the `mem:signals` scope, signal entries support filtering by recipient (`to`), message `type`, and `threadId`. The implementation in [`src/functions/signals.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/signals.ts) exposes `mem::signal-send` for broadcasting updates and `mem::signal-read` for consuming messages. Once read, signals are automatically marked as consumed, ensuring idempotent message processing across agent pools.

### Checkpoints: External Condition Gates

Checkpoints act as declarative gates that block action progress until external systems satisfy specific conditions. Defined in [`src/functions/checkpoints.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/checkpoints.ts), each checkpoint maintains a `status` (`pending` | `passed` | `failed`) and an array of `linkedActionIds` that depend on it. Workflows create checkpoints using `mem::checkpoint-create`, and external processes or agents resolve them via `mem::checkpoint-resolve`, which automatically unblocks all linked actions by updating their state in the frontier calculations.

## How the Coordination Primitives Work Together

The orchestration layer combines these primitives through a unified lifecycle:

1. **Action Creation**: An agent creates an action entry in the `mem:actions` KV scope.

2. **Lease Acquisition**: Before execution, the agent calls `mem::lease-acquire`. If granted, the agent holds exclusive rights; otherwise, it must wait or skip.

3. **Signal Exchange**: During execution, agents emit progress updates via `mem::signal-send`, while other agents poll `mem::signal-read` to coordinate sub-tasks or handoffs.

4. **Checkpoint Gating**: Actions declare checkpoints that must be resolved before specific stages. The checkpoint blocks the action in the frontier until `mem::checkpoint-resolve` is called.

5. **Frontier Scheduling**: The `frontier` function in [`src/functions/frontier.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/frontier.ts) merges data from `mem:leases`, `mem:signals`, and `mem:checkpoints` to compute which actions have no active leases, no pending checkpoints, and are ready for execution.

All state changes are atomic and audited via the built-in `recordAudit` mechanism, ensuring reproducibility across the multi-agent system.

## Code Examples

### Acquire a Lease, Execute Work, and Release

```typescript
// Attempt to acquire exclusive rights to action-123
const leaseRes = await sdk.trigger({
  function_id: "mem::lease-acquire",
  payload: { actionId: "action-123", agentId: "agent-A" },
});

if (!leaseRes.success) {
  throw new Error("Lease already held by another agent");
}

// Execute the critical section
await performActionWork();

// Explicitly release the lease
await sdk.trigger({
  function_id: "mem::lease-release",
  payload: { leaseId: leaseRes.lease.id, agentId: "agent-A" },
});

```

*Source: [`src/functions/leases.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/leases.ts)*

### Send Progress Signals Between Agents

```typescript
// Agent A notifies Agent B of completion
await sdk.trigger({
  function_id: "mem::signal-send",
  payload: {
    from: "agent-A",
    to: "agent-B",
    type: "task-complete",
    threadId: "workflow-42",
    content: "Step 1 finished successfully",
  },
});

```

### Consume Signals with Filtering

```typescript
// Agent B reads pending messages
const res = await sdk.trigger({
  function_id: "mem::signal-read",
  payload: {
    agentId: "agent-B",
    threadId: "workflow-42",
    limit: 10,
  },
});

console.log("Pending signals:", res.signals);

```

*Source: [`src/functions/signals.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/signals.ts)*

### Gate Actions with External Checkpoints

```typescript
// Create a checkpoint blocking action until CI passes
const cp = await sdk.trigger({
  function_id: "mem::checkpoint-create",
  payload: {
    type: "ci",
    name: "build-verified",
    status: "pending",
    linkedActionIds: ["action-123"],
  },
});

// Later, when CI system reports success
await sdk.trigger({
  function_id: "mem::checkpoint-resolve",
  payload: {
    checkpointId: cp.checkpoint.id,
    status: "passed",
    resolvedBy: "ci-service",
    result: { buildId: "2026-05-10-001" },
  },
});

```

*Source: [`src/functions/checkpoints.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/checkpoints.ts)*

### Complete Multi-Agent Coordination Workflow

```typescript
// Step 1: Acquire lease to prevent duplicate execution
const lease = await sdk.trigger({
  function_id: "mem::lease-acquire",
  payload: { actionId: "action-123", agentId: "agent-A" },
});

if (!lease.success) return; // Another agent owns this action

try {
  // Step 2: Notify collaborators
  await sdk.trigger({
    function_id: "mem::signal-send",
    payload: {
      from: "agent-A",
      to: "agent-B",
      type: "task-start",
      threadId: "proj-xyz",
      content: "Processing payment",
    },
  });

  // Step 3: Create checkpoint for external validation
  await sdk.trigger({
    function_id: "mem::checkpoint-create",
    payload: {
      type: "approval",
      name: "manager-approval",
      status: "pending",
      linkedActionIds: ["action-123"],
    },
  });

  // Step 4: Wait for external resolution (simulated here)
  await waitForApproval();

  await sdk.trigger({
    function_id: "mem::checkpoint-resolve",
    payload: {
      checkpointId: "ckpt-approval-01",
      status: "passed",
      resolvedBy: "manager-01",
    },
  });

} finally {
  // Step 5: Always release the lease
  await sdk.trigger({
    function_id: "mem::lease-release",
    payload: { leaseId: lease.lease.id, agentId: "agent-A" },
  });
}

```

## Key Source Files for Coordination

| File | Purpose |
|------|---------|
| [`src/functions/leases.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/leases.ts) | Implements `mem::lease-acquire`, `mem::lease-release`, and expiration logic |
| [`src/functions/signals.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/signals.ts) | Implements `mem::signal-send` and `mem::signal-read` with filtering |
| [`src/functions/checkpoints.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/checkpoints.ts) | Implements `mem::checkpoint-create` and `mem::checkpoint-resolve` |
| [`src/functions/frontier.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/frontier.ts) | Scheduler that computes ready actions by merging lease, signal, and checkpoint state |
| [`src/state/schema.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/state/schema.ts) | Defines KV namespaces (`mem:leases`, `mem:signals`, `mem:checkpoints`) |
| [`src/triggers/api.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/triggers/api.ts) | HTTP API wrappers exposing coordination functions to external callers |
| [`src/mcp/tools-registry.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/mcp/tools-registry.ts) | MCP tool definitions for integration with MCP clients |

## Summary

- **Leases** guarantee exclusive execution by granting time-bounded locks on actions, preventing race conditions and duplicate work across agent pools.
- **Signals** enable loose coupling through persistent, filterable messages that agents exchange via `mem::signal-send` and `mem::signal-read`.
- **Checkpoints** integrate external validation by blocking actions until resolved, linking multiple actions to a single gate via `linkedActionIds`.
- **Frontier scheduling** in [`src/functions/frontier.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/frontier.ts) combines these primitives transactionally to determine which actions are safe to execute.
- All coordination state resides in the SQLite-backed KV store with full audit logging, ensuring reproducible multi-agent workflows.

## Frequently Asked Questions

### How do leases prevent duplicate work in multi-agent systems?

Leases function as exclusive locks tied to specific action IDs. When an agent invokes `mem::lease-acquire` from [`src/functions/leases.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/leases.ts), the system atomically checks for existing active leases in the `mem:leases` KV scope. If none exist, it creates an entry containing the `agentId` and `expiresAt` timestamp, granting exclusive rights. If a lease already exists, the request fails immediately, forcing other agents to wait or skip the action. Background cleanup tasks automatically expire stale leases based on the timestamp.

### What distinguishes signals from checkpoints in agentmemory?

Signals provide asynchronous, non-blocking communication between agents, allowing them to broadcast updates or request assistance without halting execution. Checkpoints, conversely, are explicitly designed to block action progress until external conditions are satisfied. While signals are consumed via `mem::signal-read` and then marked as read, checkpoints created via `mem::checkpoint-create` remain in a `pending` state until explicitly resolved through `mem::checkpoint-resolve`, at which point they unblock all `linkedActionIds` in the frontier calculations.

### How does the frontier scheduler determine which actions are ready to run?

The frontier function in [`src/functions/frontier.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/frontier.ts) calculates the ready-set by filtering the `mem:actions` table against three criteria derived from coordination primitives. It excludes actions with active leases in `mem:leases`, actions blocked by unresolved checkpoints in `mem:checkpoints`, and actions with unsatisfied dependencies. By transactionally merging these KV tables, the frontier produces a deterministic list of actions eligible for immediate scheduling, ensuring agents only pick up work that is safe to execute.

### Can a single checkpoint gate multiple actions simultaneously?

Yes, the `linkedActionIds` array parameter in `mem::checkpoint-create` supports linking one checkpoint to multiple action IDs. When the checkpoint is resolved via `mem::checkpoint-resolve` in [`src/functions/checkpoints.ts`](https://github.com/rohitg00/agentmemory/blob/main/src/functions/checkpoints.ts), the system automatically updates the status for all linked actions, removing blockers from the frontier for the entire batch. This pattern enables efficient approval workflows where one external event, such as a CI build completion or manager sign-off, can unblock an entire pipeline of dependent actions.