Object.Mailbox (object v0.1.2)

Object-Oriented Reinforcement Learning (OORL) Mailbox implementation based on AAOS Section 4 - Object Interactions.

Implements a comprehensive message-passing protocol with interaction dyads, priority-based message processing, and communication mechanisms for autonomous objects. The mailbox provides reliable, efficient communication between objects in the AAOS system.

Core Features

  • Message Passing: Reliable message delivery with acknowledgments
  • Interaction Dyads: Bidirectional communication relationships
  • Priority Processing: Priority-based message ordering
  • Protocol Handlers: Extensible message type handling
  • Message History: Bounded history for debugging and analysis
  • Routing Tables: Efficient message routing information
  • Delivery Confirmations: Acknowledgment tracking system

Message Types

The mailbox supports various message types for different communication patterns:

  • :state_update - Object state change notifications
  • :goal_update - Goal function modifications
  • :belief_update - World model belief updates
  • :learning_signal - Learning data and feedback
  • :coordination - Multi-object coordination protocols
  • :negotiation - Multi-step negotiation processes
  • :acknowledgment - Delivery confirmations
  • :heartbeat - Connection health monitoring

Interaction Dyads

Dyads are bidirectional relationships between objects that enable:

  • Enhanced communication efficiency
  • Social learning and knowledge transfer
  • Coordinated behavior and cooperation
  • Trust and reputation building

Performance Characteristics

  • Message Throughput: ~1000 messages/second per mailbox
  • Latency: <1ms for local message processing
  • Memory Usage: ~100 bytes per message + content size
  • History Retention: Configurable bounded queue (default: 1000 messages)
  • Scalability: Efficient O(1) message insertion and O(log n) priority sorting

Error Handling

  • Message validation prevents malformed messages
  • Acknowledgment system ensures reliable delivery
  • Dead letter handling for undeliverable messages
  • Circuit breaker protection against message storms
  • Graceful degradation under resource pressure

Example Usage

# Create a new mailbox
mailbox = Object.Mailbox.new("agent_1")

# Send a coordination message
updated_mailbox = Object.Mailbox.send_message(
  mailbox, "agent_2", :coordination,
  %{action: :form_coalition}, [priority: :high]
)

# Form an interaction dyad
dyad_mailbox = Object.Mailbox.form_dyad(mailbox, "partner_agent", 0.8)

# Process incoming messages
{processed, final_mailbox} = Object.Mailbox.process_inbox(dyad_mailbox)

Summary

Types

Interaction dyad representing a bidirectional relationship between two objects.

Message structure for inter-object communication.

Message type for routing and protocol handling

Message processing priority level

t()

Mailbox structure for an object's communication system.

Functions

Dissolves an interaction dyad.

Forms an interaction dyad between two objects.

Gets all active interaction dyads.

Gets comprehensive mailbox statistics.

Creates a new mailbox for an object.

Processes messages in the inbox based on type and priority.

Receives a message into the inbox.

Registers a protocol handler for specific message types.

Updates routing table for message delivery.

Types

interaction_dyad()

@type interaction_dyad() :: %{
  participants: {String.t(), String.t()},
  formation_time: DateTime.t(),
  interaction_count: non_neg_integer(),
  compatibility_score: float(),
  utility_score: float(),
  active: boolean()
}

Interaction dyad representing a bidirectional relationship between two objects.

Fields

  • participants - Tuple of the two object IDs in the dyad
  • formation_time - When the dyad was first established
  • interaction_count - Number of interactions through this dyad
  • compatibility_score - Initial compatibility assessment (0.0-1.0)
  • utility_score - Calculated utility based on interaction success
  • active - Whether the dyad is currently active

Utility Calculation

Utility score is computed as:

utility = min(interaction_count / 100.0, 1.0) * compatibility_score

This rewards both frequent interaction and initial compatibility.

message()

@type message() :: %{
  id: String.t(),
  from: String.t(),
  to: String.t(),
  type: message_type(),
  content: any(),
  timestamp: DateTime.t(),
  priority: priority_level(),
  requires_ack: boolean(),
  ttl: pos_integer()
}

