@xmov/avatar 2.0.0-alpha.53 → 2.0.0-alpha.54

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmov/avatar",
3
- "version": "2.0.0-alpha.53",
3
+ "version": "2.0.0-alpha.54",
4
4
  "description": "魔珐数字人 JS-SDK",
5
5
  "type": "module",
6
6
  "source": "src/index.ts",
@@ -10,12 +10,19 @@
10
10
  "default": "./dist/index.modern.js"
11
11
  },
12
12
  "./agent": {
13
- "types": "./src/agent/index.ts",
13
+ "types": "./dist/agent/index.d.ts",
14
14
  "require": "./dist/agent/index.cjs",
15
15
  "default": "./dist/agent/avatar.modern.js"
16
16
  },
17
17
  "./agent/umd": "./dist/agent/index.umd.js"
18
18
  },
19
+ "typesVersions": {
20
+ "*": {
21
+ "agent": [
22
+ "dist/agent/index.d.ts"
23
+ ]
24
+ }
25
+ },
19
26
  "amdName": "XmovAvatar",
20
27
  "main": "./dist/index.cjs",
21
28
  "types": "./dist/index.d.ts",
@@ -2153,10 +2153,17 @@ test("XingyunAvatarAgent rejects ambiguous non-empty config sources", () => {
2153
2153
  );
2154
2154
  });
2155
2155
 
