@convai/web-sdk 1.8.0-beta.5 → 1.8.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +49 -13
  2. package/dist/core/CharacterRoster.d.ts +84 -0
  3. package/dist/core/CharacterRoster.d.ts.map +1 -0
  4. package/dist/core/CharacterRoster.js +348 -0
  5. package/dist/core/CharacterRoster.js.map +1 -0
  6. package/dist/core/ConvaiClient.d.ts +94 -1
  7. package/dist/core/ConvaiClient.d.ts.map +1 -1
  8. package/dist/core/ConvaiClient.js +583 -47
  9. package/dist/core/ConvaiClient.js.map +1 -1
  10. package/dist/core/ConvaiRoomError.d.ts +48 -0
  11. package/dist/core/ConvaiRoomError.d.ts.map +1 -0
  12. package/dist/core/ConvaiRoomError.js +111 -0
  13. package/dist/core/ConvaiRoomError.js.map +1 -0
  14. package/dist/core/MessageHandler.d.ts +14 -0
  15. package/dist/core/MessageHandler.d.ts.map +1 -1
  16. package/dist/core/MessageHandler.js +111 -5
  17. package/dist/core/MessageHandler.js.map +1 -1
  18. package/dist/core/SSESession.d.ts +15 -0
  19. package/dist/core/SSESession.d.ts.map +1 -1
  20. package/dist/core/SSESession.js +92 -3
  21. package/dist/core/SSESession.js.map +1 -1
  22. package/dist/core/connectRequest.d.ts +1 -0
  23. package/dist/core/connectRequest.d.ts.map +1 -1
  24. package/dist/core/connectRequest.js +11 -0
  25. package/dist/core/connectRequest.js.map +1 -1
  26. package/dist/core/index.d.ts +3 -0
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/index.js +3 -0
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/rosterRequest.d.ts +57 -0
  31. package/dist/core/rosterRequest.d.ts.map +1 -0
  32. package/dist/core/rosterRequest.js +210 -0
  33. package/dist/core/rosterRequest.js.map +1 -0
  34. package/dist/core/types.d.ts +462 -5
  35. package/dist/core/types.d.ts.map +1 -1
  36. package/dist/core/types.js.map +1 -1
  37. package/dist/react/components/rtc-widget/components/MarkdownRenderer.d.ts.map +1 -1
  38. package/dist/react/components/rtc-widget/components/MarkdownRenderer.js +39 -44
  39. package/dist/react/components/rtc-widget/components/MarkdownRenderer.js.map +1 -1
  40. package/dist/react/hooks/useConvaiClient.d.ts.map +1 -1
  41. package/dist/react/hooks/useConvaiClient.js +8 -0
  42. package/dist/react/hooks/useConvaiClient.js.map +1 -1
  43. package/dist/utils/inlineMarkdown.d.ts +38 -0
  44. package/dist/utils/inlineMarkdown.d.ts.map +1 -0
  45. package/dist/utils/inlineMarkdown.js +106 -0
  46. package/dist/utils/inlineMarkdown.js.map +1 -0
  47. package/dist/vanilla/AudioRenderer.d.ts.map +1 -1
  48. package/dist/vanilla/AudioRenderer.js +6 -0
  49. package/dist/vanilla/AudioRenderer.js.map +1 -1
  50. package/dist/vanilla/ConvaiWidget.d.ts.map +1 -1
  51. package/dist/vanilla/ConvaiWidget.js +71 -57
  52. package/dist/vanilla/ConvaiWidget.js.map +1 -1
  53. package/dist/vanilla/index.d.ts +1 -0
  54. package/dist/vanilla/index.d.ts.map +1 -1
  55. package/dist/vanilla/index.js +1 -0
  56. package/dist/vanilla/index.js.map +1 -1
  57. package/dist/version.d.ts +1 -1
  58. package/dist/version.js +1 -1
  59. package/dist/version.js.map +1 -1
  60. package/package.json +1 -1
