As soon as any plugin was loaded, every voice frame built a full
CallLegContext and event (~13 us) and scheduled its own callLater
(~2-7 us), even for plugins that never look at voice.
- A plugin may declare `events` (the event classes it handles). The bus
keeps the union and `wants()` / `wants_any()`; the voice and data
bridges check it before building anything, and emit() only calls
plugins that take that event. Undeclared plugins and internal
handlers still get everything.
- emit_deferred batches: one call_later per reactor tick, same order.
- The group context's per-stream constant part (IDs, mode, proxy flag,
aliases) is resolved once per stream; each event still gets its own
`extra` dict.
- Fix: _plugin_started / _plugin_ended grew by one entry per call
forever; END now drops the START key and ended keys are capped.
Per voice frame, group voice to 6 OBP + a MASTER (48 us with no plugin):
any plugin 66.0 -> 59.4 us; a data-only plugin 49.4 us.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>