@rtsdk/topia 0.19.5 → 0.19.7

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.
package/dist/index.cjs CHANGED
@@ -39751,6 +39751,11 @@ class SDKController {
39751
39751
  errorMessage = (error === null || error === void 0 ? void 0 : error.message) || message;
39752
39752
  stack = `${error.stack}\n${stackTrace.stack}`;
39753
39753
  }
39754
+ else if (error !== undefined && error !== null) {
39755
+ // Handle bare string throws and other non-Error types
39756
+ errorMessage = typeof error === "string" ? error : JSON.stringify(error);
39757
+ stack = stackTrace.stack || "empty";
39758
+ }
39754
39759
  return {
39755
39760
  data,
39756
39761
  message: errorMessage,
@@ -42258,6 +42263,7 @@ AI RULES for code assistants
42258
42263
  */
42259
42264
  class User extends SDKController {
42260
42265
  constructor(topia, options = { profileId: null, credentials: {} }) {
42266
+ var _a;
42261
42267
  super(topia, Object.assign({ profileId: options === null || options === void 0 ? void 0 : options.profileId }, options.credentials));
42262
42268
  _User_userInventoryItems.set(this, void 0);
42263
42269
  _User_adminWorldsMap.set(this, void 0);
@@ -42265,6 +42271,13 @@ class User extends SDKController {
42265
42271
  _User_scenesMap.set(this, void 0);
42266
42272
  _User_worldsMap.set(this, void 0);
42267
42273
  this.profileId = options === null || options === void 0 ? void 0 : options.profileId;
42274
+ // Warn devs about common mistake: passing profileId only inside credentials
42275
+ if (!this.profileId && ((_a = options === null || options === void 0 ? void 0 : options.credentials) === null || _a === void 0 ? void 0 : _a.profileId)) {
42276
+ console.warn(`[@rtsdk/topia] User created without top-level profileId. ` +
42277
+ `You passed profileId inside credentials but not in options. ` +
42278
+ `Methods like grantInventoryItem will fail. ` +
42279
+ `Fix: User.create({ profileId: "${options.credentials.profileId}", credentials: { ... } })`);
42280
+ }
42268
42281
  this.dataObject = {};
42269
42282
  this.profile = {};
42270
42283
  __classPrivateFieldSet(this, _User_adminWorldsMap, {}, "f");
@@ -42778,7 +42791,7 @@ class User extends SDKController {
42778
42791
  return __awaiter(this, void 0, void 0, function* () {
42779
42792
  try {
42780
42793
  if (!this.profileId)
42781
- throw "This method requires the use of a profileId";
42794
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42782
42795
  let query = "";
42783
42796
  if (appPublicKey)
42784
42797
  query = `?appPublicKey=${appPublicKey}&appJWT=${appJWT}`;
@@ -42818,7 +42831,7 @@ class User extends SDKController {
42818
42831
  return __awaiter(this, void 0, void 0, function* () {
42819
42832
  try {
42820
42833
  if (!this.profileId)
42821
- throw "This method requires the use of a profileId";
42834
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42822
42835
  yield this.topiaPublicApi().put(`/user/dataObjects/${this.profileId}/set-data-object`, Object.assign(Object.assign({}, options), { dataObject: dataObject || this.dataObject }), this.requestOptions);
42823
42836
  this.dataObject = dataObject || this.dataObject;
42824
42837
  }
@@ -42852,7 +42865,7 @@ class User extends SDKController {
42852
42865
  return __awaiter(this, void 0, void 0, function* () {
42853
42866
  try {
42854
42867
  if (!this.profileId)
42855
- throw "This method requires the use of a profileId";
42868
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42856
42869
  yield this.topiaPublicApi().put(`/user/dataObjects/${this.profileId}/update-data-object`, Object.assign(Object.assign({}, options), { dataObject: dataObject || this.dataObject }), this.requestOptions);
42857
42870
  this.dataObject = Object.assign(Object.assign({}, (this.dataObject || {})), (dataObject || {}));
42858
42871
  }
@@ -42902,7 +42915,7 @@ class User extends SDKController {
42902
42915
  return __awaiter(this, void 0, void 0, function* () {
42903
42916
  try {
42904
42917
  if (!this.profileId)
42905
- throw "This method requires the use of a profileId";
42918
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42906
42919
  const response = yield this.topiaPublicApi().get(`/user/inventory/${this.profileId}/get-user-inventory-items`, this.requestOptions);
42907
42920
  // TODO: Replace 'object' with InventoryItem and instantiate InventoryItem objects if needed
42908
42921
  const tempItems = [];
@@ -42915,7 +42928,7 @@ class User extends SDKController {
42915
42928
  __classPrivateFieldSet(this, _User_userInventoryItems, tempItems, "f");
42916
42929
  }
42917
42930
  catch (error) {
42918
- throw this.errorHandler({ error, sdkMethod: "Visitor.fetchInventoryItems" });
42931
+ throw this.errorHandler({ error, sdkMethod: "User.fetchInventoryItems" });
42919
42932
  }
42920
42933
  });
42921
42934
  }
@@ -42939,7 +42952,7 @@ class User extends SDKController {
42939
42952
  return __awaiter(this, void 0, void 0, function* () {
42940
42953
  try {
42941
42954
  if (!this.profileId)
42942
- throw "This method requires the use of a profileId";
42955
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42943
42956
  const response = yield this.topiaPublicApi().put(`/user/inventory/${this.profileId}/grant-user-inventory-item`, { itemId: item.id, quantity }, this.requestOptions);
42944
42957
  const userInventoryItemFactory = new UserInventoryItemFactory(this.topia);
42945
42958
  const { inventoryItem, user_id, quantity: newQuantity } = response.data;
@@ -42949,7 +42962,7 @@ class User extends SDKController {
42949
42962
  });
42950
42963
  }
42951
42964
  catch (error) {
42952
- throw this.errorHandler({ error, sdkMethod: "Visitor.grantInventoryItem" });
42965
+ throw this.errorHandler({ error, sdkMethod: "User.grantInventoryItem" });
42953
42966
  }
42954
42967
  });
42955
42968
  }
@@ -42970,7 +42983,7 @@ class User extends SDKController {
42970
42983
  return __awaiter(this, void 0, void 0, function* () {
42971
42984
  try {
42972
42985
  if (!this.profileId)
42973
- throw "This method requires the use of a profileId";
42986
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42974
42987
  const response = yield this.topiaPublicApi().put(`/user/inventory/${this.profileId}/update-user-inventory-item-quantity`, { userItemId: item.id, itemId: item.item_id, quantity }, this.requestOptions);
42975
42988
  const userInventoryItemFactory = new UserInventoryItemFactory(this.topia);
42976
42989
  const { inventoryItem, user_id, quantity: newQuantity } = response.data;
@@ -42980,7 +42993,7 @@ class User extends SDKController {
42980
42993
  });
42981
42994
  }
42982
42995
  catch (error) {
42983
- throw this.errorHandler({ error, sdkMethod: "Visitor.modifyInventoryItemQuantity" });
42996
+ throw this.errorHandler({ error, sdkMethod: "User.modifyInventoryItemQuantity" });
42984
42997
  }
42985
42998
  });
42986
42999
  }
@@ -43665,7 +43678,13 @@ class Visitor extends User {
43665
43678
  createNpc(userInventoryItemId, options) {
43666
43679
  return __awaiter(this, void 0, void 0, function* () {
43667
43680
  try {
43668
- const response = yield this.topiaPublicApi().post(`/world/${this.urlSlug}/visitors/${this.id}/create-npc`, { userInventoryItemId, showNameplate: options === null || options === void 0 ? void 0 : options.showNameplate }, this.requestOptions);
43681
+ const response = yield this.topiaPublicApi().post(`/world/${this.urlSlug}/visitors/${this.id}/create-npc`, {
43682
+ userInventoryItemId,
43683
+ showNameplate: options === null || options === void 0 ? void 0 : options.showNameplate,
43684
+ stationary: options === null || options === void 0 ? void 0 : options.stationary,
43685
+ replace: options === null || options === void 0 ? void 0 : options.replace,
43686
+ spawnEffect: options === null || options === void 0 ? void 0 : options.spawnEffect,
43687
+ }, this.requestOptions);
43669
43688
  return new Visitor(this.topia, response.data.player.playerId, this.urlSlug, {
43670
43689
  attributes: response.data,
43671
43690
  credentials: this.credentials,
@@ -43710,6 +43729,77 @@ class Visitor extends User {
43710
43729
  }
43711
43730
  });
43712
43731
  }
43732
+ /**
43733
+ * Start an AI voice session for this visitor's NPC.
43734
+ *
43735
+ * @remarks
43736
+ * Establishes a real-time voice connection between the visitor and an AI backend
43737
+ * (currently OpenAI Realtime API) through the NPC. The NPC must already be spawned
43738
+ * via `createNpc()` before calling this method.
43739
+ *
43740
+ * The voice session occupies a video slot in the visitor's peer video grid, showing
43741
+ * the NPC's avatar image. Audio streams bidirectionally between the visitor's microphone
43742
+ * and the AI model. Only the NPC's owner hears the AI audio.
43743
+ *
43744
+ * Topia automatically prepends non-removable child safety guardrails to the instructions.
43745
+ * Only one voice session is allowed per visitor per world at a time.
43746
+ *
43747
+ * @keywords voice, npc, ai, chat, audio, realtime, session, start, speech
43748
+ *
43749
+ * @category NPCs
43750
+ *
43751
+ * @param config - Voice session configuration including ephemeral key and AI instructions
43752
+ *
43753
+ * @example
43754
+ * ```ts
43755
+ * const ephemeralKey = await generateOpenAIEphemeralKey();
43756
+ *
43757
+ * await visitor.startNpcVoiceSession({
43758
+ * ephemeralKey,
43759
+ * voice: "alloy",
43760
+ * instructions: "You are a friendly science tutor helping with photosynthesis.",
43761
+ * model: "gpt-4o-realtime-preview",
43762
+ * });
43763
+ * ```
43764
+ *
43765
+ * @returns {Promise<void | ResponseType>} Returns `{ success: true }` or an error.
43766
+ */
43767
+ startNpcVoiceSession(config) {
43768
+ return __awaiter(this, void 0, void 0, function* () {
43769
+ try {
43770
+ const response = yield this.topiaPublicApi().put(`/world/${this.urlSlug}/visitors/${this.id}/start-npc-voice-session`, { voiceConfig: config }, this.requestOptions);
43771
+ return response.data;
43772
+ }
43773
+ catch (error) {
43774
+ throw this.errorHandler({ error, params: config, sdkMethod: "Visitor.startNpcVoiceSession" });
43775
+ }
43776
+ });
43777
+ }
43778
+ /**
43779
+ * Stop the active AI voice session for this visitor's NPC.
43780
+ *
43781
+ * @keywords voice, npc, ai, chat, audio, realtime, session, stop, end
43782
+ *
43783
+ * @category NPCs
43784
+ *
43785
+ * @example
43786
+ * ```ts
43787
+ * await visitor.stopNpcVoiceSession();
43788
+ * ```
43789
+ *
43790
+ * @returns {Promise<void | ResponseType>} Returns `{ success: true }` or an error.
43791
+ */
43792
+ stopNpcVoiceSession() {
43793
+ return __awaiter(this, void 0, void 0, function* () {
43794
+ try {
43795
+ const response = yield this.topiaPublicApi().put(`/world/${this.urlSlug}/visitors/${this.id}/stop-npc-voice-session`, {}, this.requestOptions);
43796
+ return response.data;
43797
+ }
43798
+ catch (error) {
43799
+ throw this.errorHandler({ error, sdkMethod: "Visitor.stopNpcVoiceSession" });
43800
+ }
43801
+ });
43802
+ }
43713
43803
  /**
43714
43804
  * Retrieves all inventory items owned by this visitor and app's key.
43715
43805
  *
package/dist/index.d.ts CHANGED
@@ -2160,9 +2160,7 @@ declare class Visitor extends User implements VisitorInterface {
2160
2160
  *
2161
2161
  * @returns {Promise<Visitor>} Returns a Visitor object representing the created NPC. The NPC will automatically follow the visitor.
2162
2162
  */
2163
- createNpc(userInventoryItemId: string, options?: {
2164
- showNameplate?: boolean;
2165
- }): Promise<Visitor>;
2163
+ createNpc(userInventoryItemId: string, options?: CreateNpcOptions): Promise<Visitor>;
2166
2164
  /**
2167
2165
  * Deletes the NPC (Non-Player Character) this app has assigned to this visitor.
2168
2166
  *
@@ -2188,6 +2186,57 @@ declare class Visitor extends User implements VisitorInterface {
2188
2186
  * @returns {Promise<void>} Returns nothing if successful.
2189
2187
  */
2190
2188
  deleteNpc(): Promise<void>;
2189
+ /**
2190
+ * Start an AI voice session for this visitor's NPC.
2191
+ *
2192
+ * @remarks
2193
+ * Establishes a real-time voice connection between the visitor and an AI backend
2194
+ * (currently OpenAI Realtime API) through the NPC. The NPC must already be spawned
2195
+ * via `createNpc()` before calling this method.
2196
+ *
2197
+ * The voice session occupies a video slot in the visitor's peer video grid, showing
2198
+ * the NPC's avatar image. Audio streams bidirectionally between the visitor's microphone
2199
+ * and the AI model. Only the NPC's owner hears the AI audio.
2200
+ *
2201
+ * Topia automatically prepends non-removable child safety guardrails to the instructions.
2202
+ * Only one voice session is allowed per visitor per world at a time.
2203
+ *
2204
+ * @keywords voice, npc, ai, chat, audio, realtime, session, start, speech
2205
+ *
2206
+ * @category NPCs
2207
+ *
2208
+ * @param config - Voice session configuration including ephemeral key and AI instructions
2209
+ *
2210
+ * @example
2211
+ * ```ts
2212
+ * const ephemeralKey = await generateOpenAIEphemeralKey();
2213
+ *
2214
+ * await visitor.startNpcVoiceSession({
2215
+ * ephemeralKey,
2216
+ * voice: "alloy",
2217
+ * instructions: "You are a friendly science tutor helping with photosynthesis.",
2218
+ * model: "gpt-4o-realtime-preview",
2219
+ * });
2220
+ * ```
2221
+ *
2222
+ * @returns {Promise<void | ResponseType>} Returns `{ success: true }` or an error.
2223
+ */
2224
+ startNpcVoiceSession(config: NpcVoiceConfigInterface): Promise<void | ResponseType>;
2225
+ /**
2226
+ * Stop the active AI voice session for this visitor's NPC.
2227
+ *
2228
+ * @keywords voice, npc, ai, chat, audio, realtime, session, stop, end
2229
+ *
2230
+ * @category NPCs
2231
+ *
2232
+ * @example
2233
+ * ```ts
2234
+ * await visitor.stopNpcVoiceSession();
2235
+ * ```
2236
+ *
2237
+ * @returns {Promise<void | ResponseType>} Returns `{ success: true }` or an error.
2238
+ */
2239
+ stopNpcVoiceSession(): Promise<void | ResponseType>;
2191
2240
  /**
2192
2241
  * Retrieves all inventory items owned by this visitor and app's key.
2193
2242
  *
@@ -2649,11 +2698,11 @@ interface VisitorInterface extends SDKInterface {
2649
2698
  grantInventoryItem(item: InventoryItemInterface, quantity: number): Promise<UserInventoryItem>;
2650
2699
  modifyInventoryItemQuantity(item: UserInventoryItemInterface | InventoryItemInterface, quantity: number): Promise<UserInventoryItem>;
2651
2700
  fetchInventoryItem(item: InventoryItemInterface): Promise<UserInventoryItem>;
2652
- createNpc(userInventoryItemId: string, options?: {
2653
- showNameplate?: boolean;
2654
- }): Promise<Visitor>;
2701
+ createNpc(userInventoryItemId: string, options?: CreateNpcOptions): Promise<Visitor>;
2655
2702
  deleteNpc(): Promise<void>;
2656
2703
  getNpc(): Promise<Visitor | null>;
2704
+ startNpcVoiceSession(config: NpcVoiceConfigInterface): Promise<void | ResponseType>;
2705
+ stopNpcVoiceSession(): Promise<void | ResponseType>;
2657
2706
  triggerParticle({ id, name, duration, }: {
2658
2707
  id?: string;
2659
2708
  name?: string;
@@ -2710,6 +2759,39 @@ interface OpenIframeInterface {
2710
2759
  shouldOpenInDrawer?: boolean;
2711
2760
  title?: string;
2712
2761
  }
2762
+ interface SpawnEffectConfig {
2763
+ type?: "portal" | "none";
2764
+ colors?: number[];
2765
+ glowColor?: number;
2766
+ glowOpacity?: number;
2767
+ centerColor?: number;
2768
+ duration?: number;
2769
+ sectorCount?: number;
2770
+ gridSize?: number;
2771
+ }
2772
+ interface CreateNpcOptions {
2773
+ showNameplate?: boolean;
2774
+ stationary?: boolean;
2775
+ replace?: boolean;
2776
+ spawnEffect?: SpawnEffectConfig;
2777
+ }
2778
+ interface NpcVoiceConfigInterface {
2779
+ /** OpenAI ephemeral key (ek_*). Generated server-side, used once to establish WebRTC connection. */
2780
+ ephemeralKey: string;
2781
+ /** OpenAI voice ID (e.g., "alloy", "echo", "shimmer"). */
2782
+ voice: string;
2783
+ /** System prompt including curriculum context and behavioral instructions. */
2784
+ instructions: string;
2785
+ /** OpenAI model ID. Defaults to "gpt-4o-realtime-preview". */
2786
+ model?: string;
2787
+ /** Voice activity detection configuration. */
2788
+ turnDetection?: {
2789
+ type: "server_vad";
2790
+ threshold?: number;
2791
+ prefix_padding_ms?: number;
2792
+ silence_duration_ms?: number;
2793
+ };
2794
+ }
2713
2795
 
2714
2796
  interface WebhookInterface {
2715
2797
  webhookId?: string;
@@ -4185,4 +4267,4 @@ declare class WorldFactory extends SDKController {
4185
4267
  }>;
4186
4268
  }
4187
4269
 
4188
- export { AnalyticType, AnimationMetaType, Asset, AssetFactory, AssetInterface, AssetOptionalInterface, AssetOptions, AssetType, DroppedAsset, DroppedAssetClickType, DroppedAssetFactory, DroppedAssetInterface, DroppedAssetLinkType, DroppedAssetMediaType, DroppedAssetMediaVolumeRadius, DroppedAssetOptionalInterface, DroppedAssetOptions, Ecosystem, EcosystemFactory, EcosystemInterface, EcosystemOptionalInterface, FireToastInterface, FrameType, InteractiveCredentials$1 as InteractiveCredentials, InventoryItemInterface, InventoryItemOptionalInterface, MoveAllVisitorsInterface, MoveVisitorInterface, OpenIframeInterface, RemoveClickableLinkInterface, ResponseType, SDKController, SDKInterface, Scene, SceneFactory, SceneInterface, SceneOptionalInterface, SetClickableLinkMultiInterface, Topia, TopiaInterface, UpdateBroadcastInterface, UpdateClickTypeInterface, UpdateClickableLinkMultiInterface, UpdateDroppedAssetInterface, UpdateMediaTypeInterface, UpdatePrivateZoneInterface, User, UserFactory, UserInterface, UserInventoryItemInterface, UserInventoryItemMetadataType, UserInventoryItemOptionalInterface, UserOptionalInterface, UserOptions, Visitor, VisitorFactory, VisitorInterface, VisitorOptionalInterface, VisitorOptions, VisitorType, VisitorsToMoveArrayType, VisitorsToMoveType, WebRTCConnector, WebRTCConnectorFactory, WebRTCConnectorInterface, WebRTCConnectorOptionalInterface, WebhookInterface, World, WorldActivity, WorldActivityFactory, WorldActivityOptionalInterface, WorldActivityType, WorldDetailsInterface, WorldFactory, WorldInterface, WorldOptionalInterface, WorldOptions, WorldWebhooksInterface };
4270
+ export { AnalyticType, AnimationMetaType, Asset, AssetFactory, AssetInterface, AssetOptionalInterface, AssetOptions, AssetType, CreateNpcOptions, DroppedAsset, DroppedAssetClickType, DroppedAssetFactory, DroppedAssetInterface, DroppedAssetLinkType, DroppedAssetMediaType, DroppedAssetMediaVolumeRadius, DroppedAssetOptionalInterface, DroppedAssetOptions, Ecosystem, EcosystemFactory, EcosystemInterface, EcosystemOptionalInterface, FireToastInterface, FrameType, InteractiveCredentials$1 as InteractiveCredentials, InventoryItemInterface, InventoryItemOptionalInterface, MoveAllVisitorsInterface, MoveVisitorInterface, NpcVoiceConfigInterface, OpenIframeInterface, RemoveClickableLinkInterface, ResponseType, SDKController, SDKInterface, Scene, SceneFactory, SceneInterface, SceneOptionalInterface, SetClickableLinkMultiInterface, SpawnEffectConfig, Topia, TopiaInterface, UpdateBroadcastInterface, UpdateClickTypeInterface, UpdateClickableLinkMultiInterface, UpdateDroppedAssetInterface, UpdateMediaTypeInterface, UpdatePrivateZoneInterface, User, UserFactory, UserInterface, UserInventoryItemInterface, UserInventoryItemMetadataType, UserInventoryItemOptionalInterface, UserOptionalInterface, UserOptions, Visitor, VisitorFactory, VisitorInterface, VisitorOptionalInterface, VisitorOptions, VisitorType, VisitorsToMoveArrayType, VisitorsToMoveType, WebRTCConnector, WebRTCConnectorFactory, WebRTCConnectorInterface, WebRTCConnectorOptionalInterface, WebhookInterface, World, WorldActivity, WorldActivityFactory, WorldActivityOptionalInterface, WorldActivityType, WorldDetailsInterface, WorldFactory, WorldInterface, WorldOptionalInterface, WorldOptions, WorldWebhooksInterface };
package/dist/index.js CHANGED
@@ -39749,6 +39749,11 @@ class SDKController {
39749
39749
  errorMessage = (error === null || error === void 0 ? void 0 : error.message) || message;
39750
39750
  stack = `${error.stack}\n${stackTrace.stack}`;
39751
39751
  }
39752
+ else if (error !== undefined && error !== null) {
39753
+ // Handle bare string throws and other non-Error types
39754
+ errorMessage = typeof error === "string" ? error : JSON.stringify(error);
39755
+ stack = stackTrace.stack || "empty";
39756
+ }
39752
39757
  return {
39753
39758
  data,
39754
39759
  message: errorMessage,
@@ -42256,6 +42261,7 @@ AI RULES for code assistants
42256
42261
  */
42257
42262
  class User extends SDKController {
42258
42263
  constructor(topia, options = { profileId: null, credentials: {} }) {
42264
+ var _a;
42259
42265
  super(topia, Object.assign({ profileId: options === null || options === void 0 ? void 0 : options.profileId }, options.credentials));
42260
42266
  _User_userInventoryItems.set(this, void 0);
42261
42267
  _User_adminWorldsMap.set(this, void 0);
@@ -42263,6 +42269,13 @@ class User extends SDKController {
42263
42269
  _User_scenesMap.set(this, void 0);
42264
42270
  _User_worldsMap.set(this, void 0);
42265
42271
  this.profileId = options === null || options === void 0 ? void 0 : options.profileId;
42272
+ // Warn devs about common mistake: passing profileId only inside credentials
42273
+ if (!this.profileId && ((_a = options === null || options === void 0 ? void 0 : options.credentials) === null || _a === void 0 ? void 0 : _a.profileId)) {
42274
+ console.warn(`[@rtsdk/topia] User created without top-level profileId. ` +
42275
+ `You passed profileId inside credentials but not in options. ` +
42276
+ `Methods like grantInventoryItem will fail. ` +
42277
+ `Fix: User.create({ profileId: "${options.credentials.profileId}", credentials: { ... } })`);
42278
+ }
42266
42279
  this.dataObject = {};
42267
42280
  this.profile = {};
42268
42281
  __classPrivateFieldSet(this, _User_adminWorldsMap, {}, "f");
@@ -42776,7 +42789,7 @@ class User extends SDKController {
42776
42789
  return __awaiter(this, void 0, void 0, function* () {
42777
42790
  try {
42778
42791
  if (!this.profileId)
42779
- throw "This method requires the use of a profileId";
42792
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42780
42793
  let query = "";
42781
42794
  if (appPublicKey)
42782
42795
  query = `?appPublicKey=${appPublicKey}&appJWT=${appJWT}`;
@@ -42816,7 +42829,7 @@ class User extends SDKController {
42816
42829
  return __awaiter(this, void 0, void 0, function* () {
42817
42830
  try {
42818
42831
  if (!this.profileId)
42819
- throw "This method requires the use of a profileId";
42832
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42820
42833
  yield this.topiaPublicApi().put(`/user/dataObjects/${this.profileId}/set-data-object`, Object.assign(Object.assign({}, options), { dataObject: dataObject || this.dataObject }), this.requestOptions);
42821
42834
  this.dataObject = dataObject || this.dataObject;
42822
42835
  }
@@ -42850,7 +42863,7 @@ class User extends SDKController {
42850
42863
  return __awaiter(this, void 0, void 0, function* () {
42851
42864
  try {
42852
42865
  if (!this.profileId)
42853
- throw "This method requires the use of a profileId";
42866
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42854
42867
  yield this.topiaPublicApi().put(`/user/dataObjects/${this.profileId}/update-data-object`, Object.assign(Object.assign({}, options), { dataObject: dataObject || this.dataObject }), this.requestOptions);
42855
42868
  this.dataObject = Object.assign(Object.assign({}, (this.dataObject || {})), (dataObject || {}));
42856
42869
  }
@@ -42900,7 +42913,7 @@ class User extends SDKController {
42900
42913
  return __awaiter(this, void 0, void 0, function* () {
42901
42914
  try {
42902
42915
  if (!this.profileId)
42903
- throw "This method requires the use of a profileId";
42916
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42904
42917
  const response = yield this.topiaPublicApi().get(`/user/inventory/${this.profileId}/get-user-inventory-items`, this.requestOptions);
42905
42918
  // TODO: Replace 'object' with InventoryItem and instantiate InventoryItem objects if needed
42906
42919
  const tempItems = [];
@@ -42913,7 +42926,7 @@ class User extends SDKController {
42913
42926
  __classPrivateFieldSet(this, _User_userInventoryItems, tempItems, "f");
42914
42927
  }
42915
42928
  catch (error) {
42916
- throw this.errorHandler({ error, sdkMethod: "Visitor.fetchInventoryItems" });
42929
+ throw this.errorHandler({ error, sdkMethod: "User.fetchInventoryItems" });
42917
42930
  }
42918
42931
  });
42919
42932
  }
@@ -42937,7 +42950,7 @@ class User extends SDKController {
42937
42950
  return __awaiter(this, void 0, void 0, function* () {
42938
42951
  try {
42939
42952
  if (!this.profileId)
42940
- throw "This method requires the use of a profileId";
42953
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42941
42954
  const response = yield this.topiaPublicApi().put(`/user/inventory/${this.profileId}/grant-user-inventory-item`, { itemId: item.id, quantity }, this.requestOptions);
42942
42955
  const userInventoryItemFactory = new UserInventoryItemFactory(this.topia);
42943
42956
  const { inventoryItem, user_id, quantity: newQuantity } = response.data;
@@ -42947,7 +42960,7 @@ class User extends SDKController {
42947
42960
  });
42948
42961
  }
42949
42962
  catch (error) {
42950
- throw this.errorHandler({ error, sdkMethod: "Visitor.grantInventoryItem" });
42963
+ throw this.errorHandler({ error, sdkMethod: "User.grantInventoryItem" });
42951
42964
  }
42952
42965
  });
42953
42966
  }
@@ -42968,7 +42981,7 @@ class User extends SDKController {
42968
42981
  return __awaiter(this, void 0, void 0, function* () {
42969
42982
  try {
42970
42983
  if (!this.profileId)
42971
- throw "This method requires the use of a profileId";
42984
+ throw new Error("This method requires the use of a profileId. Pass profileId as a top-level option: User.create({ profileId, credentials: { ... } })");
42972
42985
  const response = yield this.topiaPublicApi().put(`/user/inventory/${this.profileId}/update-user-inventory-item-quantity`, { userItemId: item.id, itemId: item.item_id, quantity }, this.requestOptions);
42973
42986
  const userInventoryItemFactory = new UserInventoryItemFactory(this.topia);
42974
42987
  const { inventoryItem, user_id, quantity: newQuantity } = response.data;
@@ -42978,7 +42991,7 @@ class User extends SDKController {
42978
42991
  });
42979
42992
  }
42980
42993
  catch (error) {
42981
- throw this.errorHandler({ error, sdkMethod: "Visitor.modifyInventoryItemQuantity" });
42994
+ throw this.errorHandler({ error, sdkMethod: "User.modifyInventoryItemQuantity" });
42982
42995
  }
42983
42996
  });
42984
42997
  }
@@ -43663,7 +43676,13 @@ class Visitor extends User {
43663
43676
  createNpc(userInventoryItemId, options) {
43664
43677
  return __awaiter(this, void 0, void 0, function* () {
43665
43678
  try {
43666
- const response = yield this.topiaPublicApi().post(`/world/${this.urlSlug}/visitors/${this.id}/create-npc`, { userInventoryItemId, showNameplate: options === null || options === void 0 ? void 0 : options.showNameplate }, this.requestOptions);
43679
+ const response = yield this.topiaPublicApi().post(`/world/${this.urlSlug}/visitors/${this.id}/create-npc`, {
43680
+ userInventoryItemId,
43681
+ showNameplate: options === null || options === void 0 ? void 0 : options.showNameplate,
43682
+ stationary: options === null || options === void 0 ? void 0 : options.stationary,
43683
+ replace: options === null || options === void 0 ? void 0 : options.replace,
43684
+ spawnEffect: options === null || options === void 0 ? void 0 : options.spawnEffect,
43685
+ }, this.requestOptions);
43667
43686
  return new Visitor(this.topia, response.data.player.playerId, this.urlSlug, {
43668
43687
  attributes: response.data,
43669
43688
  credentials: this.credentials,
@@ -43708,6 +43727,77 @@ class Visitor extends User {
43708
43727
  }
43709
43728
  });
43710
43729
  }
43730
+ /**
43731
+ * Start an AI voice session for this visitor's NPC.
43732
+ *
43733
+ * @remarks
43734
+ * Establishes a real-time voice connection between the visitor and an AI backend
43735
+ * (currently OpenAI Realtime API) through the NPC. The NPC must already be spawned
43736
+ * via `createNpc()` before calling this method.
43737
+ *
43738
+ * The voice session occupies a video slot in the visitor's peer video grid, showing
43739
+ * the NPC's avatar image. Audio streams bidirectionally between the visitor's microphone
43740
+ * and the AI model. Only the NPC's owner hears the AI audio.
43741
+ *
43742
+ * Topia automatically prepends non-removable child safety guardrails to the instructions.
43743
+ * Only one voice session is allowed per visitor per world at a time.
43744
+ *
43745
+ * @keywords voice, npc, ai, chat, audio, realtime, session, start, speech
43746
+ *
43747
+ * @category NPCs
43748
+ *
43749
+ * @param config - Voice session configuration including ephemeral key and AI instructions
43750
+ *
43751
+ * @example
43752
+ * ```ts
43753
+ * const ephemeralKey = await generateOpenAIEphemeralKey();
43754
+ *
43755
+ * await visitor.startNpcVoiceSession({
43756
+ * ephemeralKey,
43757
+ * voice: "alloy",
43758
+ * instructions: "You are a friendly science tutor helping with photosynthesis.",
43759
+ * model: "gpt-4o-realtime-preview",
43760
+ * });
43761
+ * ```
43762
+ *
43763
+ * @returns {Promise<void | ResponseType>} Returns `{ success: true }` or an error.
43764
+ */
43765
+ startNpcVoiceSession(config) {
43766
+ return __awaiter(this, void 0, void 0, function* () {
43767
+ try {
43768
+ const response = yield this.topiaPublicApi().put(`/world/${this.urlSlug}/visitors/${this.id}/start-npc-voice-session`, { voiceConfig: config }, this.requestOptions);
43769
+ return response.data;
43770
+ }
43771
+ catch (error) {
43772
+ throw this.errorHandler({ error, params: config, sdkMethod: "Visitor.startNpcVoiceSession" });
43773
+ }
43774
+ });
43775
+ }
43776
+ /**
43777
+ * Stop the active AI voice session for this visitor's NPC.
43778
+ *
43779
+ * @keywords voice, npc, ai, chat, audio, realtime, session, stop, end
43780
+ *
43781
+ * @category NPCs
43782
+ *
43783
+ * @example
43784
+ * ```ts
43785
+ * await visitor.stopNpcVoiceSession();
43786
+ * ```
43787
+ *
43788
+ * @returns {Promise<void | ResponseType>} Returns `{ success: true }` or an error.
43789
+ */
43790
+ stopNpcVoiceSession() {
43791
+ return __awaiter(this, void 0, void 0, function* () {
43792
+ try {
43793
+ const response = yield this.topiaPublicApi().put(`/world/${this.urlSlug}/visitors/${this.id}/stop-npc-voice-session`, {}, this.requestOptions);
43794
+ return response.data;
43795
+ }
43796
+ catch (error) {
43797
+ throw this.errorHandler({ error, sdkMethod: "Visitor.stopNpcVoiceSession" });
43798
+ }
43799
+ });
43800
+ }
43711
43801
  /**
43712
43802
  * Retrieves all inventory items owned by this visitor and app's key.
43713
43803
  *
package/package.json CHANGED
@@ -61,5 +61,5 @@
61
61
  "yalc-push": "yarn build && yalc publish --push --dev --no-scripts"
62
62
  },
63
63
  "type": "module",
64
- "version": "0.19.05"
64
+ "version": "0.19.07"
65
65
  }