Message structure for inter-object communication.

Fields

  • id - Unique message identifier for tracking and deduplication
  • from - Sender object ID
  • to - Recipient object ID
  • type - Message type atom for routing and handling
  • content - Message payload (any serializable term)
  • timestamp - Message creation timestamp
  • priority - Processing priority level
  • requires_ack - Whether delivery confirmation is required
  • ttl - Time-to-live in seconds before message expires

message_type()

@type message_type() ::
  :state_update
  | :goal_update
  | :belief_update
  | :learning_signal
  | :coordination
  | :negotiation
  | :acknowledgment
  | :heartbeat
  | atom()

Message type for routing and protocol handling

priority_level()

@type priority_level() :: :low | :medium | :high | :critical

Message processing priority level

t()

@type t() :: %Object.Mailbox{
  created_at: DateTime.t(),
  delivery_confirmations: %{required(String.t()) => DateTime.t()},
  history_size_limit: pos_integer(),
  inbox: [message()],
  interaction_dyads: %{required(String.t()) => interaction_dyad()},
  message_history: :queue.queue(message()),
  object_id: String.t(),
  outbox: [message()],
  protocol_handlers: %{required(message_type()) => function()},
  routing_table: %{required(String.t()) => any()},
  updated_at: DateTime.t()
}

Mailbox structure for an object's communication system.

Fields

  • object_id - ID of the object this mailbox belongs to
  • inbox - List of incoming messages awaiting processing
  • outbox - List of outgoing messages awaiting delivery
  • interaction_dyads - Map of dyad_id to interaction_dyad structures
  • message_history - Bounded queue of message history for debugging
  • routing_table - Routing information for efficient message delivery
  • protocol_handlers - Map of message_type to handler functions
  • delivery_confirmations - Map tracking message delivery confirmations
  • created_at - Mailbox creation timestamp
  • updated_at - Last modification timestamp
  • history_size_limit - Maximum number of messages to retain in history

Functions

dissolve_dyad(mailbox, other_object_id)

@spec dissolve_dyad(t(), String.t()) :: t()

Dissolves an interaction dyad.

Marks an existing interaction dyad as inactive, effectively ending the enhanced communication relationship while preserving historical interaction data for analysis.

Parameters

  • mailbox - Mailbox struct containing the dyad
  • other_object_id - ID of the other object in the dyad to dissolve

Returns

Updated mailbox with dyad marked as inactive (not removed).

Dissolution vs Removal

Dyads are marked inactive rather than deleted to:

  • Preserve interaction history for analysis
  • Enable potential reactivation in the future
  • Maintain social learning data
  • Support reputation and trust calculations

Examples

# Dissolve existing dyad
iex> dissolved = Object.Mailbox.dissolve_dyad(mailbox, "partner_agent")
iex> dyad = dissolved.interaction_dyads["agent_1-partner_agent"]
iex> dyad.active
false

# Dyad data preserved
iex> dyad.interaction_count
25
iex> dyad.formation_time
~D[2024-01-15 10:30:00]

Common Dissolution Triggers

  • Poor Performance: Low utility scores over time
  • Compatibility Issues: Repeated coordination failures
  • Resource Constraints: Too many active dyads
  • Task Completion: Project-specific partnerships ending
  • Manual Override: Explicit dissolution requests

Effects of Dissolution

Once dissolved, the dyad:

  • No longer provides priority message routing
  • Stops contributing to social learning
  • Removes coordination benefits
  • Preserves historical interaction data
  • Can be reactivated if needed

Reactivation

Inactive dyads can be reactivated by:

  • Calling form_dyad/3 again with the same partner
  • Automatic reactivation on successful interactions
  • Manual reactivation through administrative tools

Performance

  • Dissolution time: ~0.05ms
  • Memory preserved: Historical data retained
  • No immediate cleanup: Background garbage collection

form_dyad(mailbox, other_object_id, compatibility_score \\ 0.5)

@spec form_dyad(t(), String.t(), float()) :: t()

Forms an interaction dyad between two objects.

