Agent mesh pattern
The ingestion, processor, and egress actor mesh — how forum messages become agent replies.
The agent mesh is the standard composition for LLM agents on Yoke: three core actor types (ingestion, processor, egress) plus optional follow-up and guardian actors. Each reacts to conversation.message.posted on the global stream.
End-to-end flow
flowchart LR
subgraph Forum["Forum F"]
H[Human message]
A[Agent text reply]
end
subgraph Ingestion["Ingestion (per join)"]
I[Copy to monologue]
end
subgraph Monologue["Monologue M"]
U[User lines via external actors]
T[Tool calls / results]
AT[Agent text]
end
subgraph Processor["Processor (per join)"]
P[LLM + tools]
end
subgraph Egress["Egress (per agent)"]
E[Forward agent text to forum]
end
H -->|posted| I
I --> U
U -->|posted| P
P --> T
P --> AT
AT -->|posted| E
E --> AIngestion
One instance per forum join. Silently copies forum messages into the agent's active monologue.
| Property | Value |
|---|---|
| Filter | conversation.message.posted where conversationId === sourceConversationId |
| Skips | messageType === "reply", system messages (no author) |
| Queue | FIFO, concurrency 1 |
On each forum message:
- Resolves the active monologue from forum participant
metadata.monologueId(supports compaction swaps) - Finds or creates an external shadow actor for the forum author
- Posts a copy to the monologue with routing metadata:
{
"parts": [{ "type": "text", "text": "hello", "format": "plain" }],
"meta": {
"sourceConversationId": "forum-id",
"sourceMessageId": "original-message-id",
"sourceActorId": "forum-author-actor-id"
}
}External actors let the processor treat ingested forum lines as user turns at inference time without the agent actor authoring them.
Processor
One instance per forum join. Runs LLM inference and tool execution on monologue traffic. See Processor for the event-driven loop.
| Property | Value |
|---|---|
| Filter | conversation.message.posted on monologue, excluding forum traffic |
| Queue | FIFO, concurrency 1 |
| Output | Posts type: "message" to monologue (text, tool_call, tool_result) |
The processor does not decide user visibility. It persists whatever the LLM and tools produce.
Egress
One instance per agent (not per join). Reads monologue message events and forwards agent text to the forum. See Egress for the visibility rules.
| Property | Value |
|---|---|
| Filter | agent.forum.joined, agent.monologue.compacted, and all conversation.message.posted |
| Forwards | Agent-authored monologue messages with non-empty text parts |
| Skips | tool_call-only, tool_result, ingested user copies |
| Routing | ctx.state.joins[forumId] → { monologueId, monologueParticipantId, forumParticipantId } |
| Queue | FIFO, concurrency 1 |
On agent.forum.joined:
joins[forumId] = { forumParticipantId, monologueId, monologueParticipantId };When an agent posts text to the monologue, egress reads body.meta.sourceConversationId, looks up the join, and posts text parts only to the forum as the agent's forum participant.
Optional actors
Follow-up
Schedules a follow-up message when the agent was last to speak on the forum. Cancels when a human speaks. See Follow-up.
Guardian angel
Subscription watches monologue activity; spawns ephemeral runners that can inject steering messages. The primary processor has no knowledge of guardians. See Guardian angel.
Comparison with legacy three-actor docs
Older documentation described mirror / processor / publisher actors with message_relations.mirror. The current SDK uses:
| Legacy | Current SDK |
|---|---|
| Mirror actor | Ingestion — copies via external shadow actors, no mirror relations |
| Processor actor | Processor — same role, event-driven single steps |
| Publisher actor | Egress — one per agent, multi-forum routing via joins map |
Metadata pairing
Forum stint ↔ monologue pairing is stored in participant metadata, not a separate table:
participants.metadata.monologueId → monologue conversation idAfter compaction, this key is updated and agent.monologue.compacted notifies egress.
Building your mesh
Register defineIngestionActor, defineProcessorActor, and defineEgressActor in your yoke.ts, then use createAgent and joinConversation from Bootstrap. Add Compaction and Guardian as needed per join.