docs/book/src/architecture/request-lifecycle.md
What happens between "user sends a message" and "agent replies" — the full path, with streaming, tool calls, and security gates annotated.
flowchart LR
A[External event] -->|webhook / push / poll / WS| B[Channel adapter]
B -->|decode, dedup, pair-check| C[Inbound envelope]
C -->|workspace binding| D[Runtime: deliver_message]
A channel adapter (e.g. discord.rs, telegram.rs, email_channel.rs) receives platform-native events and converts them into a uniform inbound envelope. The adapter handles:
[channels.<name>.allowed_users] / IAM policy before the event reaches the runtimeIf the channel is not paired or the user isn't allowed, the event is dropped before the runtime sees it.
sequenceDiagram
participant CH as Channel
participant RT as Runtime
participant SEC as Security
participant MEM as Memory
participant PR as Provider
participant TL as Tool
CH->>RT: deliver_message(envelope)
RT->>MEM: load_context(conversation_id)
MEM-->>RT: prior messages + retrieved facts
RT->>PR: chat(system, history, tools)
loop Streaming
PR-->>RT: StreamEvent::TextDelta
RT-->>CH: draft update (if channel supports it)
end
PR-->>RT: StreamEvent::ToolCall(args)
RT->>SEC: validate_tool_call(name, args, risk)
alt Blocked
SEC-->>RT: Err(reason)
RT->>PR: chat(..., + tool_error)
else Approval required
SEC->>CH: ask_operator(prompt)
CH-->>SEC: approved / denied
else Allowed
SEC-->>RT: Ok
end
RT->>TL: invoke(args)
TL-->>RT: ToolResult
RT->>MEM: append(tool_call, tool_result)
RT->>PR: chat(..., + tool_result)
PR-->>RT: StreamEvent::TextDelta (final)
RT-->>CH: reply(final)
RT->>MEM: persist(conversation)
Key properties:
supports_draft_updates(), the runtime edits a sent message in place as text arrives. Discord, Slack, and Telegram support this.validate_tool_call consults the autonomy level, allow/deny lists, and path boundaries. Medium-risk calls under Supervised autonomy go to the operator-approval path.Every tool invocation produces a signed receipt written to the tool-receipts log. See Tool receipts. Receipts are chained — each one includes the hash of the previous — so tampering with any receipt invalidates the rest of the log.
Outbound messages go back through the same channel adapter. Adapters with multi-message support (Discord, Slack) can stream long replies as a sequence of messages; others (email, SMS) flush on stream completion.
crates/zeroclaw-runtime/src/agent/loop_.rscrates/zeroclaw-runtime/src/security/crates/zeroclaw-channels/src/orchestrator/crates/zeroclaw-providers/src/traits.rs (StreamEvent enum), compatible.rs (SSE parser)