Envelopes & Events
The Envelope is the wire format for everything that flows between agents on the network. Every send produces one; every observer reads them. This page covers the envelope shape, the built-in event types, audience/visibility rules, and how to send raw envelopes when session.send(text) isn't enough.
The Envelope#
@dataclass(slots=True)
class Envelope:
envelope_id: str # hub-stamped UUID
session_id: str
sender_id: str # agent_id
audience: list[str] | None # None = broadcast to all session participants
event_type: str # "ag2.msg.text", "ag2.session.invite", etc.
event_data: dict # event-specific payload
causation_id: str | None = None # envelope_id this one is "in reply to"
priority: Priority = Priority.NORMAL
created_at: str = "" # hub-stamped ISO-Z
sequence: int = 0 # hub-stamped per-session monotonic counter
The hub stamps envelope_id, created_at, and sequence at admission time. Everything else comes from the sender.
Built-in Event Types#
Constants exported from autogen.beta.network:
Substantive events#
| Constant | String | Carried in event_data |
|---|---|---|
EV_TEXT | "ag2.msg.text" | {"text": "<body>"} |
EV_PACKET | "ag2.packet" | {"routing": {...}, "context_updates": {...}, "body": "<text>"} |
These are the envelopes adapters fold into per-session state. EV_TEXT carries plain text. EV_PACKET is the workflow adapter's atomic round-end capture: it bundles the agent's routing decision (matched against ToolCalled(...) rules), accumulated context_vars mutations, and final body text into one envelope. Posted by the framework after each Agent.ask round; tool authors don't construct these directly.
Session lifecycle events#
| Constant | String | Source |
|---|---|---|
EV_SESSION_INVITE | "ag2.session.invite" | Hub posts to each target when a session is created. |
EV_SESSION_INVITE_ACK | "ag2.session.invite.ack" | Each invitee posts when accepting. |
EV_SESSION_INVITE_REJECT | "ag2.session.invite.reject" | Optional — invitee rejects (default handler doesn't, but you can override). |
EV_SESSION_OPENED | "ag2.session.opened" | Hub posts when all acks land. |
EV_SESSION_CLOSED | "ag2.session.closed" | Hub posts on any termination path; event_data.reason carries why. |
EV_SESSION_EXPIRED | "ag2.session.expired" | Hub posts when TTL sweeper closes the session. |
EV_EXPECTATION_VIOLATED | "ag2.session.expectation_violated" | Hub posts when an expectation evaluator's threshold is breached and the handler is notify (vs audit / auto_close). |
Task lifecycle events#
| Constant | String | Notes |
|---|---|---|
ag2.task.started | "ag2.task.started" | Mirrored from TaskStarted. |
ag2.task.progress | "ag2.task.progress" | Mirrored from TaskProgress. |
ag2.task.completed | "ag2.task.completed" | Mirrored from TaskCompleted. |
ag2.task.failed | "ag2.task.failed" | Mirrored from TaskFailed. |
ag2.task.expired | "ag2.task.expired" | Mirrored from TaskExpired. |
These flow only when an AgentClient is running an LLM turn — see Task Observation.
Audience and Visibility#
audience: list[str] | None controls who sees the envelope:
None— broadcast to all session participants.[agent_id_1, agent_id_2, ...]— only those participants see it.
The visible_to(envelope, agent_id) helper says whether a given participant should see an envelope:
Views (FullTranscript, WindowedSummary) honour the audience: an envelope addressed only to [bob] doesn't appear in carol's projection. See Views & Skills.
Priority#
The hub processes higher-priority envelopes ahead of lower-priority ones in queue order. Use sparingly; most application envelopes should leave priority at NORMAL.
Sending Raw Envelopes#
The session.send(text, audience=...) helper wraps the envelope construction for you. When you need a custom event type or to set fields the helper doesn't expose, build an Envelope and post it directly:
The hub doesn't validate event_type against any allowlist; custom types pass through unmodified. Adapters fold only event types they recognise — substantive ones (EV_TEXT, plus EV_PACKET under the workflow adapter) and lifecycle ones (EV_SESSION_*). Custom event types are written to the WAL and delivered to participants but don't advance turn-taking state.
Causation#
causation_id lets you mark an envelope as "in reply to" another:
The default handler does this automatically when it replies to an inbound EV_TEXT. Custom handlers should set it when they're producing a logical reply — useful for tooling that builds threaded views of a session.
Reading the WAL#
The hub maintains a per-session write-ahead log:
Envelopes appear in admission order. The WAL is the canonical replay surface — Hub.hydrate() re-folds it through each adapter to rebuild in-memory state on restart.
Custom Event Types — Practical Tips#
When you define your own event types:
- Use a dotted namespace prefix (
"myapp.review_request", not"review") to avoid collisions with futureag2.*events. - Make
event_dataJSON-serialisable (no datetimes, dataclasses, etc.) so it round-trips through the store cleanly. - If multiple participants need to react, set
audience=None. If only one, address it specifically; views will filter it out from non-recipients. - Don't rely on adapters to do anything special with custom types — they pass through. Your custom handler is responsible for processing them.
Inspecting Frames#
Below the envelope layer is the frame layer (HelloFrame, SendFrame, NotifyFrame, …) — that's the link transport. Most users don't touch it. It surfaces only when you're implementing a custom transport (see Agent Clients) or debugging a connection issue.