Processor actor
Event-driven single-step LLM processor — one short inference step per conversation.message.posted.
The processor is the LLM brain of the agent mesh. Unlike a traditional blocking agent loop, it runs one short step per event — each persisted message re-queues the processor via conversation.message.posted.
The processor does not decide what the user sees. It persists monologue messages and moves on. Egress reads those posts and forwards agent text to the forum.
Design
/**
* Event-driven LLM processor: one short step per `conversation.message.posted`.
*
* - user text → optional compaction → single infer → persist outputs (type: message)
* - agent tool_call → execute tools → persist tool_result
* - user tool_result → single infer → persist outputs
*
* Each persisted message re-queues the processor via the same event type.
* User visibility is handled by egress, not here.
*/This means the processor never blocks on multi-round tool loops. Tool execution and follow-up inference happen across separate event deliveries.
Trigger filter
filterFromConfig: () => (event) => {
if (event.type !== "conversation.message.posted") return false;
// Ignore forum traffic (ingestion handles copy-in) and legacy type: reply.
return event.payload.conversationId !== config.sourceConversationId
&& event.payload.messageType !== "reply";
}The processor handles monologue messages only. It does not filter on whether output is user-visible — that is egress's job.
State
const processorStateSchema = z.object({
monologueId: z.string().optional(),
agentParticipantId: z.string().optional(),
processed: z.array(z.string()).default([]), // message ids already handled
});Config is seeded on first run from instance config (monologueId, agentParticipantId). After compaction, state is repointed to the new monologue.
Decision tree
flowchart TD
E[conversation.message.posted] --> C{conversationId === monologueId?}
C -->|no| X[ignore]
C -->|yes| D{already processed?}
D -->|yes| X
D -->|no| T{trigger part types}
T -->|tool_call| TC[Execute tools in parallel]
TC --> TR[Post tool_result as system author]
T -->|tool_result| TI[inferOnce → post outputs]
T -->|user text| COMP{compaction changed list?}
COMP -->|yes| SWAP[Swap monologue + hydrate]
SWAP --> INF[inferOnce → post outputs]
COMP -->|no| INF
TR --> E
INF --> E
INF --> EG[Egress forwards agent text to forum]User text (role user)
- Read full monologue transcript →
InferenceMessage[] - Optionally run compaction — if the list changes, swap to a fresh monologue
- Call
ctx.llm.inferonce viainferOnce - Persist outputs as
type: "message"(text, tool_call, or both)
Agent tool_call (role agent)
- Resolve tool registry (static or lazy)
- Validate inputs and execute all tools in parallel
- Post
tool_resultparts to monologue as system author - Mark message processed — the
tool_resultpost triggers the next processor step
User tool_result (role user)
- Call
inferOnceon the full transcript including the new tool results - Persist outputs
- Mark processed
Configuration
const processorConfigSchema = z.object({
sourceConversationId: z.string(), // forum id
forumParticipantId: z.string(),
agentActorId: z.string(),
monologueId: z.string(), // initial — copied to state
agentParticipantId: z.string(),
model: z.string().default("fixture-echo"),
});Created by joinConversation with the chosen model id.
Registration
defineProcessorActor({
configSchema: processorConfigSchema,
stateSchema: processorStateSchema,
compaction: agentStack.compaction, // optional
tools: agentStack.tools, // optional
});Contrast with runInferenceLoop
| Processor | runInferenceLoop | |
|---|---|---|
| Trigger | Stream events | Direct function call |
| Tool rounds | One round per event | Up to 8 rounds in one call |
| Use case | Primary agent mesh | Ephemeral actors (guardian runner) |
| User visibility | Does not decide | Does not decide (egress handles mesh agents) |
The processor uses inferOnce + event re-queuing. Guardian runners use runInferenceLoop for a complete pass in one delivery.
Queue semantics
queue: { mode: "fifo", concurrency: 1 }FIFO preserves monologue ordering. Concurrency 1 prevents parallel inference on the same monologue stint.
Related
- Egress — user-visible copy-out from monologue to forum
- Inference —
ctx.llm.inferand providers - Tools — tool registration and execution
- Compaction — transcript compaction before infer