---
title: Event Center
description: RuntimeEvent registration, filtering, shape, and DevTools projection rules in Agently.
keywords: Agently, EventCenter, ObservationEvent, RuntimeEvent, observation, DevTools
---

# Event Center

> Languages: **English** · [中文](../../cn/observability/event-center.md)

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:

- `RuntimeEvent` is the preferred framework event model for new code.
- Event Center dispatches `RuntimeEvent` records to ordinary hooks.
- `ObservationEvent` is the DevTools bridge projection derived from `RuntimeEvent`.
- Existing `emit_observation` / `async_emit_observation` code continues to work as a compatibility alias.

Run and retry naming:

- `agent_execution` is the run lineage kind for one AgentExecution-owned Agent run.
- `attempt_index` describes a retryable model-request attempt inside a request; it is not an AgentExecution counter.
- DevTools should preserve both fields as separate semantics: render `agent_execution` from `run.run_kind`, and read model retry attempts from `payload.attempt_index` or `run.meta.attempt_index` on `model_request` runs.

## Register a hook

```python
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:

```python
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_execution.*`, `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:

```python
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:

```python
await Agently.event_center.async_emit({
    "event_type": "runtime.info",
    "source": "Docs",
    "message": "direct event",
})
```

For top-level convenience APIs:

```python
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 `payload` fields.
- Do not fail on unknown `event_type` values.
- Do not treat `payload` as a closed schema; it can grow per event type.
- When correlating a request, Session, or TriggerFlow execution, prefer `run` fields over parsing `message`.

## See also

- [TriggerFlow Events and Streams](../triggerflow/events-and-streams.md) — flow control and runtime stream
- [DevTools](devtools.md) — ready-made observation and evaluation bridge
- [FastAPI Service Exposure](../services/fastapi.md) — forwarding runtime streams to service clients
- [Coding Agents](../development/coding-agents.md) — coding-agent guidance through Agently Skills