2156
- test("XingyunAvatarAgent extends XmovAvatar and preserves base speak", async () => {
2156
+ test("XingyunAvatarAgent extends XmovAvatar while keeping the base class behavior unchanged", async () => {
2157
2157
  MockWebSocket.instances = [];
2158
2158
  const { agent } = createAgent();
2159
2159
  const XmovAvatar = require("../../index");
2160
+ const avatar = new XmovAvatar({});
2161
+
2162
+ avatar.speak("legacy TTSA speak");
2163
+ const interruptedSpeechId = avatar.interrupt("speak");
2164
+ assert.deepEqual(avatar.speeches, [["legacy TTSA speak"]]);
2165
+ assert.deepEqual(avatar.interrupts, ["speak"]);
2166
+ assert.equal(interruptedSpeechId, 1);
2160
2167
 
2161
2168
  await agent.init();
2162
2169
  const ws = MockWebSocket.instances[0];
@@ -2167,11 +2174,6 @@ test("XingyunAvatarAgent extends XmovAvatar and preserves base speak", async ()
2167
2174
 
2168
2175
  agent.start();
2169
2176
  assert.equal(agent.startCalls, 1);
2170
- agent.speak("legacy TTSA speak");
2171
- assert.deepEqual(agent.speeches, [["legacy TTSA speak"]]);
2172
- assert.deepEqual(ws.sent, []);
2173
- agent.avatarOptions.onVoiceStateChange("end");
2174
- assert.deepEqual(ws.sent, []);
2175
2177
  });
2176
2178
 
2177
2179
  test("XingyunAvatarAgent restarts TTSA after same-session ready without leaving stopped state", async () => {
@@ -2200,7 +2202,7 @@ test("XingyunAvatarAgent ignores an explicit start while init is pending", () =>
2200
2202
  assert.equal(agent.started, false);
2201
2203
  });
2202
2204
 
2203
- test("XingyunAvatarAgent sends frozen ask and speak payloads without client IDs or providers", async () => {
2205
+ test("XingyunAvatarAgent sends frozen ask and canonical speak payloads without client IDs or providers", async () => {
2204
2206
  MockWebSocket.instances = [];
2205
2207
  const conversations = [];
2206
2208
  const { agent } = createAgent({
@@ -2215,15 +2217,34 @@ test("XingyunAvatarAgent sends frozen ask and speak payloads without client IDs
2215
2217
  assert.deepEqual(askPayload, { type: "ask", message: { text: "hello" } });
2216
2218
  assert.deepEqual(conversations.at(-1), { state: "asking", text: "hello" });
2217
2219
 
2218
- await agent.speakByE2E("welcome");
2220
+ const speakResult = agent.speak("welcome");
2221
+ assert.equal(speakResult, undefined);
2219
2222
  const speakPayload = JSON.parse(ws.sent.at(-1));
2220
2223
  assert.deepEqual(speakPayload, {
2221
2224
  type: "speak",
2222
2225
  message: { text: "welcome", is_start: true, is_end: true },
2223
2226
  });
2227
+ assert.deepEqual(conversations.at(-1), { state: "speaking-directly", text: "welcome" });
2228
+
2229
+ const e2eSentCount = ws.sent.length;
2230
+ const replayExtra = { client_speak_id: "replay", client_frame: 7 };
2231
+ const replayResult = agent.speak("<speak>replay</speak>", false, true, replayExtra);
2232
+ assert.equal(replayResult, undefined);
2233
+ assert.deepEqual(agent.speeches.at(-1), ["<speak>replay</speak>", false, true, replayExtra]);
2234
+ assert.equal(ws.sent.length, e2eSentCount);
2235
+ agent.avatarOptions.onVoiceStateChange("end", 1200);
2236
+ assert.equal(ws.sent.length, e2eSentCount);
2237
+
2238
+ const sentCount = ws.sent.length;
2239
+ await agent.speakByE2E("deprecated welcome");
2240
+ assert.equal(ws.sent.length, sentCount + 1);
2241
+ assert.deepEqual(JSON.parse(ws.sent.at(-1)), {
2242
+ type: "speak",
2243
+ message: { text: "deprecated welcome", is_start: true, is_end: true },
2244
+ });
2224
2245
  });
2225
2246
 
2226
- test("XingyunAvatarAgent interrupts E2E and local playback without client IDs", async () => {
2247
+ test("XingyunAvatarAgent interrupts E2E and local playback through canonical and deprecated methods", async () => {
2227
2248
  MockWebSocket.instances = [];
2228
2249
  const { agent } = createAgent();
2229
2250
 
@@ -2231,9 +2252,16 @@ test("XingyunAvatarAgent interrupts E2E and local playback without client IDs",
2231
2252
  const ws = MockWebSocket.instances[0];
2232
2253
  await agent.ask("cancel me");
2233
2254
 
2234
- await agent.interruptConversation("user");
2255
+ const interruptResult = agent.interrupt();
2256
+ assert.equal(interruptResult, 1);
2235
2257
  assert.deepEqual(agent.interrupts, ["speak"]);
2236
2258
  assert.deepEqual(JSON.parse(ws.sent.at(-1)), { type: "interrupt" });
2259
+
2260
+ const sentCount = ws.sent.length;
2261
+ await agent.interruptConversation("user");
2262
+ assert.equal(ws.sent.length, sentCount + 1);
2263
+ assert.deepEqual(agent.interrupts, ["speak", "speak"]);
2264
+ assert.deepEqual(JSON.parse(ws.sent.at(-1)), { type: "interrupt" });
2237
2265
  });
2238
2266
 
2239
2267
  test("XingyunAvatarAgent keeps ASR listening when interrupting a conversation", async () => {
@@ -2248,7 +2276,7 @@ test("XingyunAvatarAgent keeps ASR listening when interrupting a conversation",
2248
2276
  const recorder = browser.recorders[0];
2249
2277
  ws.sent = [];
2250
2278
 
2251
- await agent.interruptConversation("user");
2279
+ agent.interrupt();
2252
2280
  const audioAfterInterrupt = new Blob(["audio-after-interrupt"], {
2253
2281
  type: "audio/webm;codecs=opus",
2254
2282
  });
@@ -262,7 +262,9 @@ export default class XingyunAvatarAgent extends XmovAvatar {
262
262
  return this.agentState;
263
263
  }
264
264
 
265
- // Kept for callers that adopted the first Agent preview.
265
+ /**
266
+ * @deprecated Use getAgentState() instead.
267
+ */
266
268
  getState() {
267
269
  return this.getAgentState();
268
270
  }
@@ -460,13 +462,6 @@ export default class XingyunAvatarAgent extends XmovAvatar {
460
462
  }
461
463
  }
462
464
 
463
- override speak(...args: Parameters<XmovAvatar["speak"]>) {
464
- if (args[2] ?? true) {
465
- this.suppressNextVoiceEnd = true;
466
- }
467
- return super.speak(...args);
468
- }
469
-
470
465
  override async destroy(reason = "user") {
471
466
  if (this.agentDestroyed) {
472
467
  if (this.agentState !== "destroyed") {
@@ -510,7 +505,16 @@ export default class XingyunAvatarAgent extends XmovAvatar {
510
505
  });
511
506
  }
512
507
 
513
- async speakByE2E(text: string): Promise<void> {
508
+ override speak(text: string): string | undefined {
509
+ // XmovAvatar Replay dispatches recorded TTSA events through this.speak(...).
510
+ // Preserve that inherited multi-argument call path without exposing it as
511
+ // the Agent's public E2E API.
512
+ if (arguments.length > 1) {
513
+ if (arguments[2] ?? true) {
514
+ this.suppressNextVoiceEnd = true;
515
+ }
516
+ return super.speak(text, arguments[1], arguments[2], arguments[3]);
517
+ }
514
518
  this.assertRunning();
515
519
  this.suppressNextVoiceEnd = false;
516
520
  this.emitConversation({ state: "speaking-directly", text });
@@ -522,6 +526,14 @@ export default class XingyunAvatarAgent extends XmovAvatar {
522
526
  is_end: true,
523
527
  },
524
528
  });
529
+ return undefined;
530
+ }
531
+
532
+ /**
533
+ * @deprecated Use speak(text) instead.
534
+ */
535
+ async speakByE2E(text: string): Promise<void> {
536
+ this.speak(text);
525
537
  }
526
538
 
527
539
  async startASR(options: AgentASRStartOptions = {}): Promise<void> {
@@ -670,18 +682,25 @@ export default class XingyunAvatarAgent extends XmovAvatar {
670
682
  }
671
683
  }
672
684
 
673
- async interruptConversation(reason = "user") {
685
+ override interrupt(): number {
674
686
  if (this.agentDestroyed) {
675
- return;
687
+ return 0;
676
688
  }
677
689
 
678
- void reason;
679
-
680
690
  this.sendControl({
681
691
  type: "interrupt",
682
692
  });
683
- super.interrupt("speak");
693
+ const interruptedSpeechId = super.interrupt("speak");
684
694
  this.emitConversation({ state: "interrupted" });
695
+ return interruptedSpeechId;
696
+ }
697
+
698
+ /**
699
+ * @deprecated Use interrupt() instead. The reason parameter is ignored.
700
+ */
701
+ async interruptConversation(reason = "user"): Promise<void> {
702
+ void reason;
703
+ this.interrupt();
685
704
  }
686
705
 
687
706
  protected override async onSessionReloaded(sessionInfo: ISessionResponse): Promise<void> {
@@ -717,10 +736,6 @@ export default class XingyunAvatarAgent extends XmovAvatar {
717
736
  }
718
737
  }
719
738
 
720
- override reloadSuccess() {
721
- super.reloadSuccess();
722
- }
723
-
724
739
  private async replaceE2EConnection(sessionInfo?: AgentSessionInfo) {
725
740
  const { wsUrl, token } = this.resolveE2EConnection(sessionInfo);
726
741
  const previousClient = this.e2eClient;