Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Messages

Messages are how agents communicate. They’re the envelopes that carry task assignments, status updates, and coordination signals.

Message Properties

PropertyTypeDescription
idUUIDUnique message identifier
fromAgentIdSender
toAgentIdRecipient
msg_typeEnumType and payload
priorityEnumProcessing order
created_atDateTimeWhen sent
correlation_idOptionFor request/response

Message Types

#![allow(unused)]
fn main() {
pub enum MessageType {
    // Semantic message types (for inter-agent communication)
    Task { description: String },           // Actionable work request
    Query { question: String },             // Question expecting response
    Informational { summary: String },      // FYI/context update
    Confirmation { ack_type: ConfirmationType }, // Receipt/acknowledgment

    // Task management
    TaskAssign { task_id: String },
    TaskDone { task_id: String, result: String },
    TaskFailed { task_id: String, error: String },

    // Status
    StatusRequest,
    StatusResponse { state: String, current_task: Option<String> },

    // Lifecycle
    Ping,
    Pong,
    Shutdown,

    // Extensibility
    Custom { kind: String, payload: String },
}

pub enum ConfirmationType {
    Received,              // Message was received
    Acknowledged,          // Message was acknowledged
    Thanks,                // Message expressing thanks
    Approved,              // Approval confirmation
    Rejected { reason: String }, // Rejection with reason
}
}

Semantic Message Classification

Messages are classified as either actionable or informational:

Actionable (require work)Informational (context only)
Task, Query, TaskAssignInformational, Confirmation
StatusRequest, PingTaskDone, TaskFailed
Shutdown, CustomStatusResponse, Pong

Use msg.is_actionable() and msg.is_informational_or_confirmation() helpers to classify.


## Priority Levels

Messages are processed by priority:

| Priority | Behavior |
|----------|----------|
| `Urgent` | Goes to front of queue, interrupt current work |
| `High` | Goes to front of queue |
| `Normal` | Goes to back of queue (default) |
| `Low` | Goes to back, processed when idle |

```rust
let msg = Message::new(from, to, MessageType::Shutdown)
    .with_priority(Priority::Urgent);

Creating Messages

#![allow(unused)]
fn main() {
use tinytown::{Message, MessageType, AgentId, Priority};

// Basic message
let msg = Message::new(
    AgentId::supervisor(),  // from
    worker_id,              // to
    MessageType::TaskAssign { task_id: "abc123".into() }
);

// With priority
let urgent = Message::new(from, to, MessageType::Shutdown)
    .with_priority(Priority::Urgent);

// With correlation (for request/response)
let request = Message::new(from, to, MessageType::StatusRequest);
let response = Message::new(to, from, MessageType::StatusResponse { 
    state: "working".into(),
    current_task: Some("task-123".into())
}).with_correlation(request.id);
}

Sending Messages

Via Channel:

#![allow(unused)]
fn main() {
let channel = town.channel();
channel.send(&message).await?;
}

Via AgentHandle:

#![allow(unused)]
fn main() {
let handle = town.agent("worker-1").await?;
handle.send(MessageType::StatusRequest).await?;
}

Receiving Messages

#![allow(unused)]
fn main() {
use std::time::Duration;

// Blocking receive (waits up to timeout)
if let Some(msg) = channel.receive(agent_id, Duration::from_secs(30)).await? {
    match msg.msg_type {
        MessageType::TaskAssign { task_id } => {
            println!("Got task: {}", task_id);
        }
        MessageType::Shutdown => {
            println!("Shutting down");
            break;
        }
        _ => {}
    }
}

// Non-blocking receive
if let Some(msg) = channel.try_receive(agent_id).await? {
    // Process message
}
}

Broadcasting

Send to all agents:

#![allow(unused)]
fn main() {
let broadcast = Message::new(
    AgentId::supervisor(),
    AgentId::supervisor(),  // Placeholder, broadcast ignores this
    MessageType::Shutdown
);
channel.broadcast(&broadcast).await?;
}

Custom Messages

For application-specific communication:

#![allow(unused)]
fn main() {
// Define your payload as JSON
let payload = serde_json::json!({
    "pr_url": "https://github.com/...",
    "files_changed": ["src/auth.rs", "tests/auth_test.rs"]
});

let msg = Message::new(from, to, MessageType::Custom {
    kind: "pr_ready".into(),
    payload: payload.to_string()
});
}

Message Flow Example

Supervisor                    Worker
    │                           │
    │  TaskAssign{task-123}     │
    │ ─────────────────────────►│
    │                           │
    │                           │ (working...)
    │                           │
    │    TaskDone{task-123}     │
    │◄───────────────────────── │
    │                           │

Comparison with Gastown Mail

FeatureTinytown MessagesGastown Mail
TransportRedis listsBeads (git-backed)
PersistenceRedis persistenceGit commits
Priority4 levelsYes
RoutingDirect to inboxComplex routing
RecoveryRedis replaysEvent replay

Tinytown messages are ephemeral by default (in Redis memory). Enable Redis persistence for durability.