# Agent Communication

TnsAI agents communicate through structured, immutable messages following established multi-agent protocols. The communication system supports direct messaging, Contract Net Protocol (CNP) for task allocation, group membership management, and leader election.

The group contracts, including membership and leader-election primitives, live
in `tnsai-core`; `tnsai-coordination` builds orchestration and protocol flows
on top of those contracts.

## AgentMessage

`com.tnsai.communication.AgentMessage` is the core message type for inter-agent communication. It is an immutable value object (all fields are final, maps are unmodifiable copies).

### Structure

| Field | Type | Description |
|-------|------|-------------|
| `messageId` | `String` | Unique message ID (auto-generated via UUID) |
| `senderId` | `String` | ID of the sending agent (required) |
| `receiverId` | `String` | ID of the receiving agent (required) |
| `messageType` | `MessageTypeMarker` | Type of message (CNP, Task, etc.) |
| `content` | `Map<String, Object>` | Message payload (immutable copy) |
| `timestamp` | `long` | Message creation time |
| `inReplyTo` | `String` | ID of the message being replied to (null for originals) |
| `metadata` | `Map<String, Object>` | Additional metadata (immutable copy) |

### Creating Messages

<!-- java-contract: src/main/java/com/example/tnsai/docs/AgentMessageCreationExample.java -->
```java
// Basic message
AgentMessage msg = AgentMessage.create(
    "agent-A",                    // sender
    "agent-B",                    // receiver
    CNPMessageType.CFP,           // type
    Map.of("task", "analyze data", "deadline", "2h")  // content
);

// Reply (automatically swaps sender/receiver, sets inReplyTo)
AgentMessage reply = AgentMessage.createReply(
    originalMessage,
    CNPMessageType.PROPOSAL,
    Map.of("bid", 100, "eta", "1h")
);

// Add metadata to existing message
AgentMessage withMeta = msg.withSpec("priority", "high");
```

### Query Methods

```java
boolean isReply = msg.isReply();
boolean isCfp = msg.isType(CNPMessageType.CFP);
boolean isCnp = msg.isCategory("CNP");
Map<String, Object> spec = msg.getSpec();  // alias for getMetadata()
```

## MessageTypeMarker

`com.tnsai.communication.MessageTypeMarker` is the marker interface that all message type enums implement. It provides a `category()` method used for filtering messages by protocol.

### CNPMessageType

`com.tnsai.communication.CNPMessageType` implements the Contract Net Protocol (Smith, 1980) for distributed task allocation.

**Protocol Flow:**

```
Phase 1: Call for Proposals
    Initiator -> CFP (broadcast task requirements)

Phase 2: Contractor Response
    Contractors -> PROPOSAL (submit bid) or REFUSE (decline)

Phase 3: Selection
    Initiator -> ACCEPT or REJECT

Phase 4: Negotiation (optional)
    COUNTER_OFFER exchanges

Phase 5: Finalization
    Initiator -> AWARD
    Contractor -> CONFIRM

Execution:
    RESULT (success) or FAILURE (error)
```

**Enum values:**

| Value | Phase | Description |
|-------|-------|-------------|
| `CFP` | 1 | Call for proposals (broadcast) |
| `PROPOSAL` | 2 | Submit bid/proposal |
| `REFUSE` | 2 | Decline to bid |
| `ACCEPT` | 3 | Accept a proposal |
| `REJECT` | 3 | Reject a proposal |
| `COUNTER_OFFER` | 4 | Propose modified terms |
| `AWARD` | 5 | Final contract award |
| `CONFIRM` | 5 | Confirm acceptance |
| `CANCEL` | 5 | Cancel the process |
| `RESULT` | Execution | Execution result |
| `FAILURE` | Execution | Execution failure |

### CNP Example

```java
// Initiator broadcasts CFP
AgentMessage cfp = AgentMessage.create(
    initiator.id(), "broadcast",
    CNPMessageType.CFP,
    Map.of("task", "translate document", "language", "Japanese")
);

// Contractor submits proposal
AgentMessage proposal = AgentMessage.createReply(cfp,
    CNPMessageType.PROPOSAL,
    Map.of("bid", 50, "eta", "30min", "quality", "high")
);

// Initiator accepts
AgentMessage accept = AgentMessage.createReply(proposal,
    CNPMessageType.ACCEPT,
    Map.of("contractId", "c-123")
);

// Contractor reports result
AgentMessage result = AgentMessage.createReply(accept,
    CNPMessageType.RESULT,
    Map.of("translation", translatedText, "wordCount", 1500)
);
```

## MembershipManager

`com.tnsai.agents.groups.MembershipManager` manages agent group membership lifecycle including registration, health monitoring, capability-based lookup, and task tracking.

