@lumavox/client 0.10.0 → 0.11.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.
@@ -13,10 +13,14 @@
13
13
  * - **Barge-in in two steps** (D15): talking while the agent's voice plays
14
14
  * lowers it (`AgentAudio.duck`) and opens the recording; the edge says
15
15
  * `barge_in` on its first words that are not the agent's own, and the
16
- * voice stops. No words within `wordsWithinMs` and it was not the person:
17
- * the recording is thrown away (`discardAudio`), the voice comes back up,
18
- * and the detector must hear silence before it opens another — so the
19
- * agent's own leftover voice, or a long cough, never loops.
16
+ * voice stops. **Over the agent, only words end an utterance, never
17
+ * silence**: the transcriber's first words come about 2 s after the first
18
+ * word (`wordsWithinMs`), and a short "Stop." is heard only while audio
19
+ * still reaches it — ended at 700 ms of silence, it never was. No words
20
+ * within `wordsWithinMs` and it was not the person: the recording is
21
+ * thrown away (`discardAudio`), the voice comes back up, and the detector
22
+ * must hear silence before it opens another — so the agent's own leftover
23
+ * voice, or a long cough, never loops.
20
24
  * - **The detector's threshold rises while the agent speaks**: what echo
21
25
  * cancellation leaves of its voice is louder than a quiet room.
22
26
  * - **Refused where the agent would hear itself** (D16): an adapter that
@@ -75,9 +79,11 @@ export interface ListenOptions extends TurnOptions {
75
79
  startAfterMs?: number;
76
80
  /**
77
81
  * Over the agent's voice: no words this long into the talking, and it
78
- * was not the person, ms: 1,200 — the plan's ~0.7 s and the partial
79
- * transcripts' lag behind speech (left-overs #101), until a device
80
- * measures it.
82
+ * was not the person, ms: 3,000. On staging (2026-10-31, speech paced in
83
+ * real time) the first words came 2.06 to 2.46 s after the first word
84
+ * spoken — the transcriber's session opening with the recording — and
85
+ * 1.47 s on one already open; the plan's ~0.7 s would have thrown every
86
+ * barge-in away.
81
87
  */
82
88
  wordsWithinMs?: number;
83
89
  /** The app knows the agent's voice stays out of the microphone — headphones, say — though the adapter cannot tell. */
@@ -87,7 +87,7 @@ class HandsFree {
87
87
  this.detector = options.detector ?? loudness();
88
88
  this.endAfterMs = options.endAfterMs ?? 700;
89
89
  this.startAfterMs = options.startAfterMs ?? 100;
90
- this.wordsWithinMs = options.wordsWithinMs ?? 1200;
90
+ this.wordsWithinMs = options.wordsWithinMs ?? 3000;
91
91
  this.ringFrames = Math.ceil(((options.preRollMs ?? 300) + this.startAfterMs) / FRAME_MS);
92
92
  }
93
93
  /** The microphone, and everything watched: `null`, or why it did not open. */
@@ -153,6 +153,9 @@ class HandsFree {
153
153
  this.audio.stop();
154
154
  this.audio.duck?.(false);
155
155
  this.options.onBargeIn?.(frame);
156
+ // The words came after the person stopped: the turn is theirs now.
157
+ if (this.quietMs >= this.endAfterMs)
158
+ this.end();
156
159
  }
157
160
  /** The device took the audio, and closed the recorder; or gave it back. */
158
161
  interrupted(now) {
@@ -215,9 +218,11 @@ class HandsFree {
215
218
  }
216
219
  this.conversation.sendAudio(frame);
217
220
  open.ms += FRAME_MS;
218
- if (open.barging && !open.barged && open.ms >= this.wordsWithinMs) {
219
- this.throwAway();
220
- this.waitForQuiet = true;
221
+ if (open.barging && !open.barged) {
222
+ if (open.ms >= this.wordsWithinMs) {
223
+ this.throwAway();
224
+ this.waitForQuiet = this.quietMs < this.endAfterMs;
225
+ }
221
226
  }
222
227
  else if (this.quietMs >= this.endAfterMs) {
223
228
  this.end();
@@ -241,13 +246,9 @@ class HandsFree {
241
246
  this.conversation.sendAudio(frame);
242
247
  this.set("hearing");
243
248
  }
244
- /** Silence: a turn — but over the agent, only once the person's own words were heard. */
249
+ /** Silence after the person's words: a turn. */
245
250
  end() {
246
251
  const open = this.open;
247
- if (open.barging && !open.barged) {
248
- this.throwAway();
249
- return;
250
- }
251
252
  this.open = null;
252
253
  if (open.barging)
253
254
  this.audio.duck?.(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumavox/client",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Talk to Lumavox agents from a browser, a React Native app or Node, with an end user's ephemeral key: conversations on the agent's socket, sessions, memory and relations. Zero dependencies.",
5
5
  "license": "MIT",
6
6
  "type": "module",