@framers/agentos 0.1.108 → 0.1.109

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 (37) hide show
  1. package/dist/voice-pipeline/AcousticEndpointDetector.d.ts +95 -20
  2. package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -1
  3. package/dist/voice-pipeline/AcousticEndpointDetector.js +110 -24
  4. package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -1
  5. package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +66 -15
  6. package/dist/voice-pipeline/HardCutBargeinHandler.d.ts.map +1 -1
  7. package/dist/voice-pipeline/HardCutBargeinHandler.js +65 -13
  8. package/dist/voice-pipeline/HardCutBargeinHandler.js.map +1 -1
  9. package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +116 -42
  10. package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -1
  11. package/dist/voice-pipeline/HeuristicEndpointDetector.js +159 -52
  12. package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -1
  13. package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +89 -24
  14. package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts.map +1 -1
  15. package/dist/voice-pipeline/SoftFadeBargeinHandler.js +74 -20
  16. package/dist/voice-pipeline/SoftFadeBargeinHandler.js.map +1 -1
  17. package/dist/voice-pipeline/VoiceInterruptError.d.ts +68 -10
  18. package/dist/voice-pipeline/VoiceInterruptError.d.ts.map +1 -1
  19. package/dist/voice-pipeline/VoiceInterruptError.js +53 -6
  20. package/dist/voice-pipeline/VoiceInterruptError.js.map +1 -1
  21. package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +190 -39
  22. package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -1
  23. package/dist/voice-pipeline/VoicePipelineOrchestrator.js +266 -53
  24. package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -1
  25. package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +135 -43
  26. package/dist/voice-pipeline/WebSocketStreamTransport.d.ts.map +1 -1
  27. package/dist/voice-pipeline/WebSocketStreamTransport.js +109 -47
  28. package/dist/voice-pipeline/WebSocketStreamTransport.js.map +1 -1
  29. package/dist/voice-pipeline/index.d.ts +34 -1
  30. package/dist/voice-pipeline/index.d.ts.map +1 -1
  31. package/dist/voice-pipeline/index.js +41 -1
  32. package/dist/voice-pipeline/index.js.map +1 -1
  33. package/dist/voice-pipeline/types.d.ts +432 -106
  34. package/dist/voice-pipeline/types.d.ts.map +1 -1
  35. package/dist/voice-pipeline/types.js +21 -9
  36. package/dist/voice-pipeline/types.js.map +1 -1
  37. package/package.json +1 -1
@@ -1,30 +1,72 @@
1
1
  /**
2
2
  * @module voice-pipeline/SoftFadeBargeinHandler
3
3
  *
4
- * Implements a three-tier soft-fade barge-in policy.
4
+ * Implements a three-tier soft-fade barge-in policy that maps detected speech
5
+ * duration to one of three actions: ignore, pause (with fade-out), or cancel.
5
6
  *
6
- * Very short speech detections (< `ignoreMs`) are dismissed as noise.
7
- * Medium-length detections trigger a fade-out pause so the user can speak
8
- * without an abrupt cut. Long detections (>= `cancelMs`) stop playback
9
- * outright and inject a conversation marker.
7
+ * ## Three-tier logic
8
+ *
9
+ * The handler divides the speech duration axis into three regions:
10
+ *
11
+ * ```
12
+ * 0 ms ignoreMs cancelMs
13
+ * |-------- ignore --------|-------- pause --------|-------- cancel -------->
14
+ * (noise) (fade-out) (hard stop)
15
+ * ```
16
+ *
17
+ * | Region | Action | Rationale |
18
+ * |---------------------------------|----------|-----------------------------------------------|
19
+ * | `speechDurationMs < ignoreMs` | `ignore` | Too short to be intentional (noise, breath). |
20
+ * | `ignoreMs <= speech < cancelMs` | `pause` | Probably intentional; fade out gracefully. |
21
+ * | `speechDurationMs >= cancelMs` | `cancel` | Definitely intentional; stop immediately. |
22
+ *
23
+ * ## Configurable thresholds
24
+ *
25
+ * - **`ignoreMs`** (default 100 ms): The noise floor. Anything shorter than
26
+ * this is dismissed. Set lower in quiet environments, higher in noisy ones.
27
+ *
28
+ * - **`cancelMs`** (default 2,000 ms): The hard-stop ceiling. By this point,
29
+ * the user has clearly been speaking for a while and wants to take over.
30
+ * The pipeline should stop TTS immediately rather than fading.
31
+ *
32
+ * - **`fadeMs`** (default 200 ms): The duration of the audio fade-out applied
33
+ * during a `'pause'` action. Shorter fades (100 ms) feel snappier; longer
34
+ * fades (300+ ms) feel smoother but delay the user's ability to be heard.
35
+ *
36
+ * ## When to use soft-fade vs hard-cut
37
+ *
38
+ * Soft-fade is preferred when:
39
+ * - The environment is noisy and false barge-in detections are common.
40
+ * - The conversation is measured/educational and abrupt cuts feel jarring.
41
+ * - The TTS voice has long trailing prosody that benefits from a fade.
42
+ *
43
+ * Use {@link HardCutBargeinHandler} when:
44
+ * - The conversation is fast-paced (customer support, command interfaces).
45
+ * - Audio quality is high and false positives are rare.
46
+ * - Minimal interruption latency is critical.
47
+ *
48
+ * @see {@link HardCutBargeinHandler} for the binary hard-cut alternative.
49
+ * @see {@link IBargeinHandler} for the interface contract.
10
50
  */