Creates a bidirectional interaction relationship between the mailbox owner and another object, enabling enhanced communication, coordination, and social learning capabilities.

Parameters

  • mailbox - Mailbox struct to add dyad to
  • other_object_id - ID of the other object in the dyad
  • compatibility_score - Initial compatibility assessment (0.0-1.0, default: 0.5)

Returns

Updated mailbox with new dyad added to interaction_dyads map.

Dyad Structure

Created dyads include:

  • Participants: Tuple of both object IDs
  • Formation Time: Timestamp of dyad creation
  • Interaction Count: Initial count of 0
  • Compatibility Score: Initial assessment
  • Utility Score: Calculated benefit metric (starts at 0.0)
  • Active Status: Set to true for new dyads

Compatibility Guidelines

  • 0.0-0.3 - Low compatibility, limited benefits
  • 0.3-0.7 - Moderate compatibility, task-specific benefits
  • 0.7-1.0 - High compatibility, excellent collaboration

Examples

# Form high-compatibility dyad
iex> updated = Object.Mailbox.form_dyad(mailbox, "partner_agent", 0.8)
iex> dyads = Object.Mailbox.get_active_dyads(updated)
iex> map_size(dyads)
1

# Default compatibility
iex> mailbox = Object.Mailbox.form_dyad(mailbox, "sensor_1")
iex> dyad = mailbox.interaction_dyads["agent_1-sensor_1"]
iex> dyad.compatibility_score
0.5

Dyad Benefits

Once formed, dyads provide:

Enhanced Communication

  • Priority message routing between dyad partners
  • Reduced message latency and overhead
  • Dedicated communication channels

Social Learning

  • Shared experience and knowledge transfer
  • Collaborative problem solving
  • Behavioral imitation and adaptation

Coordination

  • Simplified cooperation protocols
  • Joint action planning and execution
  • Resource sharing and allocation

Trust Building

  • Reputation tracking and assessment
  • Reliability and performance monitoring
  • Long-term relationship maintenance

Dyad Evolution

Dyads evolve over time through:

  1. Interaction: Message exchanges update interaction count
  2. Utility Calculation: Performance-based utility scoring
  3. Compatibility Adjustment: Adaptation based on outcomes
  4. Activity Management: Automatic activation/deactivation

Performance

  • Formation time: ~0.1ms
  • Memory overhead: ~200 bytes per dyad
  • Lookup performance: O(1) by dyad ID
  • Maximum recommended dyads: 50 per object

get_active_dyads(mailbox)

@spec get_active_dyads(t()) :: %{required(String.t()) => interaction_dyad()}

Gets all active interaction dyads.

Retrieves all currently active interaction dyads from the mailbox, filtering out inactive dyads and returning only those that provide active communication and coordination benefits.

Parameters

  • mailbox - Mailbox struct to query

Returns

Map of dyad_id → interaction_dyad for all active dyads.

Examples

# Get active dyads
iex> active_dyads = Object.Mailbox.get_active_dyads(mailbox)
iex> map_size(active_dyads)
3

# Check specific dyad activity
iex> dyads = Object.Mailbox.get_active_dyads(mailbox)
iex> Map.has_key?(dyads, "agent_1-partner_2")
true

# Examine dyad details
iex> dyads = Object.Mailbox.get_active_dyads(mailbox)
iex> dyad = dyads["agent_1-coordinator_1"]
iex> dyad.utility_score
0.75
iex> dyad.interaction_count
42

Use Cases

Active dyad information is useful for:

Communication Optimization

  • Priority routing decisions
  • Load balancing across partners
  • Connection health monitoring

Social Learning

  • Partner selection for knowledge transfer
  • Imitation target identification
  • Collaborative learning opportunities

Coordination

  • Coalition formation decisions
  • Task assignment optimization
  • Resource sharing partnerships

Performance Analysis

  • Dyad effectiveness measurement
  • Social network analysis
  • Interaction pattern identification

Dyad Activity

Dyads are considered active when:

  • active field is true
  • Recent interaction activity (implementation dependent)
  • Both participants are still available
  • Utility score above minimum threshold

