Refactor React sub-providers to use conversation.on() subscriptions#749
Refactor React sub-providers to use conversation.on() subscriptions#749kraenhansen wants to merge 7 commits into
Conversation
Move AsyncAPI schemas from packages/types/schemas/ to schemas/ at repo root, with symlinks from both packages. Move manually maintained types (Role, Mode, Status, Callbacks, etc.) from @elevenlabs/types to @elevenlabs/client, making the types package generated-code-only. This is preparation for event codegen in the client package, which needs direct access to the schemas. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Records the removal of manual types from @elevenlabs/types as a minor bump (pre-v1). Consumers should import these types from @elevenlabs/client instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `scripts/generate-events.ts` to the client package that reads `agent.asyncapi.yaml` and generates: - `GeneratedEventMap` with typed event callbacks for each wire event - `GeneratedWireType` union of all generated wire type strings - `WIRE_TYPE_TO_EVENT_NAME` / `WIRE_TYPE_TO_PAYLOAD_FIELD` lookup tables - `dispatchGeneratedEvent()` helper for runtime dispatch Internally handled events (audio, agent_response, user_transcript, client_tool_call, ping, error, internal_tentative_agent_response) are excluded — they need custom dispatch logic in BaseConversation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move Callbacks and CALLBACK_KEYS from types.ts to a new events.ts module that combines generated events with hand-written internal and extra events into a unified ConversationEventMap. Add CALLBACK_KEY_TO_EVENT_NAME mapping for wiring callbacks to event names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace direct this.options.onXxx() callback invocations with this.emit() calls through a typed event emitter. Constructor-provided callbacks are automatically registered as initial listeners. The emitter supports multiple listeners per event, with on() returning an unsubscribe function for ergonomic cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace this.options.onAudio/onAudioAlignment/onError in VoiceConversation with this.emit() calls - Remove no-op callback defaults from getFullOptions (the emitter handles the no-listeners case) - Keep pre-instance callbacks (onStatusChange, onConnect) as direct calls since errors during startSession must propagate to the caller Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ions Replace ListenerMap/ListenerSet callback composition with direct event subscriptions on the conversation instance. Sub-providers now subscribe via conversation.on() and derive status from isStarting/startupError context values. Hook-level callbacks compose via mergeOptions instead of the removed registerCallbacks mechanism. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 39282de. Configure here.
| return conversation.on("error", msg => { | ||
| setErrorMessage(msg); | ||
| }); | ||
| }, [conversation]); |
There was a problem hiding this comment.
Status stuck in "error" after runtime error then disconnect
High Severity
When conversation becomes null (disconnect), the error-subscription effect early-returns without clearing errorMessage. Since the status derivation checks errorMessage !== undefined before checking conversation, the status stays "error" instead of transitioning to "disconnected". All other sub-providers (ConversationModeProvider, ConversationFeedbackProvider, ConversationInputProvider) correctly reset their state when conversation is null, but ConversationStatusProvider does not.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 39282de. Configure here.
| } else { | ||
| emit(eventName); | ||
| } | ||
| } |
There was a problem hiding this comment.
Generated dispatchGeneratedEvent function is never imported or used
Low Severity
dispatchGeneratedEvent is exported from generated/dispatch.ts but is never imported anywhere in the codebase. The onMessageHandler in BaseConversation still uses explicit switch/case statements for all event types. This is dead generated code that adds maintenance burden without being used.
Reviewed by Cursor Bugbot for commit 39282de. Configure here.


Summary
ConversationStatus,ConversationMode,ConversationFeedback,ConversationInput) now subscribe to events viaconversation.on()instead of composed callback objectsConversationStatusProviderderives status fromisStarting/startupErrorcontext values instead ofonStatusChangecallbacksuseConversationhook composes callbacks viamergeOptionsinstead ofuseRegisterCallbacksListenerMap,ListenerSetand their tests (net -419 lines)test-utils.tswith mock conversation that supportson()/__emit()Test plan
pnpm turbo build)tsc --noEmit)react-native,convai-widget-core) build successfullyexamples/agent-testbench, verify status/mode/mute/feedback/messages/disconnect🤖 Generated with Claude Code
Note
Medium Risk
Medium risk because it changes the conversation callback delivery mechanism to a shared event-emitter model and refactors React provider lifecycle/state wiring, which could affect listener ordering and session cleanup behavior.
Overview
Adds a typed event-emitter API (
conversation.on/off) to@elevenlabs/clientconversations and routes all existing constructor callbacks through this emitter, allowing multiple listeners per event with unsubscribe support.Introduces AsyncAPI-based codegen (
generate-events) to generate wire-event maps/dispatch helpers and moves previously hand-maintained shared conversation types (Role,Mode,Status,Callbacks, etc.) out of@elevenlabs/typesinto@elevenlabs/client.Refactors
@elevenlabs/reactsub-providers to subscribe viaconversation.on()(and derives status from newisStarting/startupErrorcontext state), removes the internalListenerMap/ListenerSetcallback-composition utilities, and updates tests to use a shared mock conversation with__emit.Reviewed by Cursor Bugbot for commit 39282de. Bugbot is set up for automated code reviews on this repo. Configure here.