Custom Events
AG-UI CUSTOM events let a backend node push arbitrary data to the Angular client while a run is in progress. The adapter accumulates these events into a customEvents signal on the AgUiAgent returned by injectAgent() โ reachable directly, no cast required (shown in Reading Custom Events below).
customEvents is a Signal<CustomStreamEvent[]>. The list is reset to [] when RUN_STARTED arrives, so it only ever contains events from the current run.
The special CUSTOM event with name: "on_interrupt" is handled separately โ it populates agent.interrupt and does not appear in customEvents. See the Interrupts guide.
#Where Custom Events Come From
A LangGraph node emits a custom event by writing to the stream writer with stream_mode='custom':
The ag-ui-langgraph package surfaces this as an AG-UI CUSTOM event on the wire:
The adapter JSON-parses value when it arrives as a string, so consumers always receive the structured object. The event is appended to customEvents as { name: "analysis_progress", data: { step: "scoring", pct: 42 } }.
#Reading Custom Events in Angular
injectAgent() returns an AgUiAgent, so the customEvents signal is available directly on the injected agent โ no cast needed.
#Reactive effect
Use an effect to react every time new events arrive:
#Computed signal
When you only need to derive a value, computed is more concise:
Both patterns are zoneless-safe: Angular's signal graph tracks the customEvents() read and re-evaluates the derived value automatically.
customEvents is the mechanism the chat composition uses for progressive a2ui surface updates โ partial argument events accumulate here during a tool call and drive live rendering before the call completes. The consuming side is documented in chat's A2UI overview. If you are building a custom a2ui integration over AG-UI, read agent.customEvents() the same way.
#Relation to Interrupts
CUSTOM events named on_interrupt follow a separate path: the adapter routes them to agent.interrupt (a Signal<AgentInterrupt | undefined>) and they never enter customEvents. This keeps the two signals purpose-distinct โ interrupt drives human-in-the-loop approval flows, while customEvents carries all other backend-pushed data.
See the Interrupts guide for the full interrupt lifecycle including <chat-approval-card> and submit({ resume }).
#See Also
- Architecture โ how the adapter reduces protocol events into Angular signals
- Event Mapping โ full table of AG-UI event types and the agent fields they populate
- injectAgent() โ the injection function that returns
AgUiAgent