Performance

  • Query time: O(n) where n is total dyad count
  • Typical response: ~0.1ms for <50 dyads
  • Memory usage: New map creation, ~100 bytes overhead
  • No side effects: Read-only operation

Filtering Criteria

Only dyads matching these criteria are returned:

  • dyad.active == true
  • Dyad record is not nil
  • Both participants are valid object IDs

get_stats(mailbox)

@spec get_stats(t()) :: %{
  total_messages_sent: non_neg_integer(),
  total_messages_received: non_neg_integer(),
  pending_inbox: non_neg_integer(),
  pending_outbox: non_neg_integer(),
  active_dyads: non_neg_integer(),
  total_dyads: non_neg_integer(),
  history_size: non_neg_integer(),
  history_limit: pos_integer(),
  uptime: non_neg_integer()
}

Gets comprehensive mailbox statistics.

Retrieves detailed statistics about the mailbox's communication patterns, performance metrics, and operational status. Useful for monitoring, debugging, and performance optimization.

Parameters

  • mailbox - Mailbox struct to analyze

Returns

Map containing comprehensive statistics:

  • :total_messages_sent - Number of messages sent by this mailbox
  • :total_messages_received - Number of messages received
  • :pending_inbox - Current unprocessed inbox messages
  • :pending_outbox - Current unsent outbox messages
  • :active_dyads - Number of currently active interaction dyads
  • :total_dyads - Total dyads ever formed (including inactive)
  • :history_size - Current message history queue size
  • :history_limit - Maximum history size limit
  • :uptime - Mailbox uptime in seconds

Examples

# Get basic statistics
iex> stats = Object.Mailbox.get_stats(mailbox)
iex> stats.total_messages_sent
127
iex> stats.active_dyads
5

# Calculate performance metrics
iex> stats = Object.Mailbox.get_stats(mailbox)
iex> message_rate = (stats.total_messages_sent + stats.total_messages_received) / stats.uptime
iex> dyad_efficiency = stats.active_dyads / max(1, stats.total_dyads)

Performance Indicators

Message Volume

  • High Volume (>100 msg/min): Active hub or coordinator
  • Medium Volume (10-100 msg/min): Regular operational object
  • Low Volume (<10 msg/min): Peripheral or specialized object

Processing Health

  • Pending Inbox: Should be near 0 for healthy processing
  • Pending Outbox: Indicates delivery bottlenecks if high
  • History Usage: history_size / history_limit ratio

Social Connectivity

  • Active Dyads: Number of active partnerships
  • Dyad Efficiency: active_dyads / total_dyads ratio
  • Interaction Density: Messages per dyad

Monitoring Applications

Performance Monitoring

  • Message throughput analysis
  • Processing bottleneck identification
  • Resource usage tracking

Health Monitoring

  • Communication failures detection
  • Overload condition identification
  • System degradation alerts

Social Analysis

  • Interaction pattern analysis
  • Network connectivity assessment
  • Partner effectiveness evaluation

Debugging

  • Message flow tracing
  • Delivery failure analysis
  • Performance regression investigation

Statistical Calculations

The statistics are computed as follows:

# Message counts from history
sent = Enum.count(history, &(&1.from == object_id))
received = Enum.count(history, &(&1.to == object_id))

# Current queue sizes
pending_in = length(inbox)
pending_out = length(outbox)

# Dyad metrics
active = count_active_dyads(interaction_dyads)
total = map_size(interaction_dyads)

# Temporal metrics
uptime = DateTime.diff(DateTime.utc_now(), created_at, :second)

Performance

  • Calculation time: ~0.5-2ms depending on history size
  • Memory overhead: Temporary iteration over message history
  • No side effects: Read-only operation
  • Caching: Statistics can be cached for frequent access

new(object_id, opts \\ [])

@spec new(
  String.t(),
  keyword()
) :: t()

Creates a new mailbox for an object.

Initializes a complete mailbox with all necessary components for inter-object communication, including message queues, protocol handlers, and interaction tracking.

Parameters

  • object_id - ID of the object this mailbox belongs to
  • opts - Optional configuration:
    • :history_size_limit - Maximum message history size (default: 1000)

