How Strix Sub-Agents Communicate and Share State in Multi-Agent Workflows
Strix sub-agents communicate through a thread-safe message bus and shared graph structure while maintaining isolated AgentState objects, enabling both loose coupling and tight coordination in multi-agent workflows.
The usestrix/strix repository implements a dynamic multi-agent framework where parent agents spawn specialized sub-agents to handle delegated tasks. Understanding how Strix sub-agents communicate and share state is essential for building reliable orchestration workflows that coordinate concurrent agent execution.
Agent Isolation and the Shared Coordination Graph
Strix implements a hybrid state management model that balances agent isolation with global coordination. Each running agent maintains its own private AgentState object containing its unique ID, task description, parent ID, iteration counters, and message history. These states are stored in the _agent_states dictionary and are never directly accessed by other agents.
However, all agents participate in a shared agents-graph that tracks relationships, message flows, and execution status. This graph structure, maintained in strix/tools/agents_graph/agents_graph_actions.py, enables loose coupling—agents only need their own state to function—while still allowing tight coordination through the global _agent_messages queue and _agent_graph registry.
The Agent Lifecycle: From Creation to Completion
Spawning Sub-Agents with create_agent
When a parent agent needs to delegate work, it invokes the create_agent tool. In strix/tools/agents_graph/agents_graph_actions.py, this function constructs a new AgentState, registers it in _agent_states, and starts the agent in its own thread via _run_agent_in_thread.
The new agent receives its own isolated state but can optionally inherit the parent's conversation context through the inherit_context parameter, ensuring the sub-agent starts with necessary background information without direct state sharing.
# Inside a running Strix agent (LLM generated tool call)
result = create_agent(
agent_state=state, # current AgentState supplied automatically
task="Analyze the log files in /workspace/logs",
name="Log Analyzer",
inherit_context=True, # include parent conversation history
skills="file_read,regex_search"
)
print(result["agent_id"])
Message Passing via send_message_to_agent
Agents exchange data through the send_message_to_agent function, which implements the core communication protocol. This function creates a message record in the global _agent_messages dictionary, creates a graph edge of type "message" in _agent_graph, and marks the message as delivered.
# Assume this runs inside Agent A's tool execution
send_message_to_agent(
agent_state=state,
target_agent_id="agent_4f3b2a1c",
message="I have finished parsing the CSV. Here are the top 5 rows.",
message_type="information",
priority="high"
)
Polling and Processing with _check_agent_messages
Each agent's main execution loop in strix/agents/base_agent.py calls _check_agent_messages on every iteration. This method queries the shared _agent_messages dictionary for entries addressed to the current agent, injects them into the agent's private AgentState.messages list as user-role messages, and clears the waiting flag if the agent was paused.
# No explicit code – the BaseAgent loop calls this automatically
# When a new message is placed in _agent_messages, the next iteration runs:
self._check_agent_messages(self.state)
Pausing Execution with wait_for_message
Agents can explicitly yield control using the wait_for_message tool. This function sets the agent's AgentState.waiting_for_input flag, updates the graph node status to "waiting", and notifies the telemetry tracer that the agent is idle. The agent automatically resumes when a message arrives or when the timeout threshold is reached.
# Agent B decides it needs input from another agent or the user
wait_for_message(agent_state=state, reason="Awaiting data export from Log Analyzer")
Reporting Results with agent_finish
When a sub-agent completes its delegated task, it calls agent_finish to update the agents-graph with its final status. The function records the result in _agent_graph, optionally sends a structured XML report back to the parent via _agent_messages, and removes the sub-agent from the running _agent_instances pool.
agent_finish(
agent_state=state,
result_summary="Log analysis completed successfully.",
findings=["Error spike at 03:00 UTC", "No critical failures"],
success=True,
report_to_parent=True,
final_recommendations=["Increase log rotation frequency"]
)
Shared State Mechanics Reference
The communication architecture relies on three core data structures defined in strix/tools/agents_graph/agents_graph_actions.py:
AgentState– Defined instrix/agents/state.py, these unique per-agent objects contain IDs, task definitions, message lists, and context dictionaries. They are passed as thestateargument to tools and stored in_agent_states._agent_graph– A global dictionary tracking all agents, their parent-child relationships, and status edges. It updates when agents are created, finish, or send messages._agent_messages– A thread-safe dictionary acting as a message queue wheresend_message_to_agentappends entries and_check_agent_messagesconsumes them per agent.
Summary
- Strix sub-agents maintain isolated state through individual
AgentStateobjects while coordinating via a shared in-memory graph and message bus. - The
create_agentfunction inagents_graph_actions.pyspawns sub-agents with optional context inheritance, registering them in global registries. - Message passing occurs through
send_message_to_agent, which writes to the global_agent_messagesdictionary and creates traceable graph edges. - Automatic message polling via
_check_agent_messagesinbase_agent.pyinjects external messages into private agent state and handles resume logic. - Explicit synchronization is available through
wait_for_message, which pauses agents until specific inputs arrive.
Frequently Asked Questions
How do parent agents pass context to sub-agents in Strix?
Parent agents pass context using the inherit_context=True parameter in create_agent. When enabled, the parent’s conversation history is copied into the new agent’s AgentState, providing background information without requiring direct state access or message passing after creation.
What happens when a Strix sub-agent is waiting for a message?
When an agent invokes wait_for_message, its status changes to "waiting" in the agents-graph and its AgentState.waiting_for_input flag is set. The agent loop pauses execution until _check_agent_messages detects a new entry in _agent_messages, at which point the message is injected into the agent’s private state and execution resumes.
How does Strix ensure thread safety when multiple agents communicate?
Thread safety is maintained through the global _agent_messages dictionary and _agent_states registry in agents_graph_actions.py, which are accessed by agent threads in a coordinated manner. The _check_agent_messages method consumes messages atomically, preventing race conditions during message delivery and state updates.
Can sub-agents communicate directly with siblings, or only through parents?
Sub-agents can communicate directly with any other agent using send_message_to_agent by specifying the target's agent_id. While hierarchical parent-child relationships are tracked in _agent_graph, the messaging system supports direct peer-to-peer communication without requiring message routing through parent agents.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →