@voicethere/agent 0.7.12 → 0.8.0

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 (62) hide show
  1. package/README.md +14 -3
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/runtime.d.ts.map +1 -1
  7. package/dist/runtime.js +11 -2
  8. package/dist/runtime.js.map +1 -1
  9. package/dist/templates/echo/agent.js +1 -1
  10. package/dist/templates/echo-dc/agent.js +1 -1
  11. package/dist/templates/game-sync/agent.js +162 -125
  12. package/dist/templates/registry.d.ts +1 -1
  13. package/dist/templates/registry.d.ts.map +1 -1
  14. package/dist/templates/registry.js +42 -26
  15. package/dist/templates/registry.js.map +1 -1
  16. package/dist/templates/spatial-showcase/agent.js +261 -22
  17. package/dist/templates/voice-starter/agent.js +1 -1
  18. package/dist/templates/webhooks/agent.js +2 -2
  19. package/dist/templates/webhooks-redis/agent.js +11 -4
  20. package/dist/templates/world-sync/agent.js +829 -0
  21. package/dist/templates/world-sync-binary/agent.js +1031 -0
  22. package/package.json +3 -2
  23. package/templates/README.md +41 -69
  24. package/templates/crash/README.md +13 -0
  25. package/templates/echo/README.md +13 -0
  26. package/templates/{echo.ts → echo/agent.ts} +1 -1
  27. package/templates/echo-dc/README.md +13 -0
  28. package/templates/{echo-dc.ts → echo-dc/agent.ts} +1 -1
  29. package/templates/echo-smoke/README.md +13 -0
  30. package/templates/game-sync/README.md +21 -0
  31. package/templates/{game-sync.ts → game-sync/agent.ts} +126 -94
  32. package/templates/{game-sync-redis.ts → game-sync/redis.ts} +4 -13
  33. package/templates/{game-sync-sim.ts → game-sync/sim.ts} +14 -7
  34. package/templates/{game-sync-world-layout.ts → game-sync/world-layout.ts} +72 -30
  35. package/templates/game-sync-smoke/README.md +13 -0
  36. package/templates/mix-smoke/README.md +13 -0
  37. package/templates/positional-tts/README.md +13 -0
  38. package/templates/recording-consent/README.md +13 -0
  39. package/templates/redis-sync/README.md +23 -0
  40. package/templates/redis-sync/agent.ts +72 -95
  41. package/templates/redis-sync/world-layout.ts +103 -26
  42. package/templates/spatial-showcase/README.md +13 -0
  43. package/templates/spatial-showcase/agent.ts +245 -18
  44. package/templates/spatial-showcase/protocol.ts +79 -0
  45. package/templates/spatial-showcase/sine.ts +28 -5
  46. package/templates/voice-showcase/README.md +13 -0
  47. package/templates/voice-starter/README.md +13 -0
  48. package/templates/webhooks/README.md +13 -0
  49. package/templates/{webhooks.ts → webhooks/agent.ts} +1 -1
  50. package/templates/webhooks-redis/README.md +13 -0
  51. package/templates/{webhooks-redis.ts → webhooks-redis/agent.ts} +9 -9
  52. package/templates/world-sync/README.md +20 -0
  53. package/templates/world-sync/agent.ts +62 -0
  54. package/templates/world-sync-binary/README.md +21 -0
  55. package/templates/world-sync-binary/agent.ts +107 -0
  56. package/templates/world-sync-binary/protocol.ts +281 -0
  57. /package/templates/{crash.ts → crash/agent.ts} +0 -0
  58. /package/templates/{echo-smoke.ts → echo-smoke/agent.ts} +0 -0
  59. /package/templates/{game-sync-protocol.ts → game-sync/protocol.ts} +0 -0
  60. /package/templates/{game-sync-smoke.ts → game-sync-smoke/agent.ts} +0 -0
  61. /package/templates/{mix-smoke.ts → mix-smoke/agent.ts} +0 -0
  62. /package/templates/{agent.ts → voice-starter/agent.ts} +0 -0