@@ -115,6 +115,17 @@ export interface ServerResponseExtras {
115
115
  has_speak_tag?: boolean;
116
116
  text?: string;
117
117
  room_name?: string;
118
+ command_id?: string;
119
+ room_session_id?: string;
120
+ previous_membership_id?: string | null;
121
+ active_membership_id?: string | null;
122
+ previous_active_membership_id?: string | null;
123
+ route_epoch?: number;
124
+ roster_epoch?: number;
125
+ changed?: boolean;
126
+ added?: unknown[];
127
+ removed_membership_ids?: string[];
128
+ code?: string;
118
129
  supported_types?: string[];
119
130
  vision_buffer?: VisionBufferStatus;
120
131
  requested_respond_mode?: RespondMode;
@@ -267,6 +278,11 @@ export interface RespondModesConfig {
267
278
  trigger?: RespondMode;
268
279
  sceneMetadata?: RespondMode;
269
280
  }
281
+ /** Client-requested action/model output protocol versions. */
282
+ export interface ActionProtocolCapabilities {
283
+ actionProtocolVersion: 1 | 2;
284
+ modelOutputVersion?: 1 | 2;
285
+ }
270
286
  export interface VisionSamplingWindowConfig {
271
287
  count: number;
272
288
  intervalMs: number;
@@ -401,8 +417,12 @@ export interface ConvaiConfig {
401
417
  *
402
418
  * May also carry a version selector suffix (`<uuid>-draft`, `<uuid>-latest`,
403
419
  * `<uuid>-1.2`); `characterVersion` is the clearer way to say the same thing.
420
+ *
421
+ * Optional only because a multi-character room supplies `characters`
422
+ * instead. Exactly one of the two is required, and a version selector is not
423
+ * accepted inside a roster entry.
404
424
  */
405
- characterId: string;
425
+ characterId?: string;
406
426
  /**
407
427
  * Which version of the character to run (optional).
408
428
  *
@@ -431,6 +451,62 @@ export interface ConvaiConfig {
431
451
  * `https://api2-stg.convai.com` to author against staging.
432
452
  */
433
453
  characterApiUrl?: string;
454
+ /**
455
+ * Ordered roster of character instances for a multi-character room. Pass this
456
+ * *instead of* `characterId` -- exactly one of the two is valid.
457
+ *
458
+ * Every entry becomes a distinct membership with its own actor, LiveKit
459
+ * participant, voice, history and lifecycle. The same `characterId` may
460
+ * appear more than once; each copy is an independent clone addressed by its
461
+ * own `membershipId`. `characters[0]` is the initial active member.
462
+ *
463
+ * Roster rooms require the LiveKit transport, a nonblank `endUserId`, and the
464
+ * `MULTI_CHARACTER_V0` entitlement on the publisher.
465
+ *
466
+ * @example
467
+ * ```ts
468
+ * const client = new ConvaiClient({
469
+ * apiKey: 'YOUR_API_KEY',
470
+ * endUserId: 'player-42',
471
+ * characters: [
472
+ * { characterId: GUIDE }, // initial active member
473
+ * { characterId: ASSESSOR },
474
+ * { characterId: GUIDE }, // a second, independent copy of GUIDE
475
+ * ],
476
+ * });
477
+ * ```
478
+ */
479
+ characters?: ConvaiCharacterSpec[];
480
+ /**
481
+ * Publisher-scoped room grouping and topology key (1-128 chars of letters,
482
+ * digits, hyphen or underscore). A matching create request reuses the
483
+ * existing room; reusing the key with a different ordered roster, creator or
484
+ * capacity is rejected with `ROSTER_FINGERPRINT_MISMATCH`. Reordering the
485
+ * same characters counts as different, because it changes the initial member.
486
+ *
487
+ * This does not raise human capacity on its own -- see `maxNumParticipants`.
488
+ */
489
+ sharedSessionKey?: string;
490
+ /**
491
+ * Idempotency key for one logical connect (1-128 chars, same charset as
492
+ * `sharedSessionKey`). Reuse it only when retrying with an identical payload:
493
+ * within the replay window a completed attempt returns its original response
494
+ * and token, while a changed payload or a still-running original is rejected.
495
+ */
496
+ connectAttemptId?: string;
497
+ /**
498
+ * Maximum number of distinct human speakers in a roster room. Characters do
499
+ * not count toward it. Defaults to the server's value of 1, which admits no
500
+ * second human -- raise it before expecting `joinRoom()` to succeed.
501
+ */
502
+ maxNumParticipants?: number;
503
+ /**
504
+ * Bounded retry when a roster room is still provisioning (409
505
+ * `ROSTER_PROVISIONING_IN_PROGRESS`). Defaults to four attempts starting at
506
+ * 500ms and doubling with jitter. Pass `false` to surface the error instead
507
+ * and retry in application code.
508
+ */
509
+ rosterProvisioningRetry?: RosterProvisioningRetry | false;
434
510
  /** Temporary character state of mind sent as `state_of_mind` on connect. */
435
511
  stateOfMind?: string | null;
436
512
  /**
@@ -490,6 +566,13 @@ export interface ConvaiConfig {
490
566
  visionInputConfig?: VisionInputConfig;
491
567
  /** Connect-time respond-mode defaults by modality. */
492
568
  respondModes?: RespondModesConfig;
569
+ /** Client-requested action/model output protocol versions. */
570
+ capabilities?: ActionProtocolCapabilities;
571
+ /** Descriptive objects visible in the scene at connection time. */
572
+ sceneDescription?: Array<{
573
+ name: string;
574
+ description: string;
575
+ }>;
493
576
  /**
494
577
  * WebRTC ICE transport policy for the LiveKit connection (default: "relay").
495
578
  * "relay" forces TURN-only (reliable against Convai's hosted LiveKit which
@@ -599,6 +682,12 @@ export interface ConvaiConfig {
599
682
  /** Object description or properties */
600
683
  description: string;
601
684
  }>;
685
+ /** Client-executed tools negotiated by action protocol v2. */
686
+ tools?: Array<{
687
+ name: string;
688
+ description: string;
689
+ inputSchema: Record<string, unknown>;
690
+ }>;
602
691
  /** Name of the object the character is currently focused on. Must match one of objects[].name. */
603
692
  current_attention_object?: string;
604
693
  };
@@ -652,6 +741,37 @@ export interface ConvaiConfig {
652
741
  * Enable debug mode for additional logging and diagnostics (default: false).
653
742
  */
654
743
  debug?: boolean;
744
+ /** Optional per-session response lifecycle contract override. */
745
+ useResponseLifecycleContract?: boolean;
746
+ /**
747
+ * Keep links in the character's text output (default: server-side, currently off).
748
+ *
749
+ * Core-service flattens the LLM's markdown before streaming `bot-llm-text`,
750
+ * which also drops link targets: `[Doc](https://…/a.pdf)` reaches the client
751
+ * as `Doc`. With this on, the session preserves `[text](url)`, `![alt](url)`
752
+ * and bare `http(s)://` URLs verbatim (trailing sentence punctuation stays
753
+ * outside the URL, non-http(s) schemes are still stripped) while bold,
754
+ * headings, tables and code are flattened as before. Speech is unaffected —
755
+ * URLs are never read aloud.
756
+ *
757
+ * Both widgets render the preserved form as clickable links, with inline
758
+ * previews for image and video URLs. If you render `chatMessages` yourself,
759
+ * expect markdown links in `content` once this is on.
760
+ *
761
+ * Sent as `preserve_links_in_output` on `/connect`; omitted when undefined so
762
+ * the server's own default applies. If you build the `/connect` request
763
+ * server-side for `connectWithConnectionData()`, add the field there.
764
+ *
765
+ * @example
766
+ * ```typescript
767
+ * const client = new ConvaiClient({
768
+ * apiKey: 'your-api-key',
769
+ * characterId: 'your-character-id',
770
+ * preserveLinksInOutput: true,
771
+ * });
772
+ * ```
773
+ */
774
+ preserveLinksInOutput?: boolean;
655
775
  /**
656
776
  * Log decoded RTVI data messages to the browser console (default: true).
657
777
  * Set to false to silence incoming and outgoing RTVI message logs.
@@ -676,11 +796,254 @@ export interface SendUserTextMessageOptions {
676
796
  logicalTurnId?: string;
677
797
  /** User-selected temporary emotion to apply before processing the text. */
678
798
  stateOfMind?: string | null;
799
+ /** Latest request-scoped dynamic context to include in this same interaction. */
800
+ dynamicInfo?: DynamicInfo;
679
801
  }
680
802
  /**
681
803
  * The body returned by POST /connect. The embed receives this from a
682
804
  * customer-hosted proxy route rather than fetching it directly.
683
805
  */
806
+ /**
807
+ * One ordered character instance requested for a multi-character room.
808
+ *
809
+ * Repeating a `characterId` is legal and intentional: each entry becomes an
810
+ * independent clone with its own membership, session, participant and voice.
811
+ */
812
+ export interface ConvaiCharacterSpec {
813
+ /**
814
+ * Bare character UUID. Version selectors (`<uuid>-draft`, `<uuid>-1.2`) are
815
+ * accepted by single-character `characterId` but rejected inside a roster --
816
+ * the runtime types roster entries as plain UUIDs.
817
+ */
818
+ characterId: string;
819
+ /**
820
+ * Resume this instance's prior conversation history. Must be unique across
821
+ * the roster, and must belong to the same publisher, speaker and character.
822
+ */
823
+ characterSessionId?: string;
824
+ }
825
+ /**
826
+ * Dispatch state of one roster member, as reported by `/connect`.
827
+ *
828
+ * These describe provisioning only and never readiness. A member becomes
829
+ * usable when its own `bot-ready` arrives -- `dispatch_accepted` is not
830
+ * `bot-ready`. The union deliberately has no member that could be mistaken for
831
+ * a ready state; the separate `character-status` event carries those.
832
+ */
833
+ export type CharacterProvisioningStatus = "dispatch_queued" | "dispatch_accepted" | "dispatch_failed";
834
+ /** One concrete character instance in a roster room. */
835
+ export interface ConvaiCharacterInstance {
836
+ /** Primary key for this instance. Address members by this, never by `characterId`. */
837
+ membershipId: string;
838
+ /** The authored character this instance runs. Not unique across a roster. */
839
+ characterId: string;
840
+ /** Backend session for support correlation. */
841
+ sessionId: string | null;
842
+ /** This instance's conversation/history session. */
843
+ characterSessionId: string | null;
844
+ /** Stable LiveKit identity, always `character:{membershipId}`. */
845
+ participantIdentity: string;
846
+ /** True for `characters[0]`, the initially active member. */
847
+ isInitial: boolean;
848
+ provisioningStatus: CharacterProvisioningStatus;
849
+ /**
850
+ * The character's display name, when the runtime sends one. Saves a separate
851
+ * `character/get` call just to label a member in the UI. Undocumented in the
852
+ * published contract but returned by the live API, so treat it as optional.
853
+ */
854
+ displayName: string | null;
855
+ /** Character description, when the runtime sends one. */
856
+ description: string | null;
857
+ /** Populated when `provisioningStatus` is `dispatch_failed`. */
858
+ failureCode: string | null;
859
+ /** True once this member's own `bot-ready` has arrived. */
860
+ isReady: boolean;
861
+ }
862
+ /**
863
+ * Server-owned snapshot of a multi-character room. Null for single-character
864
+ * sessions.
865
+ */
866
+ export interface ConvaiRoomSession {
867
+ /** Durable room identity. Use it to join, and to correlate with support. */
868
+ roomSessionId: string;
869
+ roomName: string | null;
870
+ /** Trace id of the `/connect` that produced this room. Quote it in bug reports. */
871
+ requestTraceId: string | null;
872
+ /** Membership currently authorised to consume user turns. */
873
+ activeMembershipId: string | null;
874
+ /** Monotonic version of the active target; fences target switches. */
875
+ routeEpoch: number;
876
+ /** Monotonic version of roster composition; fences add/remove. */
877
+ rosterEpoch: number;
878
+ /** True when at least one member already failed dispatch. */
879
+ partialDispatch: boolean;
880
+ characters: ConvaiCharacterInstance[];
881
+ }
882
+ /** Result of a successful target switch. */
883
+ export interface ActiveCharacterChange {
884
+ /** Membership that was active before, or null. */
885
+ previous: string | null;
886
+ /** Membership now authorised to consume user turns, or null when all gates are closed. */
887
+ active: string | null;
888
+ routeEpoch: number;
889
+ }
890
+ /** Result of a successful roster mutation. */
891
+ export interface CharacterRosterChange {
892
+ added: ConvaiCharacterInstance[];
893
+ removed: ConvaiCharacterInstance[];
894
+ /** The roster after the change. */
895
+ characters: ConvaiCharacterInstance[];
896
+ rosterEpoch: number;
897
+ routeEpoch: number;
898
+ activeMembershipId: string | null;
899
+ }
900
+ /** Options for adding and removing roster members on a live room. */
901
+ export interface UpdateCharacterRosterOptions {
902
+ /** Members to add. Repeating an existing characterId creates another clone. */
903
+ add?: ConvaiCharacterSpec[];
904
+ /** Membership ids to remove. Never characterIds -- clones make those ambiguous. */
905
+ remove?: string[];
906
+ /**
907
+ * Member to take over routing when the active one is removed. Must be a ready
908
+ * member that survives the change. Chosen automatically when omitted.
909
+ */
910
+ replacementTarget?: string;
911
+ }
912
+ /** Options for joining an existing multi-character room. */
913
+ export interface JoinRoomOptions {
914
+ /** Preferred locator. Exactly one of this or `sharedSessionKey`. */
915
+ roomSessionId?: string;
916
+ /** Publisher-scoped alternative locator. */
917
+ sharedSessionKey?: string;
918
+ /** Required and nonblank; identifies the joining human. */
919
+ endUserId: string;
920
+ endUserMetadata?: Record<string, unknown>;
921
+ /** Idempotency key for retrying this join. */
922
+ connectAttemptId?: string;
923
+ }
924
+ /**
925
+ * Lifecycle transition of one roster member, after `/connect` has returned.
926
+ *
927
+ * `bot-ready` remains the readiness authority: do not enable interaction from a
928
+ * `ready` status alone. This event's real job is reporting a member that was
929
+ * added at runtime, or one whose dispatch failed in the background.
930
+ */
931
+ export interface CharacterStatusEvent {
932
+ status: "starting" | "ready" | "failed";
933
+ membershipId: string;
934
+ characterId: string;
935
+ characterSessionId: string | null;
936
+ participantIdentity: string;
937
+ roomSessionId: string | null;
938
+ rosterEpoch: number | null;
939
+ /** Set when `status` is `failed`, e.g. `ACTOR_ASSIGNMENT_FAILED`. */
940
+ failureCode: string | null;
941
+ }
942
+ /**
943
+ * An audio track bound to the character instance that publishes it.
944
+ *
945
+ * In a multi-character room `membershipId` is always set -- a track that
946
+ * cannot be attributed to a roster member is dropped, never surfaced against
947
+ * the active character. In a single-character room it is null.
948
+ */
949
+ export interface CharacterAudioTrackEvent {
950
+ membershipId: string | null;
951
+ characterId: string | null;
952
+ /** LiveKit participant identity; `character:{membershipId}` in a roster. */
953
+ participantIdentity: string;
954
+ track: MediaStreamTrack;
955
+ }
956
+ /**
957
+ * A finalized turn's usage/cost breakdown, streamed by the server after
958
+ * {@link IConvaiClient.toggleUsageUpdates} enables it. The shape is
959
+ * server-defined and informational; treat unknown fields as opaque.
960
+ */
961
+ export type UsageUpdate = Record<string, unknown>;
962
+ /**
963
+ * Every event the client emits, with its payload.
964
+ *
965
+ * `on()` is typed against this map, so a misspelt event name or a listener
966
+ * with the wrong signature fails to compile. Payloads the server defines as
967
+ * opaque blobs (blendshape frames, metrics, usage) are left as records rather
968
+ * than guessed at.
969
+ */
970
+ export interface ConvaiEventMap {
971
+ connect: [];
972
+ connected: [];
973
+ disconnect: [reason: DisconnectReason | null];
974
+ disconnected: [];
975
+ error: [error: unknown];
976
+ stateChange: [state: ConvaiClientState];
977
+ botReady: [];
978
+ characterSessionId: [characterSessionId: string];
979
+ interactionCreated: [data: InteractionCreated];
980
+ message: [message: ChatMessage];
981
+ messagesChange: [messages: ChatMessage[]];
982
+ userTranscriptionChange: [transcription: string];
983
+ speakingChange: [isSpeaking: boolean];
984
+ listeningChange: [isListening: boolean];
985
+ botRespondingChange: [isResponding: boolean];
986
+ turnEnd: [turn: {
987
+ sessionId: number;
988
+ duration: number;
989
+ timestamp: number;
990
+ }];
991
+ conversationStart: [
992
+ start: {
993
+ sessionId: number;
994
+ userMessage: string;
995
+ timestamp: number;
996
+ }
997
+ ];
998
+ emotionChange: [emotion: {
999
+ emotion: string;
1000
+ scale?: number;
1001
+ } | null];
1002
+ actionResponse: [data: ActionResponseEvent];
1003
+ serverResponse: [response: ServerResponse];
1004
+ botOutput: [data: Record<string, unknown>];
1005
+ botTtsText: [data: {
1006
+ text: string;
1007
+ }];
1008
+ botTtsStarted: [];
1009
+ botTtsStopped: [];
1010
+ botTurnCompleted: [data: unknown];
1011
+ userMuteStarted: [];
1012
+ userMuteStopped: [];
1013
+ idleWarning: [data: {
1014
+ remainingSeconds: number | null;
1015
+ }];
1016
+ llmNoResponse: [];
1017
+ metrics: [data: Record<string, unknown>];
1018
+ usageUpdate: [data: UsageUpdate];
1019
+ blendshapes: [data: Record<string, unknown>];
1020
+ blendshapeCancel: [data: Record<string, unknown>];
1021
+ blendshapeStatsReceived: [stats: Record<string, unknown>];
1022
+ botAudioTrack: [track: MediaStreamTrack];
1023
+ audioStateChange: [enabled: boolean];
1024
+ videoStateChange: [enabled: boolean];
1025
+ screenShareStateChange: [active: boolean];
1026
+ characterReady: [instance: ConvaiCharacterInstance | undefined];
1027
+ characterStatus: [status: CharacterStatusEvent];
1028
+ activeCharacterChange: [change: ActiveCharacterChange];
1029
+ rosterChange: [change: CharacterRosterChange];
1030
+ characterAudioTrack: [event: CharacterAudioTrackEvent];
1031
+ }
1032
+ /** A transcript line inserted locally by {@link IConvaiClient.appendMessage}. */
1033
+ export interface AppendMessageOptions {
1034
+ sender: "user" | "bot";
1035
+ content: string;
1036
+ /** Attribute the line to a roster member, in a multi-character room. */
1037
+ membershipId?: string;
1038
+ characterId?: string;
1039
+ }
1040
+ /** Bounded retry for a roster room that is still provisioning. */
1041
+ export interface RosterProvisioningRetry {
1042
+ /** Total attempts including the first. Default 4. */
1043
+ attempts?: number;
1044
+ /** Delay before the second attempt; doubles with jitter. Default 500. */
1045
+ initialDelayMs?: number;
1046
+ }
684
1047
  export interface ConnectionData {
685
1048
  /** LiveKit room URL, or the WebSocket URL when the transport is websocket. */
686
1049
  room_url: string;
@@ -714,6 +1077,14 @@ export interface ChatMessage {
714
1077
  * - `llm-no-response`: LLM deliberately did not respond (abstain); `content` is always `""`
715
1078
  */
716
1079
  type: 'user' | 'convai' | 'emotion' | 'behavior-tree' | 'action' | 'user-transcription' | 'bot-llm-text' | 'bot-output' | 'bot-emotion' | 'user-llm-text' | 'interrupt-bot' | 'idle-warning' | 'llm-no-response';
1080
+ /**
1081
+ * Which character instance produced this message, in a multi-character room.
1082
+ * Undefined in single-character sessions. Attribute UI to this, never to
1083
+ * `characterId` -- two clones of one character share a `characterId`.
1084
+ */
1085
+ membershipId?: string;
1086
+ /** The authored character behind {@link membershipId}. Not unique across a roster. */
1087
+ characterId?: string;
717
1088
  /** The text content of the message */
718
1089
  content: string;
719
1090
  /** ISO timestamp string of when the message was created */
@@ -819,6 +1190,16 @@ export interface ConvaiClientState {
819
1190
  * @see DisconnectReason
820
1191
  */
821
1192
  disconnectReason: DisconnectReason | null;
1193
+ /**
1194
+ * Character instances in a multi-character room, in roster order. Empty for
1195
+ * single-character sessions, so a check on `length` distinguishes the two.
1196
+ */
1197
+ characters: ConvaiCharacterInstance[];
1198
+ /**
1199
+ * Membership currently receiving user turns, or null. Only this member
1200
+ * consumes interaction input.
1201
+ */
1202
+ activeMembershipId: string | null;
822
1203
  }
823
1204
  /**
824
1205
  * Audio control interface for managing microphone
@@ -836,7 +1217,10 @@ export interface AudioControls {
836
1217
  getAudioDevices: () => Promise<MediaDeviceInfo[]>;
837
1218
  startAudioLevelMonitoring: () => void;
838
1219
  stopAudioLevelMonitoring: () => void;
839
- on: (event: string, callback: (...args: any[]) => void) => () => void;
1220
+ on: {
1221
+ <E extends keyof ConvaiEventMap>(event: E, callback: (...args: ConvaiEventMap[E]) => void): () => void;
1222
+ (event: string, callback: (...args: any[]) => void): () => void;
1223
+ };
840
1224
  off: (event: string, callback: (...args: any[]) => void) => void;
841
1225
  }
842
1226
  /**
@@ -859,7 +1243,10 @@ export interface VideoControls {
859
1243
  publishCanvas: (canvas: HTMLCanvasElement, options?: PublishCanvasOptions) => Promise<VisionSourceHandle>;
860
1244
  publishVideoTrack: (track: MediaStreamTrack, options?: PublishVideoTrackOptions) => Promise<VisionSourceHandle>;
861
1245
  unpublishVisionSource: (source?: VisionSourceHandle | MediaStreamTrack) => Promise<void>;
862
- on: (event: string, callback: (...args: any[]) => void) => () => void;
1246
+ on: {
1247
+ <E extends keyof ConvaiEventMap>(event: E, callback: (...args: ConvaiEventMap[E]) => void): () => void;
1248
+ (event: string, callback: (...args: any[]) => void): () => void;
1249
+ };
863
1250
  off: (event: string, callback: (...args: any[]) => void) => void;
864
1251
  }
865
1252
  /**
@@ -873,7 +1260,10 @@ export interface ScreenShareControls {
873
1260
  toggleScreenShare: () => Promise<void>;
874
1261
  enableScreenShareWithAudio: () => Promise<void>;
875
1262
  getScreenShareTracks: () => Promise<any[]>;
876
- on: (event: string, callback: (...args: any[]) => void) => () => void;
1263
+ on: {
1264
+ <E extends keyof ConvaiEventMap>(event: E, callback: (...args: ConvaiEventMap[E]) => void): () => void;
1265
+ (event: string, callback: (...args: any[]) => void): () => void;
1266
+ };
877
1267
  off: (event: string, callback: (...args: any[]) => void) => void;
878
1268
  }
879
1269
  /**
@@ -1336,6 +1726,70 @@ export interface IConvaiClient {
1336
1726
  * await client.connectWithConnectionData(data)
1337
1727
  */
1338
1728
  connectWithConnectionData: (data: ConnectionData, config?: ConvaiConfig) => Promise<void>;
1729
+ /**
1730
+ * Join an existing multi-character room by `roomSessionId` or
1731
+ * `sharedSessionKey`. The roster is server-owned, so no character topology is
1732
+ * sent; joining never spawns, removes or repairs a character.
1733
+ *
1734
+ * @example
1735
+ * ```ts
1736
+ * await client.joinRoom({ roomSessionId: 'the-room-id', endUserId: 'player-99' })
1737
+ * ```
1738
+ */
1739
+ joinRoom: (options: JoinRoomOptions) => Promise<void>;
1740
+ /** Server-owned snapshot of a multi-character room (null when single-character) */
1741
+ readonly roomSession: ConvaiRoomSession | null;
1742
+ /** Character instances in roster order; empty for a single-character session */
1743
+ readonly characters: ConvaiCharacterInstance[];
1744
+ /** Membership currently receiving user turns, or null */
1745
+ readonly activeMembershipId: string | null;
1746
+ /**
1747
+ * Insert a message into the transcript locally, without sending it to the
1748
+ * character. Use it for client-side lines -- a greeting, a system notice,
1749
+ * replayed history -- that must appear in the chat but must never reach
1750
+ * the LLM or be billed as a turn. Not `sendUserTextMessage`: nothing is
1751
+ * transmitted. Returns the message that was added.
1752
+ *
1753
+ * @example
1754
+ * ```ts
1755
+ * client.appendMessage({ sender: 'bot', content: 'Welcome back!' })
1756
+ * ```
1757
+ */
1758
+ appendMessage: (message: AppendMessageOptions) => ChatMessage;
1759
+ /**
1760
+ * Opt in or out of `usageUpdate` events -- the server's per-turn usage and
1761
+ * cost breakdown. Informational only; billing is unaffected.
1762
+ *
1763
+ * @example
1764
+ * ```ts
1765
+ * client.toggleUsageUpdates(true)
1766
+ * client.on('usageUpdate', (u) => console.log(u))
1767
+ * ```
1768
+ */
1769
+ toggleUsageUpdates: (enabled: boolean) => void;
1770
+ /**
1771
+ * Route subsequent user turns to a different character instance. Pass null to
1772
+ * close every input gate so no member hears the user.
1773
+ *
1774
+ * Resolves on the server's acknowledgement. Retries once against the
1775
+ * server's authoritative epoch when another participant switched first.
1776
+ *
1777
+ * @example
1778
+ * ```ts
1779
+ * await client.setInteractionTarget(assessor.membershipId)
1780
+ * client.sendUserTextMessage('Now grade me.')
1781
+ * ```
1782
+ */
1783
+ setInteractionTarget: (membershipId: string | null) => Promise<ActiveCharacterChange>;
1784
+ /**
1785
+ * Add or remove character instances on a live room, without reconnecting.
1786
+ *
1787
+ * @example
1788
+ * ```ts
1789
+ * await client.updateCharacterRoster({ add: [{ characterId: NARRATOR }] })
1790
+ * ```
1791
+ */
1792
+ updateCharacterRoster: (options: UpdateCharacterRosterOptions) => Promise<CharacterRosterChange>;
1339
1793
  /** Disconnect from the current character session */
1340
1794
  disconnect: () => Promise<void>;
1341
1795
  /** Reconnect - disconnect and connect again using stored config */
@@ -1472,7 +1926,10 @@ export interface IConvaiClient {
1472
1926
  * @param callback Callback function
1473
1927
  * @returns Unsubscribe function
1474
1928
  */
1475
- on: (event: string, callback: (...args: any[]) => void) => () => void;
1929
+ on: {
1930
+ <E extends keyof ConvaiEventMap>(event: E, callback: (...args: ConvaiEventMap[E]) => void): () => void;
1931
+ (event: string, callback: (...args: any[]) => void): () => void;
1932
+ };
1476
1933
  /**
1477
1934
  * Unsubscribe from an event
1478
1935
  * @param event Event name