Event Center
Languages: English · 中文
The Event Center is Agently's framework-level runtime event channel. It carries RuntimeEvent records: model requests, Session application, TriggerFlow lifecycle, Action calls, and similar framework activity can be forwarded to DevTools or to a custom log sink through this channel.
It is separate from TriggerFlow emit / when: emit / when changes control flow inside a flow; RuntimeEvent records report what happened.
Naming compatibility:
RuntimeEventis the preferred framework event model for new code.- Event Center dispatches
RuntimeEventrecords to ordinary hooks. ObservationEventis the DevTools bridge projection derived fromRuntimeEvent.- Existing
emit_observation/async_emit_observationcode continues to work as a compatibility alias.
Run and retry naming:
agent_turnis a run lineage kind for one Agent-facing turn.attempt_indexdescribes a retryable model-request attempt inside a request; it is not an Agent turn counter.- DevTools should preserve both fields as separate semantics: render
agent_turnfromrun.run_kind, and read model retry attempts frompayload.attempt_indexorrun.meta.attempt_indexonmodel_requestruns.
Register a hook
from agently import Agently
captured = []
async def capture(event):
captured.append(event)
Agently.event_center.register_hook(
capture,
event_types="runtime.info",
hook_name="docs.capture",
)
emitter = Agently.event_center.create_emitter("Docs")
await emitter.async_info("hello")
Agently.event_center.unregister_hook("docs.capture")event_types can be a string, a list of strings, or None. With None, the hook receives every event. Sync callbacks are accepted too; Event Center normalizes them to async calls.
Hooks that forward high-frequency runtime events to an expensive outlet can ask Event Center to summarize delivery:
Agently.event_center.register_hook(
capture,
event_types="model.response.delta",
hook_name="docs.summary_capture",
delivery_policy={
"mode": "summary",
"dispatch": "await",
"emit_interval": 0.1,
"max_items": 20,
"high_frequency_only": True,
},
)The default delivery policy is raw and awaited. Summary delivery is per hook; it does not change the producer's RuntimeEvent records or other hooks that request raw events. Summary events carry meta["coalesced"], coalesced_count, first_event_id, and last_event_id.
Use dispatch="background" only for best-effort outlets that have an explicit flush/close point. await Agently.event_center.async_flush(hook_name) drains buffered summaries and tracked background deliveries before shutdown. Event Center also runs an on-demand idle flush monitor while background deliveries or summary buffers exist: each new event refreshes the idle timer, and a quiet period triggers bounded flushing. This is a long-lived-loop safety net, not a replacement for explicit flush before CLI/script shutdown.
Emit a runtime event
Agently-owned event types such as model.*, request.*, action.*, tool.*, session.*, agent_turn.*, triggerflow.*, and execution_environment.* are produced by core runtime coordinators. Custom plugins and applications may emit their own Event Center messages, but they should use an application/plugin-owned namespace and must not rely on official Agently modules consuming those custom messages.
Built-in plugins report facts to core through typed observations, handler decisions, or route stream callbacks. Core-owned coordinators map those facts into official RuntimeEvent records and AgentExecution stream items.
The usual path is an emitter:
emitter = Agently.event_center.create_emitter(
"BillingWorker",
base_meta={"tenant": "demo"},
)
await emitter.async_emit(
"billing.invoice_created",
message="invoice created",
payload={"invoice_id": "inv-1"},
)You can also emit a dict directly:
await Agently.event_center.async_emit({
"event_type": "runtime.info",
"source": "Docs",
"message": "direct event",
})For top-level convenience APIs:
await Agently.async_emit_observation({
"event_type": "runtime.info",
"source": "Docs",
"message": "compatibility observation API",
})
await Agently.async_emit_runtime({
"event_type": "runtime.info",
"source": "Docs",
"message": "runtime API",
})Event shape
The top-level fields come from agently.types.data.event.RuntimeEvent. ObservationEvent has the same serialized shape when the main framework DevTools bridge projects RuntimeEvent records for DevTools ingestion:
| Field | Meaning |
|---|---|
event_id | event id, generated by default |
event_type | dotted name such as triggerflow.execution_started |
source | where the event came from |
level | DEBUG / INFO / WARNING / ERROR / CRITICAL |
message | human-readable message |
payload | event-specific structured data |
error | error information; exceptions are normalized to ErrorInfo |
run | run lineage, including run_id, parent_run_id, session_id, execution_id, and related ids |
meta | additional metadata |
timestamp | millisecond timestamp |
TriggerFlow event aliases
Event Center keeps compatibility with historical TriggerFlow event prefixes. A subscription to workflow.execution_started can receive triggerflow.execution_started; a subscription to trigger_flow.signal can receive triggerflow.signal. Documentation and new code should prefer triggerflow.*.
Action compatibility events
Action Runtime lifecycle events use action.* as the primary namespace. When the current Action Runtime branch is tool-compatible, Agently also emits paired tool.* compatibility events for existing subscribers and old examples:
| Primary event | Tool compatibility event |
|---|---|
action.loop_started | tool.loop_started |
action.plan_ready | tool.plan_ready |
action.loop_failed | tool.loop_failed |
action.loop_completed | tool.loop_completed |
Paired compatibility events include meta.compat_event_alias=True, meta.compat_alias_for, and meta.primary_event_id so consumers can deduplicate them from the primary action.* event.
Concrete action execution uses action.started, action.completed, and action.failed. Policy or sandbox gates that stop an action before normal execution use action.approval_required or action.blocked instead of being reported as ordinary failures. For tool-backed actions, payload.action_type may be "tool"; that does not change the event family.
Execution environment events
Execution Environment lifecycle uses execution_environment.*. Providers and DevTools consumers should treat this namespace as extensible. Current manager events include declared, approval_required, ensuring, ready, unhealthy, releasing, released, and failed. unhealthy means a ready handle failed the health check before reuse; the manager releases it and ensures a fresh handle.
Runtime Progress And Stall Diagnostics
Event Center is the runtime-event intake and outlet dispatch layer. Liveness state is updated before expensive hook delivery, so a slow or failing hook must not block stall diagnostics.
AgentExecution records progress in async_get_meta()["diagnostics"]:
diagnostics["stages"]["events"]keeps recent stage progress.diagnostics["last_progress"]records the latest accepted progress event.diagnostics["timeouts"]records hard-deadline failures.diagnostics["stalls"]records idle no-progress failures.
When debugging a live app, attach a temporary Event Center hook or enable console detail logs with .set_settings("debug", True) / .set_settings("debug", "detail"). Remove temporary debug hooks and debug settings after the issue is understood.
Compatibility rules
RuntimeEvent records are an extensible framework event protocol. Agently-DevTools and custom consumers should fail open:
- Ignore unknown top-level fields and unknown
payloadfields. - Do not fail on unknown
event_typevalues. - Do not treat
payloadas a closed schema; it can grow per event type. - When correlating a request, Session, or TriggerFlow execution, prefer
runfields over parsingmessage.
See also
- TriggerFlow Events and Streams — flow control and runtime stream
- DevTools — ready-made observation and evaluation bridge
- FastAPI Service Exposure — forwarding runtime streams to service clients
- Coding Agents — coding-agent guidance through Agently Skills