Skip to content

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:

  • 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_turn is a run lineage kind for one Agent-facing turn.
  • attempt_index describes 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_turn 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_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:

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:

FieldMeaning
event_idevent id, generated by default
event_typedotted name such as triggerflow.execution_started
sourcewhere the event came from
levelDEBUG / INFO / WARNING / ERROR / CRITICAL
messagehuman-readable message
payloadevent-specific structured data
errorerror information; exceptions are normalized to ErrorInfo
runrun lineage, including run_id, parent_run_id, session_id, execution_id, and related ids
metaadditional metadata
timestampmillisecond 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 eventTool compatibility event
action.loop_startedtool.loop_started
action.plan_readytool.plan_ready
action.loop_failedtool.loop_failed
action.loop_completedtool.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