@voicethere/agent 0.7.4 → 0.7.5

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": "@voicethere/agent",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "VoiceThere customer agent SDK — IPC types and runtime helpers for sandboxed child bundles",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,7 +13,8 @@
13
13
  * - `{ type: "mix", action: "set_tts_pose", clientId, pose }`
14
14
  * - `{ type: "mix", action: "speak", clientId, text }`
15
15
  * - `{ type: "mix", action: "clear_tts_pose", clientId }`
16
- * - `{ type: "mix", action: "play", sessionIds?, bytes?, url?, volume? }`
16
+ * - `{ type: "mix", action: "play", sessionIds?, bytes?, url?, volume? }` — trigger only;
17
+ * clip WAV is generated in-template unless bytes/url override.
17
18
  *
18
19
  * Acks: `{ type: "mix_ack", action, ok: true, statuses?, ... }` or `{ ok: false, error }`.
19
20
  * Ignores `ping` / chat strings (voice-control readiness).
@@ -37,6 +38,43 @@ import {
37
38
  /** E2E fixture marker — rewritten before each fixture upload when needed. */
38
39
  export const FIXTURE_MARKER = "mix-smoke-fixture-a";
39
40
 
41
+ /** Inline clip: 16 kHz mono s16le — decoded size stays under 64 KiB play cap. */
42
+ export const CLIP_WAV_DURATION_MS = 2000;
43
+ export const CLIP_WAV_AMPLITUDE = 14_000;
44
+ export const INLINE_CLIP_DUMMY_URL = "https://example.com/inline-clip.wav";
45
+
46
+ /** Loud mono WAV (s16le 16 kHz) for default inline clip play. */
47
+ export function buildMixSmokeInlineClipBase64(
48
+ durationMs = CLIP_WAV_DURATION_MS,
49
+ amplitude = CLIP_WAV_AMPLITUDE,
50
+ ): string {
51
+ const sampleRate = 16_000;
52
+ const channels = 1;
53
+ const bytesPerSample = 2;
54
+ const numSamples = Math.floor((sampleRate * durationMs) / 1000);
55
+ const dataSize = numSamples * channels * bytesPerSample;
56
+ const buffer = Buffer.alloc(44 + dataSize);
57
+ buffer.write("RIFF", 0);
58
+ buffer.writeUInt32LE(36 + dataSize, 4);
59
+ buffer.write("WAVE", 8);
60
+ buffer.write("fmt ", 12);
61
+ buffer.writeUInt32LE(16, 16);
62
+ buffer.writeUInt16LE(1, 20);
63
+ buffer.writeUInt16LE(channels, 22);
64
+ buffer.writeUInt32LE(sampleRate, 24);
65
+ buffer.writeUInt32LE(sampleRate * channels * bytesPerSample, 28);
66
+ buffer.writeUInt16LE(channels * bytesPerSample, 32);
67
+ buffer.writeUInt16LE(16, 34);
68
+ buffer.write("data", 36);
69
+ buffer.writeUInt32LE(dataSize, 40);
70
+ for (let i = 0; i < numSamples; i++) {
71
+ buffer.writeInt16LE(amplitude, 44 + i * 2);
72
+ }
73
+ return buffer.toString("base64");
74
+ }
75
+
76
+ const INLINE_CLIP = buildMixSmokeInlineClipBase64();
77
+
40
78
  export type MixPose = {
41
79
  position: { x: number; y: number; z: number };
42
80
  orientation: { x: number; y: number; z: number; w: number };
@@ -212,13 +250,6 @@ export function isMixCommand(message: unknown): message is MixCommand {
212
250
  url?: unknown;
213
251
  volume?: unknown;
214
252
  };
215
- const hasBytes =
216
- typeof playMsg.bytes === "string" && playMsg.bytes.trim().length > 0;
217
- const hasUrl =
218
- typeof playMsg.url === "string" && playMsg.url.trim().length > 0;
219
- if (!hasBytes && !hasUrl) {
220
- return false;
221
- }
222
253
  if (
223
254
  playMsg.bytes !== undefined &&
224
255
  (typeof playMsg.bytes !== "string" || playMsg.bytes.trim().length === 0)
@@ -440,14 +471,16 @@ async function handleMixCommand(
440
471
  return;
441
472
  }
442
473
  case "play": {
443
- const inlineUrl =
444
- command.url?.trim() || "https://example.com/inline-clip.wav";
474
+ const trimmedBytes = command.bytes?.trim();
475
+ const trimmedUrl = command.url?.trim();
476
+ const url = trimmedUrl || INLINE_CLIP_DUMMY_URL;
477
+ const bytes = trimmedBytes || (!trimmedUrl ? INLINE_CLIP : undefined);
445
478
  const result = await play({
446
- url: inlineUrl,
479
+ url,
447
480
  ...(command.sessionIds?.length
448
481
  ? { sessionIds: command.sessionIds }
449
482
  : {}),
450
- ...(command.bytes ? { bytes: command.bytes } : {}),
483
+ ...(bytes ? { bytes } : {}),
451
484
  ...(command.volume !== undefined ? { volume: command.volume } : {}),
452
485
  });
453
486
  if (!result.ok || !result.playId) {