### Registration

```java
// Register an agent
Optional<MemberInfo> info = membershipManager.register(agent);

// Validate before registration
MembershipValidation validation = membershipManager.validate(agent);
if (!validation.valid()) {
    System.err.println("Rejected: " + validation.reason());
}

// Unregister
boolean removed = membershipManager.unregister(agentId, "task completed");
```

### Health Monitoring

```java
// Record heartbeat
membershipManager.heartbeat(agentId);

// Check health
Optional<MemberHealth> health = membershipManager.getHealth(agentId);

// Get unhealthy members
List<MemberInfo> unhealthy = membershipManager.getUnhealthyMembers();

// Auto-remove unhealthy
int removed = membershipManager.checkAndRemoveUnhealthy();

// Start/stop automatic health checking
membershipManager.startHealthCheck(Duration.ofSeconds(30));
membershipManager.stopHealthCheck();
```

`MemberHealth` enum: `HEALTHY`, `DEGRADED`, `UNHEALTHY`, `UNKNOWN`.

### Member Queries

```java
List<MemberInfo> all = membershipManager.getMembers();
Optional<MemberInfo> member = membershipManager.getMember(agentId);
List<MemberInfo> searchers = membershipManager.findByCapability("search");
int count = membershipManager.getMemberCount();
```

### Task Tracking

```java
// Record task start/complete
membershipManager.recordTaskStart(agentId);
membershipManager.recordTaskComplete(agentId);

// Find available members (healthy + can accept more tasks)
List<MemberInfo> available = membershipManager.getAvailableMembers();

// Available with specific capability
List<MemberInfo> available = membershipManager.getAvailableMembersWithCapability("coding");
```

### MemberInfo Record

```java
record MemberInfo(
    String agentId,
    String agentName,
    List<String> capabilities,
    Instant joinedAt,
    Instant lastHeartbeat,
    MemberHealth health,
    Map<String, Object> metadata,
    int priority,              // default 5
    int maxConcurrentTasks,    // default 1
    int activeTasks,           // current active count
    String role                // default "member"
)
```

Key methods on `MemberInfo`:

| Method | Description |
|--------|-------------|
| `isHealthy()` | `health == HEALTHY` |
| `canAcceptTask()` | `activeTasks < maxConcurrentTasks` |
| `getMembershipDuration()` | Time since `joinedAt` |
| `withTaskStarted()` | New `MemberInfo` with `activeTasks + 1` |
| `withTaskCompleted()` | New `MemberInfo` with `activeTasks - 1` |
| `withHeartbeat(Instant)` | New `MemberInfo` with updated heartbeat |
| `withHealth(MemberHealth)` | New `MemberInfo` with updated health |

### Statistics and History

```java
MembershipStats stats = membershipManager.getStats();
// stats.currentCount(), stats.healthyCount(), stats.averageMembershipDuration()

List<MembershipHistoryEntry> history = membershipManager.getHistory(50);
// Each entry: agentId, agentName, action (JOINED/LEFT/REMOVED_UNHEALTHY/REMOVED_POLICY),
//             reason, timestamp
```

## LeaderElectionStrategy

`com.tnsai.agents.groups.LeaderElectionStrategy` is a `@FunctionalInterface` for selecting a leader from group members.

```java
@FunctionalInterface
public interface LeaderElectionStrategy {
    Agent elect(List<Agent> members);
    default Optional<Agent> electOptional(List<Agent> members) { ... }
}
```

### Built-in Strategies

| Strategy | Constant | Behavior |
|----------|----------|----------|
| Random | `RANDOM` | Random selection from members |
| First Joined | `FIRST_JOINED` | First member in the list |
| Last Joined | `LAST_JOINED` | Last member in the list |
| Round Robin | `ROUND_ROBIN` | Rotating (delegates to `FIRST_JOINED`; state managed by group) |
| Role-Based | `ROLE_BASED` | Agent with most roles (most capabilities) |
| None | `NONE` | Always returns null |

### Custom Strategies

```java
// Lambda-based custom strategy
LeaderElectionStrategy byCapabilities = members ->
    members.stream()
        .max(Comparator.comparingInt(a -> a.roles().size()))
        .orElse(null);

// Usage
Agent leader = strategy.elect(groupMembers);
Optional<Agent> leaderOpt = strategy.electOptional(groupMembers);
```

## Related Documentation

- [Advanced Agent Features](../agents/advanced.md) -- hierarchy and delegation
- [Events](../agents/fundamentals/events.md) -- TnsAI event system
- [SPI Reference](../reference/spi.md) -- MessageBroker SPI for pluggable transports