51
+ // ---------------------------------------------------------------------------
52
+ // Implementation
53
+ // ---------------------------------------------------------------------------
11
54
  /**
12
55
  * Barge-in handler that applies a three-tier soft-fade strategy.
13
56
  *
14
- * The handler maps the confirmed speech duration to one of three actions:
57
+ * The handler is stateless -- each {@link handleBargein} call is evaluated
58
+ * independently with no memory of previous barge-in events.
15
59
  *
16
- * | Speech duration | Action |
17
- * |--------------------------|---------------------------------------------|
18
- * | `< ignoreMs` | `ignore` — noise, continue TTS uninterrupted |
19
- * | `>= ignoreMs < cancelMs` | `pause` with `fadeMs` fade-out |
20
- * | `>= cancelMs` | `cancel` with `'[interrupted]'` marker |
60
+ * @see {@link IBargeinHandler} for the interface contract.
61
+ * @see {@link HardCutBargeinHandler} for the binary hard-cut alternative.
21
62
  *
22
63
  * @example
23
- * ```ts
64
+ * ```typescript
24
65
  * const handler = new SoftFadeBargeinHandler({ ignoreMs: 80, cancelMs: 1500, fadeMs: 150 });
25
- * handler.handleBargein({ speechDurationMs: 500, ... }); // { type: 'pause', fadeMs: 150 }
26
- * handler.handleBargein({ speechDurationMs: 1600, ... }); // { type: 'cancel', injectMarker: '[interrupted]' }
27
- * handler.handleBargein({ speechDurationMs: 30, ... }); // { type: 'ignore' }
66
+ *
67
+ * handler.handleBargein({ speechDurationMs: 30, ... }); // -> { type: 'ignore' }
68
+ * handler.handleBargein({ speechDurationMs: 500, ... }); // -> { type: 'pause', fadeMs: 150 }
69
+ * handler.handleBargein({ speechDurationMs: 1600, ... }); // -> { type: 'cancel', injectMarker: '[interrupted]' }
28
70
  * ```
29
71
  */