Returns

New mailbox struct with initialized state and default protocol handlers.

Default Protocol Handlers

The mailbox is created with handlers for common message types:

  • :state_update - Process object state updates
  • :goal_update - Handle goal function changes
  • :belief_update - Update world model beliefs
  • :learning_signal - Process learning data
  • :coordination - Handle coordination protocols
  • :negotiation - Process negotiation messages
  • :acknowledgment - Handle delivery confirmations
  • :heartbeat - Process connectivity checks

Examples

# Create basic mailbox
iex> mailbox = Object.Mailbox.new("agent_1")
iex> mailbox.object_id
"agent_1"
iex> length(mailbox.inbox)
0

# Create with custom history limit
iex> mailbox = Object.Mailbox.new("sensor_1", history_size_limit: 500)
iex> mailbox.history_size_limit
500

Performance

  • Creation time: ~0.1ms
  • Memory usage: ~1KB base + message storage
  • History queue: Efficient FIFO operations
  • Protocol handlers: Fast O(1) lookup

process_inbox(mailbox)

@spec process_inbox(t()) :: {[{message(), term()}], t()}

Processes messages in the inbox based on type and priority.

Processes all pending messages in the inbox according to priority order, applying appropriate protocol handlers and generating results. This is the core message processing function that drives object communication.

Parameters

  • mailbox - Mailbox struct with messages to process

Returns

{processed_messages, updated_mailbox} where:

  • processed_messages - List of {message, result} tuples
  • updated_mailbox - Mailbox with cleared inbox and updated state

Processing Priority

Messages are processed in strict priority order:

  1. :critical - System-critical messages (emergency stops, failures)
  2. :high - Important coordination and control messages
  3. :medium - Regular operational messages (default)
  4. :low - Background tasks and maintenance messages

Within each priority level, messages are processed by timestamp (FIFO).

Protocol Handlers

Each message type has a dedicated handler:

  • :state_update → Update object state
  • :goal_update → Modify goal function
  • :belief_update → Update world model beliefs
  • :learning_signal → Process learning data
  • :coordination → Handle coordination protocols
  • :negotiation → Process negotiation steps
  • :acknowledgment → Handle delivery confirmations
  • :heartbeat → Update connection status

Examples

# Process accumulated messages
iex> {processed, updated} = Object.Mailbox.process_inbox(mailbox)
iex> length(processed)
3
iex> length(updated.inbox)
0

# Examine processing results
iex> {[{msg1, result1}, {msg2, result2}], _} = Object.Mailbox.process_inbox(mailbox)
iex> result1
{:coordination_received, %{action: :form_coalition}}
iex> result2
{:state_updated, %{energy: 95}}

Error Handling

  • Handler errors are caught and returned as {:error, reason}
  • Unknown message types return {:error, :no_handler}
  • Processing continues despite individual message failures
  • Failed messages are logged for debugging

Performance

  • Processing rate: ~100-500 messages/second depending on complexity
  • Priority sorting: O(n log n) where n is inbox size
  • Handler execution: Varies by message type and content
  • Memory usage: Temporary overhead during processing

Batch Processing

  • All inbox messages processed in single operation
  • Atomic inbox clearing prevents message loss
  • Efficient sorting reduces processing overhead
  • Results collected for analysis and debugging

receive_message(mailbox, message)

@spec receive_message(t(), map()) :: t() | {:error, atom()}

Receives a message into the inbox.

Validates and accepts an incoming message into the mailbox inbox, updating interaction dyads and handling acknowledgments as needed. Invalid messages are rejected with appropriate error codes.

Parameters

  • mailbox - Mailbox struct to receive into
  • message - Message to receive (must include required fields)

Returns

  • Updated mailbox with message added to inbox
  • {:error, reason} if message validation fails

Message Validation

Required fields for valid messages:

  • :id - Unique message identifier
  • :from - Sender object ID
  • :to - Recipient object ID (should match mailbox owner)
  • :type - Message type for routing
  • :content - Message payload
  • :timestamp - Message creation time