@@ -1137,6 +1137,8 @@ function resolveClipUrl(assetOrigin, clipId) {
1137
1137
  var SHOWCASE_DEMOS = /* @__PURE__ */ new Set(["orbit", "soundboard", "proximity"]);
1138
1138
  var MAX_SAY_TEXT_LENGTH = 200;
1139
1139
  var MAX_XZ = 5;
1140
+ var ORBIT_SINE_MIN_FREQUENCY_HZ = 110;
1141
+ var ORBIT_SINE_MAX_FREQUENCY_HZ = 1760;
1140
1142
  function isFiniteNumber(value) {
1141
1143
  return typeof value === "number" && Number.isFinite(value);
1142
1144
  }
@@ -1146,6 +1148,9 @@ function clampVolume(value) {
1146
1148
  function isInXzRange(value) {
1147
1149
  return value >= -MAX_XZ && value <= MAX_XZ;
1148
1150
  }
1151
+ function isOrbitSineFrequencyHz(value) {
1152
+ return value >= ORBIT_SINE_MIN_FREQUENCY_HZ && value <= ORBIT_SINE_MAX_FREQUENCY_HZ;
1153
+ }
1149
1154
  function isMixPlacement(value) {
1150
1155
  return value === "center" || value === "left" || value === "right" || value === "front" || value === "behind" || value === "below" || value === "above";
1151
1156
  }
@@ -1194,15 +1199,33 @@ function parseShowcaseMessage(message) {
1194
1199
  if (set.paused !== void 0 && typeof set.paused !== "boolean") {
1195
1200
  return null;
1196
1201
  }
1202
+ if (set.frequencyHz !== void 0 && (!isFiniteNumber(set.frequencyHz) || !isOrbitSineFrequencyHz(set.frequencyHz))) {
1203
+ return null;
1204
+ }
1205
+ if (set.sinePlaying !== void 0 && typeof set.sinePlaying !== "boolean") {
1206
+ return null;
1207
+ }
1197
1208
  return {
1198
1209
  type: "orbit",
1199
1210
  action: "set",
1200
1211
  ...set.radius !== void 0 ? { radius: set.radius } : {},
1201
1212
  ...set.periodSec !== void 0 ? { periodSec: set.periodSec } : {},
1202
1213
  ...set.elevation !== void 0 ? { elevation: set.elevation } : {},
1203
- ...set.paused !== void 0 ? { paused: set.paused } : {}
1214
+ ...set.paused !== void 0 ? { paused: set.paused } : {},
1215
+ ...set.frequencyHz !== void 0 ? { frequencyHz: set.frequencyHz } : {},
1216
+ ...set.sinePlaying !== void 0 ? { sinePlaying: set.sinePlaying } : {}
1204
1217
  };
1205
1218
  }
1219
+ if (orbit.action === "place") {
1220
+ const place = message;
1221
+ if (!isFiniteNumber(place.x) || !isInXzRange(place.x)) {
1222
+ return null;
1223
+ }
1224
+ if (!isFiniteNumber(place.z) || !isInXzRange(place.z)) {
1225
+ return null;
1226
+ }
1227
+ return { type: "orbit", action: "place", x: place.x, z: place.z };
1228
+ }
1206
1229
  if (orbit.action === "say") {
1207
1230
  const say = message;
1208
1231
  if (typeof say.text !== "string" || say.text.length === 0 || say.text.length > MAX_SAY_TEXT_LENGTH) {
@@ -1254,6 +1277,24 @@ function parseShowcaseMessage(message) {
1254
1277
  }
1255
1278
  return { type: "pad_status", playId: status.playId.trim() };
1256
1279
  }
1280
+ case "pad_move": {
1281
+ const padMove = message;
1282
+ if (!isNonEmptyString(padMove.playId)) {
1283
+ return null;
1284
+ }
1285
+ if (!isFiniteNumber(padMove.x) || !isInXzRange(padMove.x)) {
1286
+ return null;
1287
+ }
1288
+ if (!isFiniteNumber(padMove.z) || !isInXzRange(padMove.z)) {
1289
+ return null;
1290
+ }
1291
+ return {
1292
+ type: "pad_move",
1293
+ playId: padMove.playId.trim(),
1294
+ x: padMove.x,
1295
+ z: padMove.z
1296
+ };
1297
+ }
1257
1298
  case "move": {
1258
1299
  const move = message;
1259
1300
  if (!isFiniteNumber(move.x) || !isInXzRange(move.x)) {
@@ -1368,12 +1409,22 @@ var ProximityRoom = class {
1368
1409
  var ORBIT_SINE_DURATION_MS = 2e3;
1369
1410
  var ORBIT_SINE_FREQUENCY_HZ = 440;
1370
1411
  var ORBIT_SINE_AMPLITUDE = 8e3;
1412
+ var ORBIT_SINE_SAMPLE_RATE = 16e3;
1371
1413
  var ORBIT_SINE_INLINE_URL = "https://example.com/inline-orbit-sine.wav";
1372
- function buildOrbitSineInlineClipBase64(durationMs = ORBIT_SINE_DURATION_MS, frequencyHz = ORBIT_SINE_FREQUENCY_HZ, amplitude = ORBIT_SINE_AMPLITUDE) {
1373
- const sampleRate = 16e3;
1414
+ function orbitSineSampleCount(durationMs = ORBIT_SINE_DURATION_MS, frequencyHz = ORBIT_SINE_FREQUENCY_HZ, sampleRate = ORBIT_SINE_SAMPLE_RATE) {
1415
+ const targetCycles = durationMs / 1e3 * frequencyHz;
1416
+ const cycles = Math.max(1, Math.floor(targetCycles));
1417
+ return Math.round(cycles * sampleRate / frequencyHz);
1418
+ }
1419
+ function orbitSineClipDurationMs(durationMs = ORBIT_SINE_DURATION_MS, frequencyHz = ORBIT_SINE_FREQUENCY_HZ, sampleRate = ORBIT_SINE_SAMPLE_RATE) {
1420
+ const numSamples = orbitSineSampleCount(durationMs, frequencyHz, sampleRate);
1421
+ return numSamples / sampleRate * 1e3;
1422
+ }
1423
+ function buildOrbitSineInlineClipBase64(durationMs = ORBIT_SINE_DURATION_MS, frequencyHz = ORBIT_SINE_FREQUENCY_HZ, amplitude = ORBIT_SINE_AMPLITUDE, phaseRad = 0) {
1424
+ const sampleRate = ORBIT_SINE_SAMPLE_RATE;
1374
1425
  const channels = 1;
1375
1426
  const bytesPerSample = 2;
1376
- const numSamples = Math.floor(sampleRate * durationMs / 1e3);
1427
+ const numSamples = orbitSineSampleCount(durationMs, frequencyHz, sampleRate);
1377
1428
  const dataSize = numSamples * channels * bytesPerSample;
1378
1429
  const buffer = Buffer.alloc(44 + dataSize);
1379
1430
  buffer.write("RIFF", 0);
@@ -1392,7 +1443,7 @@ function buildOrbitSineInlineClipBase64(durationMs = ORBIT_SINE_DURATION_MS, fre
1392
1443
  for (let i = 0; i < numSamples; i++) {
1393
1444
  const t = i / sampleRate;
1394
1445
  const sample = Math.round(
1395
- amplitude * Math.sin(2 * Math.PI * frequencyHz * t)
1446
+ amplitude * Math.sin(2 * Math.PI * frequencyHz * t + phaseRad)
1396
1447
  );
1397
1448
  buffer.writeInt16LE(sample, 44 + i * 2);
1398
1449
  }
@@ -1405,6 +1456,7 @@ var ORBIT_POSE_EMIT_MS = 100;
1405
1456
  var ROOM_STATE_TICK_MS = 100;
1406
1457
  var LOOP_PAD_POLL_MS = 250;
1407
1458
  var ORBIT_SINE_POLL_MS = 250;
1459
+ var ORBIT_SINE_GAPLESS_LEAD_MS = 200;
1408
1460
  var ORBIT_SINE_VOLUME = 0.25;
1409
1461
  var MAX_ACTIVE_PLAYS = 12;
1410
1462
  var SHOWCASE_ROOM_GROUP_ID = "showcase-room";
@@ -1467,6 +1519,9 @@ function defaultSessionState(demo, assetOrigin) {
1467
1519
  playIds: /* @__PURE__ */ new Set(),
1468
1520
  loopPads: /* @__PURE__ */ new Map(),
1469
1521
  loopPollTimers: /* @__PURE__ */ new Map(),
1522
+ orbitSineFrequencyHz: 440,
1523
+ orbitSineEnabled: true,
1524
+ orbitSinePhaseRad: 0,
1470
1525
  inProximityRoom: false
1471
1526
  };
1472
1527
  }
@@ -1508,27 +1563,114 @@ function clearAllPlays(sessionId, state) {
1508
1563
  state.loopPollTimers.clear();
1509
1564
  }
1510
1565
  function currentOrbitPose(state) {
1566
+ if (state.orbitManualPose) {
1567
+ const elevation = state.orbit.elevation ?? 0;
1568
+ return {
1569
+ position: {
1570
+ x: state.orbitManualPose.x,
1571
+ y: elevation,
1572
+ z: state.orbitManualPose.z
1573
+ },
1574
+ orientation: { x: 0, y: 0, z: 0, w: 1 }
1575
+ };
1576
+ }
1511
1577
  if (!state.orbitStartMs) {
1512
1578
  return orbitTtsPose(0, state.orbit);
1513
1579
  }
1514
1580
  const elapsedSec = (Date.now() - state.orbitStartMs) / 1e3;
1515
1581
  return orbitTtsPose(elapsedSec, state.orbit);
1516
1582
  }
1583
+ function orbitPoseFromXz(state, x, z) {
1584
+ const elevation = state.orbit.elevation ?? 0;
1585
+ return {
1586
+ position: { x, y: elevation, z },
1587
+ orientation: { x: 0, y: 0, z: 0, w: 1 }
1588
+ };
1589
+ }
1590
+ function syncOrbitClockFromManualPose(state) {
1591
+ const manual = state.orbitManualPose;
1592
+ if (!manual) {
1593
+ return;
1594
+ }
1595
+ const periodSec = state.orbit.periodSec ?? 2 * Math.PI;
1596
+ const angleRad = Math.atan2(manual.z, manual.x);
1597
+ const elapsedSec = angleRad * periodSec / (2 * Math.PI);
1598
+ state.orbitStartMs = Date.now() - elapsedSec * 1e3;
1599
+ state.orbitManualPose = void 0;
1600
+ }
1517
1601
  function clearOrbitSinePoll(state) {
1518
1602
  if (state.orbitSinePollTimer) {
1519
1603
  clearInterval(state.orbitSinePollTimer);
1520
1604
  state.orbitSinePollTimer = void 0;
1521
1605
  }
1522
1606
  }
1607
+ function clearOrbitSineRestartTimer(state) {
1608
+ if (state.orbitSineRestartTimer) {
1609
+ clearTimeout(state.orbitSineRestartTimer);
1610
+ state.orbitSineRestartTimer = void 0;
1611
+ }
1612
+ }
1523
1613
  async function stopOrbitSine(state) {
1524
1614
  clearOrbitSinePoll(state);
1615
+ clearOrbitSineRestartTimer(state);
1525
1616
  const playId = state.orbitSinePlayId;
1526
1617
  state.orbitSinePlayId = void 0;
1527
1618
  if (playId) {
1528
1619
  await stopPlay(playId);
1529
1620
  }
1530
1621
  }
1622
+ function orbitSineClipBytes(state) {
1623
+ return buildOrbitSineInlineClipBase64(
1624
+ ORBIT_SINE_DURATION_MS,
1625
+ state.orbitSineFrequencyHz,
1626
+ void 0,
1627
+ state.orbitSinePhaseRad
1628
+ );
1629
+ }
1630
+ function orbitSineClipDuration(state) {
1631
+ return orbitSineClipDurationMs(
1632
+ ORBIT_SINE_DURATION_MS,
1633
+ state.orbitSineFrequencyHz
1634
+ );
1635
+ }
1636
+ function scheduleOrbitSineGaplessRestart(sessionId, state) {
1637
+ clearOrbitSineRestartTimer(state);
1638
+ const durationMs = orbitSineClipDuration(state);
1639
+ const delayMs = Math.max(0, durationMs - ORBIT_SINE_GAPLESS_LEAD_MS);
1640
+ state.orbitSineRestartTimer = setTimeout(() => {
1641
+ state.orbitSineRestartTimer = void 0;
1642
+ void restartOrbitSineGapless(sessionId, state);
1643
+ }, delayMs);
1644
+ }
1645
+ async function restartOrbitSineGapless(sessionId, state) {
1646
+ if (!state.orbitSineEnabled || !state.orbitSinePlayId) {
1647
+ return;
1648
+ }
1649
+ const previousPlayId = state.orbitSinePlayId;
1650
+ const result = await play({
1651
+ url: ORBIT_SINE_INLINE_URL,
1652
+ bytes: orbitSineClipBytes(state),
1653
+ sessionIds: [sessionId],
1654
+ volume: ORBIT_SINE_VOLUME,
1655
+ pose: currentOrbitPose(state)
1656
+ });
1657
+ if (!result.ok || !result.playId) {
1658
+ agentLog(
1659
+ "warn",
1660
+ `spatial-showcase orbit sine gapless restart failed: ${result.reason ?? "play_failed"}`,
1661
+ sessionId
1662
+ );
1663
+ startOrbitSineLoopPoll(sessionId, state);
1664
+ return;
1665
+ }
1666
+ state.orbitSinePlayId = result.playId;
1667
+ void stopPlay(previousPlayId);
1668
+ scheduleOrbitSineGaplessRestart(sessionId, state);
1669
+ }
1531
1670
  function startOrbitSineLoopPoll(sessionId, state) {
1671
+ if (state.orbitSineRestartTimer) {
1672
+ return;
1673
+ }
1532
1674
  clearOrbitSinePoll(state);
1533
1675
  const playId = state.orbitSinePlayId;
1534
1676
  if (!playId) {
@@ -1536,7 +1678,7 @@ function startOrbitSineLoopPoll(sessionId, state) {
1536
1678
  }
1537
1679
  state.orbitSinePollTimer = setInterval(() => {
1538
1680
  void (async () => {
1539
- if (!state.orbitSinePlayId) {
1681
+ if (!state.orbitSinePlayId || state.orbitSineRestartTimer) {
1540
1682
  clearOrbitSinePoll(state);
1541
1683
  return;
1542
1684
  }
@@ -1545,25 +1687,19 @@ function startOrbitSineLoopPoll(sessionId, state) {
1545
1687
  return;
1546
1688
  }
1547
1689
  if (status.status === "ended") {
1548
- const replay = await play({
1549
- url: ORBIT_SINE_INLINE_URL,
1550
- bytes: buildOrbitSineInlineClipBase64(),
1551
- sessionIds: [sessionId],
1552
- volume: ORBIT_SINE_VOLUME,
1553
- pose: currentOrbitPose(state)
1554
- });
1555
- if (replay.ok && replay.playId) {
1556
- state.orbitSinePlayId = replay.playId;
1557
- }
1690
+ await restartOrbitSineGapless(sessionId, state);
1558
1691
  }
1559
1692
  })();
1560
1693
  }, ORBIT_SINE_POLL_MS);
1561
1694
  }
1562
1695
  async function startOrbitSinePlay(sessionId, state) {
1696
+ if (!state.orbitSineEnabled) {
1697
+ return;
1698
+ }
1563
1699
  await stopOrbitSine(state);
1564
1700
  const result = await play({
1565
1701
  url: ORBIT_SINE_INLINE_URL,
1566
- bytes: buildOrbitSineInlineClipBase64(),
1702
+ bytes: orbitSineClipBytes(state),
1567
1703
  sessionIds: [sessionId],
1568
1704
  volume: ORBIT_SINE_VOLUME,
1569
1705
  pose: currentOrbitPose(state)
@@ -1577,10 +1713,11 @@ async function startOrbitSinePlay(sessionId, state) {
1577
1713
  return;
1578
1714
  }
1579
1715
  state.orbitSinePlayId = result.playId;
1716
+ scheduleOrbitSineGaplessRestart(sessionId, state);
1580
1717
  startOrbitSineLoopPoll(sessionId, state);
1581
1718
  }
1582
1719
  function emitOrbitPose(sessionId, state) {
1583
- if (!state.orbitStartMs) {
1720
+ if (!state.orbitStartMs && !state.orbitManualPose) {
1584
1721
  return;
1585
1722
  }
1586
1723
  const now = Date.now();
@@ -1588,9 +1725,8 @@ function emitOrbitPose(sessionId, state) {
1588
1725
  return;
1589
1726
  }
1590
1727
  state.lastOrbitPoseEmitMs = now;
1591
- const elapsedSec = (now - state.orbitStartMs) / 1e3;
1592
- const pose = orbitTtsPose(elapsedSec, state.orbit);
1593
- const angleRad = 2 * Math.PI * elapsedSec / (state.orbit.periodSec ?? 2 * Math.PI);
1728
+ const pose = currentOrbitPose(state);
1729
+ const angleRad = state.orbitManualPose ? Math.atan2(state.orbitManualPose.z, state.orbitManualPose.x) : 2 * Math.PI * (now - state.orbitStartMs) / 1e3 / (state.orbit.periodSec ?? 2 * Math.PI);
1594
1730
  sendToClient(sessionId, {
1595
1731
  type: "orbit_pose",
1596
1732
  angleDeg: angleRad * 180 / Math.PI,
@@ -1598,12 +1734,22 @@ function emitOrbitPose(sessionId, state) {
1598
1734
  z: pose.position.z
1599
1735
  });
1600
1736
  }
1737
+ function emitOrbitPlacePose(sessionId, state, x, z) {
1738
+ state.lastOrbitPoseEmitMs = Date.now();
1739
+ const angleRad = Math.atan2(z, x);
1740
+ sendToClient(sessionId, {
1741
+ type: "orbit_pose",
1742
+ angleDeg: angleRad * 180 / Math.PI,
1743
+ x,
1744
+ z
1745
+ });
1746
+ }
1601
1747
  function startOrbitDemo(sessionId, state) {
1602
1748
  stopOrbitInterval(state);
1603
1749
  beginOrbitClock(state);
1604
1750
  void startOrbitSinePlay(sessionId, state);
1605
1751
  state.orbitTimer = setInterval(() => {
1606
- if (state.orbit.paused || !state.orbitStartMs) {
1752
+ if (state.orbit.paused || !state.orbitStartMs && !state.orbitManualPose) {
1607
1753
  return;
1608
1754
  }
1609
1755
  const pose = currentOrbitPose(state);
@@ -1758,6 +1904,24 @@ async function handlePadPlay(sessionId, state, clipId, x, z, volume, placement)
1758
1904
  void startLoopPadPoll(sessionId, state, result.playId, pad);
1759
1905
  }
1760
1906
  }
1907
+ async function handlePadMove(sessionId, state, playId, x, z) {
1908
+ if (!state.playIds.has(playId)) {
1909
+ ackError(sessionId, "pad_move", "unknown_play");
1910
+ return;
1911
+ }
1912
+ const poseResult = await setPlayPose(playId, poseAt(x, z));
1913
+ if (!poseResult.ok) {
1914
+ ackError(sessionId, "pad_move", poseResult.reason ?? "set_pose_failed");
1915
+ return;
1916
+ }
1917
+ const loopPad = state.loopPads.get(playId);
1918
+ if (loopPad) {
1919
+ loopPad.x = x;
1920
+ loopPad.z = z;
1921
+ delete loopPad.placement;
1922
+ }
1923
+ ack(sessionId, "pad_move", { playId, x, z });
1924
+ }
1761
1925
  async function handleJoin(sessionId, demo, assetOrigin) {
1762
1926
  teardownSession(sessionId);
1763
1927
  const state = defaultSessionState(demo, assetOrigin);
@@ -1884,11 +2048,54 @@ async function dispatchMessage(sessionId, message) {
1884
2048
  state.orbit.elevation = message.elevation;
1885
2049
  }
1886
2050
  if (message.paused !== void 0) {
2051
+ if (message.paused === false && state.orbitManualPose) {
2052
+ syncOrbitClockFromManualPose(state);
2053
+ }
1887
2054
  state.orbit.paused = message.paused;
1888
2055
  }
2056
+ if (message.frequencyHz !== void 0) {
2057
+ state.orbitSineFrequencyHz = message.frequencyHz;
2058
+ if (state.orbitSineEnabled && state.orbitSinePlayId) {
2059
+ void startOrbitSinePlay(sessionId, state);
2060
+ }
2061
+ }
2062
+ if (message.sinePlaying !== void 0) {
2063
+ state.orbitSineEnabled = message.sinePlaying;
2064
+ if (message.sinePlaying) {
2065
+ if (!state.orbitSinePlayId) {
2066
+ void startOrbitSinePlay(sessionId, state);
2067
+ }
2068
+ } else {
2069
+ void stopOrbitSine(state);
2070
+ }
2071
+ }
1889
2072
  ack(sessionId, "orbit_set");
1890
2073
  return;
1891
2074
  }
2075
+ if (message.action === "place") {
2076
+ state.orbit.paused = true;
2077
+ state.orbitManualPose = { x: message.x, z: message.z };
2078
+ const pose = orbitPoseFromXz(state, message.x, message.z);
2079
+ if (state.orbitSinePlayId) {
2080
+ void safeMixCall(
2081
+ sessionId,
2082
+ "setPlayPose",
2083
+ () => setPlayPose(state.orbitSinePlayId, pose)
2084
+ );
2085
+ }
2086
+ void safeMixCall(
2087
+ sessionId,
2088
+ "setTtsPose",
2089
+ () => setTtsPose(sessionId, pose)
2090
+ );
2091
+ emitOrbitPlacePose(sessionId, state, message.x, message.z);
2092
+ ack(sessionId, "orbit_place");
2093
+ return;
2094
+ }
2095
+ sendToClient(sessionId, {
2096
+ type: "orbit_say_status",
2097
+ status: "playing"
2098
+ });
1892
2099
  speak(sessionId, message.text);
1893
2100
  ack(sessionId, "orbit_say");
1894
2101
  return;
@@ -1945,6 +2152,20 @@ async function dispatchMessage(sessionId, message) {
1945
2152
  }
1946
2153
  return;
1947
2154
  }
2155
+ case "pad_move": {
2156
+ if (state.demo !== "soundboard") {
2157
+ ackError(sessionId, "pad_move", "wrong_demo");
2158
+ return;
2159
+ }
2160
+ await handlePadMove(
2161
+ sessionId,
2162
+ state,
2163
+ message.playId,
2164
+ message.x,
2165
+ message.z
2166
+ );
2167
+ return;
2168
+ }
1948
2169
  case "move": {
1949
2170
  if (state.demo !== "proximity") {
1950
2171
  ackError(sessionId, "move", "wrong_demo");
@@ -2027,6 +2248,23 @@ defineAgent({
2027
2248
  }
2028
2249
  void dispatchMessage(ctx.sessionId, message);
2029
2250
  },
2251
+ onSpeechEvent(ctx, event) {
2252
+ const state = sessions.get(ctx.sessionId);
2253
+ if (!state || state.demo !== "orbit") {
2254
+ return;
2255
+ }
2256
+ if (event.type === "agent_speaking_start") {
2257
+ sendToClient(ctx.sessionId, {
2258
+ type: "orbit_say_status",
2259
+ status: "playing"
2260
+ });
2261
+ } else if (event.type === "agent_speaking_end") {
2262
+ sendToClient(ctx.sessionId, {
2263
+ type: "orbit_say_status",
2264
+ status: "ended"
2265
+ });
2266
+ }
2267
+ },
2030
2268
  onSessionEnd({ sessionId }) {
2031
2269
  teardownSession(sessionId);
2032
2270
  agentLog("info", `spatial-showcase session_end ${sessionId}`, sessionId);
@@ -2034,5 +2272,6 @@ defineAgent({
2034
2272
  });
2035
2273
  export {
2036
2274
  MAX_ACTIVE_PLAYS,
2275
+ handlePadMove,
2037
2276
  handlePadPlay
2038
2277
  };
@@ -779,7 +779,7 @@ function agentLog(level, message, fieldsOrSessionId, sessionId) {
779
779
  sendParentMessage(buildAgentLogPayload(level, message, fields, resolvedSessionId));
780
780
  }
781
781
 
782
- // templates/agent.ts
782
+ // templates/voice-starter/agent.ts
783
783
  var peers = /* @__PURE__ */ new Map();
784
784
  function peerState(sessionId) {
785
785
  let state = peers.get(sessionId);
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  const require = createRequire(import.meta.url);
3
3
 
4
4
 
5
- // templates/webhooks.ts
5
+ // templates/webhooks/agent.ts
6
6
  import { createHmac, timingSafeEqual } from "node:crypto";
7
7
 
8
8
  // dist/runtime.js
@@ -790,7 +790,7 @@ function agentLog(level, message, fieldsOrSessionId, sessionId) {
790
790
  sendParentMessage(buildAgentLogPayload(level, message, fields, resolvedSessionId));
791
791
  }
792
792
 
793
- // templates/webhooks.ts
793
+ // templates/webhooks/agent.ts
794
794
  var WEBHOOK_SIGNATURE_HEADER = "x-agent-webhook-signature";
795
795
  var connectedSessions = /* @__PURE__ */ new Set();
796
796
  function getHeaderCaseInsensitive(headers, name) {
@@ -10753,7 +10753,7 @@ var require_built3 = __commonJS({
10753
10753
  }
10754
10754
  });
10755
10755
 
10756
- // templates/webhooks-redis.ts
10756
+ // templates/webhooks-redis/agent.ts
10757
10757
  var import_ioredis = __toESM(require_built3(), 1);
10758
10758
  import { createHmac, timingSafeEqual } from "node:crypto";
10759
10759
 
@@ -11539,7 +11539,7 @@ function agentLog(level, message, fieldsOrSessionId, sessionId) {
11539
11539
  sendParentMessage(buildAgentLogPayload(level, message, fields, resolvedSessionId));
11540
11540
  }
11541
11541
 
11542
- // templates/webhooks-redis.ts
11542
+ // templates/webhooks-redis/agent.ts
11543
11543
  var WEBHOOK_SIGNATURE_HEADER = "x-agent-webhook-signature";
11544
11544
  var REDIS_COUNTER_KEY = "agent:webhook:event_count";
11545
11545
  var LUA_INCREMENT_COUNTER = `
@@ -11582,7 +11582,7 @@ defineAgent({
11582
11582
  );
11583
11583
  return;
11584
11584
  }
11585
- redis = new import_ioredis.default(redisUrl, {
11585
+ redis = new import_ioredis.Redis(redisUrl, {
11586
11586
  maxRetriesPerRequest: 3,
11587
11587
  lazyConnect: true
11588
11588
  });
@@ -11614,7 +11614,11 @@ defineAgent({
11614
11614
  }
11615
11615
  let eventCount = null;
11616
11616
  if (redis) {
11617
- const result = await redis.eval(LUA_INCREMENT_COUNTER, 1, REDIS_COUNTER_KEY);
11617
+ const result = await redis.eval(
11618
+ LUA_INCREMENT_COUNTER,
11619
+ 1,
11620
+ REDIS_COUNTER_KEY
11621
+ );
11618
11622
  eventCount = typeof result === "number" ? result : Number(result);
11619
11623
  }
11620
11624
  const sessionIds = ctx.sessionIds.length > 0 ? ctx.sessionIds : [...connectedSessions];
@@ -11641,3 +11645,6 @@ defineAgent({
11641
11645
  );
11642
11646
  }
11643
11647
  });
11648
+ export {
11649
+ LUA_INCREMENT_COUNTER
11650
+ };