30
72
  export class SoftFadeBargeinHandler {
@@ -37,7 +79,7 @@ export class SoftFadeBargeinHandler {
37
79
  constructor(options = {}) {
38
80
  /**
39
81
  * The interruption strategy implemented by this handler.
40
- * Always `'soft-fade'`.
82
+ * Always `'soft-fade'` -- TTS audio is faded out over a configurable window.
41
83
  */
42
84
  this.mode = 'soft-fade';
43
85
  this.ignoreMs = options.ignoreMs ?? 100;
@@ -48,21 +90,33 @@ export class SoftFadeBargeinHandler {
48
90
  * Evaluate the barge-in context and return the pipeline action.
49
91
  *
50
92
  * Decision tree (evaluated in order):
51
- * 1. `speechDurationMs < ignoreMs` → `{ type: 'ignore' }`
52
- * 2. `speechDurationMs >= cancelMs` `{ type: 'cancel', injectMarker: '[interrupted]' }`
53
- * 3. Otherwise `{ type: 'pause', fadeMs }`
93
+ *
94
+ * 1. `speechDurationMs < ignoreMs` -> `{ type: 'ignore' }`
95
+ * Too short to be intentional. Likely a lip smack, breath, or noise burst.
96
+ *
97
+ * 2. `speechDurationMs >= cancelMs` -> `{ type: 'cancel', injectMarker: '[interrupted]' }`
98
+ * The user has been speaking long enough that they clearly want to take over.
99
+ * Stop TTS immediately and mark the conversation as interrupted.
100
+ *
101
+ * 3. Otherwise (ignoreMs <= speech < cancelMs) -> `{ type: 'pause', fadeMs }`
102
+ * Probably intentional but not yet certain. Fade out TTS gracefully so the
103
+ * user can be heard. If the speech stops, the pipeline can resume playback.
54
104
  *
55
105
  * @param context - Snapshot of the barge-in state at the moment of detection.
56
- * @returns The pipeline action to execute.
106
+ * @returns The pipeline action to execute. Always synchronous (no Promise).
57
107
  */
58
108
  handleBargein(context) {
59
109
  const { speechDurationMs } = context;
110
+ // Tier 1: Noise floor -- dismiss as accidental
60
111
  if (speechDurationMs < this.ignoreMs) {
61
112
  return { type: 'ignore' };
62
113
  }
114
+ // Tier 3: Hard stop -- user has been speaking long enough to be certain.
115
+ // (Evaluated before Tier 2 to avoid the pause action for long speech.)
63
116
  if (speechDurationMs >= this.cancelMs) {
64
117
  return { type: 'cancel', injectMarker: '[interrupted]' };
65
118
  }
119
+ // Tier 2: Fade region -- probably intentional, fade out gracefully.
66
120
  return { type: 'pause', fadeMs: this.fadeMs };
67
121
  }
68
122
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SoftFadeBargeinHandler.js","sourceRoot":"","sources":["../../src/voice-pipeline/SoftFadeBargeinHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkCH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,sBAAsB;IAsBjC;;;;;OAKG;IACH,YAAY,UAAyC,EAAE;QA3BvD;;;WAGG;QACM,SAAI,GAAG,WAAoB,CAAC;QAwBnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;IACtC,CAAC;IAED;;;;;;;;;;OAUG;IACH,aAAa,CAAC,OAAuB;QACnC,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC;QAErC,IAAI,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAED,IAAI,gBAAgB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;QAC3D,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAChD,CAAC;CACF"}
1
+ {"version":3,"file":"SoftFadeBargeinHandler.js","sourceRoot":"","sources":["../../src/voice-pipeline/SoftFadeBargeinHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAqDH,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,sBAAsB;IAyBjC;;;;;OAKG;IACH,YAAY,UAAyC,EAAE;QA9BvD;;;WAGG;QACM,SAAI,GAAG,WAAoB,CAAC;QA2BnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;IACtC,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,OAAuB;QACnC,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC;QAErC,+CAA+C;QAC/C,IAAI,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAED,yEAAyE;QACzE,uEAAuE;QACvE,IAAI,gBAAgB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;QAC3D,CAAC;QAED,oEAAoE;QACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAChD,CAAC;CACF"}
@@ -3,45 +3,103 @@
3
3
  *
4
4
  * Typed error thrown when a voice session is interrupted by user barge-in.
5
5
  * Used by VoiceNodeExecutor to catch barge-in AbortSignal and return
6
- * a structured result instead of propagating the error.
6
+ * a structured result instead of propagating a generic Error.
7
+ *
8
+ * ## Design rationale
9
+ *
10
+ * Using a typed error subclass (rather than a plain Error with a message string)
11
+ * allows consumers to:
12
+ * 1. Catch specifically via `instanceof VoiceInterruptError`.
13
+ * 2. Access structured fields ({@link interruptedText}, {@link userSpeech},
14
+ * {@link playedDurationMs}) without parsing an error message.
15
+ * 3. Distinguish barge-in interruptions from other pipeline errors in
16
+ * catch blocks and error boundaries.
17
+ *
18
+ * The `name` property is set as a class field (not via the prototype) so that
19
+ * it survives serialisation and can be used for `instanceof`-free checks when
20
+ * errors cross process boundaries (e.g. worker threads, RPC).
7
21
  */
8
22
  /**
9
23
  * Structured error representing a user barge-in interruption during TTS playback.
10
24
  *
11
25
  * Thrown (or returned as a typed sentinel) when the user speaks over the agent.
12
- * Consumers can inspect `interruptedText`, `userSpeech`, and `playedDurationMs`
13
- * to decide how to resume or branch the conversation graph.
26
+ * Consumers can inspect {@link interruptedText}, {@link userSpeech}, and
27
+ * {@link playedDurationMs} to decide how to resume or branch the conversation graph.
28
+ *
29
+ * @see {@link IBargeinHandler} which triggers the barge-in decision.
30
+ * @see {@link VoicePipelineOrchestrator} which transitions through INTERRUPTING state.
14
31
  *
15
- * @example
32
+ * @example Catching a barge-in interruption
16
33
  * ```typescript
17
34
  * try {
18
35
  * await voiceNode.run(context);
19
36
  * } catch (err) {
20
37
  * if (err instanceof VoiceInterruptError) {
21
- * console.log(`Interrupted after ${err.playedDurationMs}ms`);
38
+ * console.log(`Agent was saying: "${err.interruptedText}"`);
39
+ * console.log(`User said: "${err.userSpeech}"`);
40
+ * console.log(`Played ${err.playedDurationMs}ms before interruption`);
41
+ * // Resume conversation from the user's barge-in utterance
22
42
  * await handleBargein(err.userSpeech);
43
+ * } else {
44
+ * throw err; // Re-throw non-barge-in errors
23
45
  * }
24
46
  * }
25
47
  * ```
48
+ *
49
+ * @example Checking without instanceof (e.g. cross-process)
50
+ * ```typescript
51
+ * if (error.name === 'VoiceInterruptError') {
52
+ * // Safe to access barge-in fields
53
+ * }
54
+ * ```
26
55
  */
27
56
  export declare class VoiceInterruptError extends Error {
28
- /** Discriminant name — always `'VoiceInterruptError'` for `instanceof`-free checks. */
57
+ /**
58
+ * Discriminant name -- always `'VoiceInterruptError'`.
59
+ *
60
+ * Set as a class field (not via prototype) so it:
61
+ * - Survives JSON serialisation/deserialisation.
62
+ * - Can be used for `instanceof`-free type discrimination.
63
+ * - Overrides the default `Error` name in stack traces.
64
+ */
29
65
  readonly name = "VoiceInterruptError";
30
- /** The text the agent was speaking when interrupted. */
66
+ /**
67
+ * The full text the agent was speaking when interrupted.
68
+ * Corresponds to the cumulative TTS text accumulated by the orchestrator
69
+ * at the point of barge-in detection.
70
+ */
31
71
  readonly interruptedText: string;
32
- /** What the user said that caused the interruption. */
72
+ /**
73
+ * What the user said that caused the interruption.
74
+ * Typically the partial or final STT transcript captured during the
75
+ * barge-in speech detection.
76
+ */
33
77
  readonly userSpeech: string;
34
78
  /**
35
79
  * How much of the agent's response was already played (ms).
36
- * Derived from the cumulative `durationMs` of `EncodedAudioChunk`s sent
37
- * to the transport before the barge-in was detected.
80
+ * Derived from the cumulative `durationMs` of {@link EncodedAudioChunk}s
81
+ * sent to the transport before the barge-in was detected.
82
+ *
83
+ * Combined with {@link interruptedText}, this allows the consumer to
84
+ * estimate how much of the response the user actually heard.
38
85
  */
39
86
  readonly playedDurationMs: number;
40
87
  /**
88
+ * Create a new VoiceInterruptError with structured barge-in context.
89
+ *
41
90
  * @param context - Barge-in context captured at the moment of interruption.
42
91
  * @param context.interruptedText - Full TTS text the agent was speaking.
43
92
  * @param context.userSpeech - Transcript of the user's barge-in utterance.
44
93
  * @param context.playedDurationMs - Milliseconds of audio already played.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * throw new VoiceInterruptError({
98
+ * interruptedText: 'I was explaining the process of...',
99
+ * userSpeech: 'Wait, go back to the first step.',
100
+ * playedDurationMs: 2300,
101
+ * });
102
+ * ```
45
103
  */
46
104
  constructor(context: {
47
105
  interruptedText: string;
@@ -1 +1 @@
1
- {"version":3,"file":"VoiceInterruptError.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,uFAAuF;IACvF,QAAQ,CAAC,IAAI,yBAAyB;IAEtC,wDAAwD;IACxD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAElC;;;;;OAKG;gBACS,OAAO,EAAE;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;KAC1B;CAMF"}
1
+ {"version":3,"file":"VoiceInterruptError.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,yBAAyB;IAEtC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAElC;;;;;;;;;;;;;;;;OAgBG;gBACS,OAAO,EAAE;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;KAC1B;CAMF"}
@@ -3,37 +3,84 @@
3
3
  *
4
4
  * Typed error thrown when a voice session is interrupted by user barge-in.
5
5
  * Used by VoiceNodeExecutor to catch barge-in AbortSignal and return
6
- * a structured result instead of propagating the error.
6
+ * a structured result instead of propagating a generic Error.
7
+ *
8
+ * ## Design rationale
9
+ *
10
+ * Using a typed error subclass (rather than a plain Error with a message string)
11
+ * allows consumers to:
12
+ * 1. Catch specifically via `instanceof VoiceInterruptError`.
13
+ * 2. Access structured fields ({@link interruptedText}, {@link userSpeech},
14
+ * {@link playedDurationMs}) without parsing an error message.
15
+ * 3. Distinguish barge-in interruptions from other pipeline errors in
16
+ * catch blocks and error boundaries.
17
+ *
18
+ * The `name` property is set as a class field (not via the prototype) so that
19
+ * it survives serialisation and can be used for `instanceof`-free checks when
20
+ * errors cross process boundaries (e.g. worker threads, RPC).
7
21
  */
8
22
  /**
9
23
  * Structured error representing a user barge-in interruption during TTS playback.
10
24
  *
11
25
  * Thrown (or returned as a typed sentinel) when the user speaks over the agent.
12
- * Consumers can inspect `interruptedText`, `userSpeech`, and `playedDurationMs`
13
- * to decide how to resume or branch the conversation graph.
26
+ * Consumers can inspect {@link interruptedText}, {@link userSpeech}, and
27
+ * {@link playedDurationMs} to decide how to resume or branch the conversation graph.
28
+ *
29
+ * @see {@link IBargeinHandler} which triggers the barge-in decision.
30
+ * @see {@link VoicePipelineOrchestrator} which transitions through INTERRUPTING state.
14
31
  *
15
- * @example
32
+ * @example Catching a barge-in interruption
16
33
  * ```typescript
17
34
  * try {
18
35
  * await voiceNode.run(context);
19
36
  * } catch (err) {
20
37
  * if (err instanceof VoiceInterruptError) {
21
- * console.log(`Interrupted after ${err.playedDurationMs}ms`);
38
+ * console.log(`Agent was saying: "${err.interruptedText}"`);
39
+ * console.log(`User said: "${err.userSpeech}"`);
40
+ * console.log(`Played ${err.playedDurationMs}ms before interruption`);
41
+ * // Resume conversation from the user's barge-in utterance
22
42
  * await handleBargein(err.userSpeech);
43
+ * } else {
44
+ * throw err; // Re-throw non-barge-in errors
23
45
  * }
24
46
  * }
25
47
  * ```
48
+ *
49
+ * @example Checking without instanceof (e.g. cross-process)
50
+ * ```typescript
51
+ * if (error.name === 'VoiceInterruptError') {
52
+ * // Safe to access barge-in fields
53
+ * }
54
+ * ```
26
55
  */
27
56
  export class VoiceInterruptError extends Error {
28
57
  /**
58
+ * Create a new VoiceInterruptError with structured barge-in context.
59
+ *
29
60
  * @param context - Barge-in context captured at the moment of interruption.
30
61
  * @param context.interruptedText - Full TTS text the agent was speaking.
31
62
  * @param context.userSpeech - Transcript of the user's barge-in utterance.
32
63
  * @param context.playedDurationMs - Milliseconds of audio already played.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * throw new VoiceInterruptError({
68
+ * interruptedText: 'I was explaining the process of...',
69
+ * userSpeech: 'Wait, go back to the first step.',
70
+ * playedDurationMs: 2300,
71
+ * });
72
+ * ```
33
73
  */
34
74
  constructor(context) {
35
75
  super('Voice session interrupted by user');
36
- /** Discriminant name — always `'VoiceInterruptError'` for `instanceof`-free checks. */
76
+ /**
77
+ * Discriminant name -- always `'VoiceInterruptError'`.
78
+ *
79
+ * Set as a class field (not via prototype) so it:
80
+ * - Survives JSON serialisation/deserialisation.
81
+ * - Can be used for `instanceof`-free type discrimination.
82
+ * - Overrides the default `Error` name in stack traces.
83
+ */
37
84
  this.name = 'VoiceInterruptError';
38
85
  this.interruptedText = context.interruptedText;
39
86
  this.userSpeech = context.userSpeech;
@@ -1 +1 @@
1
- {"version":3,"file":"VoiceInterruptError.js","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAiB5C;;;;;OAKG;IACH,YAAY,OAIX;QACC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QA3B7C,uFAAuF;QAC9E,SAAI,GAAG,qBAAqB,CAAC;QA2BpC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACnD,CAAC;CACF"}
1
+ {"version":3,"file":"VoiceInterruptError.js","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAmC5C;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,OAIX;QACC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAxD7C;;;;;;;WAOG;QACM,SAAI,GAAG,qBAAqB,CAAC;QAiDpC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACnD,CAAC;CACF"}