Processing Steps

  1. Validation: Check message format and required fields
  2. Inbox Addition: Add to inbox for processing
  3. History Recording: Add to message history
  4. Dyad Update: Update interaction dyad with sender
  5. Acknowledgment: Send ACK if required by message

Examples

# Receive valid coordination message
iex> message = %{
...>   id: "msg_123",
...>   from: "agent_2",
...>   to: "agent_1",
...>   type: :coordination,
...>   content: %{action: :form_coalition},
...>   timestamp: DateTime.utc_now(),
...>   priority: :high,
...>   requires_ack: true,
...>   ttl: 3600
...> }
iex> updated = Object.Mailbox.receive_message(mailbox, message)
iex> length(updated.inbox)
1

# Invalid message format
iex> bad_message = %{invalid: true}
iex> Object.Mailbox.receive_message(mailbox, bad_message)
{:error, :invalid_message_format}

Acknowledgments

When requires_ack: true:

  • Automatic acknowledgment message is sent back to sender
  • ACK contains original message ID for correlation
  • High priority for timely delivery confirmation

Error Conditions

  • :invalid_message_format - Missing required fields
  • :malformed_content - Content structure invalid
  • :expired_message - Message TTL exceeded
  • :duplicate_message - Message ID already processed

Performance

  • Validation time: ~0.05ms per message
  • Inbox insertion: O(1) operation
  • History maintenance: Bounded queue operations
  • Dyad updates: O(1) lookup and modification

register_handler(mailbox, message_type, handler_fn)

Registers a protocol handler for specific message types.

Parameters

  • mailbox: Mailbox struct
  • message_type: Type of message to handle
  • handler_fn: Function to handle messages of this type

Returns

Updated mailbox with new handler registered

send_message(mailbox, to_object_id, message_type, content, opts \\ [])

@spec send_message(t(), String.t(), message_type(), any(), keyword()) :: t()

Sends a message to another object.

Creates and queues a message for delivery to another object, updating the mailbox state and interaction dyad information. Messages are validated and assigned unique IDs for tracking.

Parameters

  • mailbox - Mailbox struct to send from
  • to_object_id - ID of the recipient object
  • message_type - Type of message for routing and handling
  • content - Message content (any serializable term)
  • opts - Optional message configuration:
    • :priority - Message priority (:low, :medium, :high, :critical)
    • :requires_ack - Whether delivery confirmation is required
    • :ttl - Time-to-live in seconds (default: 3600)

Returns

Updated mailbox with:

  • Message added to outbox
  • Message recorded in history
  • Interaction dyad updated or created
  • Updated timestamp

Message Processing

  1. Message Creation: Generate unique ID and timestamp
  2. Validation: Ensure required fields are present
  3. Outbox Addition: Add to outbox for delivery
  4. History Recording: Add to bounded message history
  5. Dyad Update: Update or create interaction dyad

Examples

# Send coordination message
iex> updated = Object.Mailbox.send_message(
...>   mailbox, "coordinator_1", :coordination,
...>   %{action: :join_coalition},
...>   [priority: :high, requires_ack: true]
...> )
iex> length(updated.outbox)
1

# Send learning signal with TTL
iex> Object.Mailbox.send_message(
...>   mailbox, "learner_2", :learning_signal,
...>   %{reward: 1.0, experience: %{action: :explore}},
...>   [ttl: 300]
...> )
%Object.Mailbox{outbox: [%{ttl: 300, ...}], ...}

Interaction Dyads

Sending messages automatically:

  • Creates new dyads if they don't exist
  • Updates interaction count for existing dyads
  • Calculates utility scores based on interaction frequency
  • Maintains dyad metadata for social learning

Performance

  • Message creation: ~0.1ms
  • History bounded at configurable limit
  • Dyad updates: O(1) lookup and update
  • Memory usage: ~100 bytes + content size per message

update_routing(mailbox, object_id, route_info)

Updates routing table for message delivery.

Parameters

  • mailbox: Mailbox struct
  • object_id: Object to update routing for
  • route_info: Routing information

Returns

Updated mailbox with new routing information