@framers/agentos 0.1.107 → 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.
- package/dist/memory/ingestion/ChunkingEngine.d.ts.map +1 -1
- package/dist/memory/ingestion/ChunkingEngine.js +5 -1
- package/dist/memory/ingestion/ChunkingEngine.js.map +1 -1
- package/dist/memory/ingestion/DocxLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/DocxLoader.js +2 -1
- package/dist/memory/ingestion/DocxLoader.js.map +1 -1
- package/dist/memory/ingestion/FolderScanner.d.ts.map +1 -1
- package/dist/memory/ingestion/FolderScanner.js +6 -3
- package/dist/memory/ingestion/FolderScanner.js.map +1 -1
- package/dist/memory/ingestion/HtmlLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/HtmlLoader.js +2 -1
- package/dist/memory/ingestion/HtmlLoader.js.map +1 -1
- package/dist/memory/ingestion/MarkdownLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/MarkdownLoader.js +2 -1
- package/dist/memory/ingestion/MarkdownLoader.js.map +1 -1
- package/dist/memory/ingestion/PdfLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/PdfLoader.js +2 -1
- package/dist/memory/ingestion/PdfLoader.js.map +1 -1
- package/dist/memory/ingestion/TextLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/TextLoader.js +3 -2
- package/dist/memory/ingestion/TextLoader.js.map +1 -1
- package/dist/memory/ingestion/pathUtils.d.ts +40 -0
- package/dist/memory/ingestion/pathUtils.d.ts.map +1 -0
- package/dist/memory/ingestion/pathUtils.js +62 -0
- package/dist/memory/ingestion/pathUtils.js.map +1 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts +95 -20
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -1
- package/dist/voice-pipeline/AcousticEndpointDetector.js +110 -24
- package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -1
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +66 -15
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts.map +1 -1
- package/dist/voice-pipeline/HardCutBargeinHandler.js +65 -13
- package/dist/voice-pipeline/HardCutBargeinHandler.js.map +1 -1
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +116 -42
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -1
- package/dist/voice-pipeline/HeuristicEndpointDetector.js +159 -52
- package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -1
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +89 -24
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts.map +1 -1
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js +74 -20
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js.map +1 -1
- package/dist/voice-pipeline/VoiceInterruptError.d.ts +68 -10
- package/dist/voice-pipeline/VoiceInterruptError.d.ts.map +1 -1
- package/dist/voice-pipeline/VoiceInterruptError.js +53 -6
- package/dist/voice-pipeline/VoiceInterruptError.js.map +1 -1
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +190 -39
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -1
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js +266 -53
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -1
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +135 -43
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts.map +1 -1
- package/dist/voice-pipeline/WebSocketStreamTransport.js +109 -47
- package/dist/voice-pipeline/WebSocketStreamTransport.js.map +1 -1
- package/dist/voice-pipeline/index.d.ts +34 -1
- package/dist/voice-pipeline/index.d.ts.map +1 -1
- package/dist/voice-pipeline/index.js +41 -1
- package/dist/voice-pipeline/index.js.map +1 -1
- package/dist/voice-pipeline/types.d.ts +432 -106
- package/dist/voice-pipeline/types.d.ts.map +1 -1
- package/dist/voice-pipeline/types.js +21 -9
- package/dist/voice-pipeline/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,46 +6,105 @@
|
|
|
6
6
|
* as Float32Array audio frames; text messages are parsed as
|
|
7
7
|
* {@link ClientTextMessage} control envelopes.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* ## Wire protocol
|
|
10
|
+
*
|
|
11
|
+
* ### Binary frames (inbound)
|
|
12
|
+
* Every binary WebSocket message is interpreted as a raw Float32Array of PCM
|
|
13
|
+
* samples. The sender (browser/client) must transmit audio as a `Float32Array`
|
|
14
|
+
* view serialised to its underlying `ArrayBuffer`. The transport reconstructs
|
|
15
|
+
* the `Float32Array` from the received `Buffer`, using `byteOffset` and
|
|
16
|
+
* `byteLength` to handle sliced buffers correctly.
|
|
17
|
+
*
|
|
18
|
+
* ### Text frames (inbound)
|
|
19
|
+
* Every text WebSocket message must be valid JSON conforming to
|
|
20
|
+
* {@link ClientTextMessage}. Malformed JSON emits an `'error'` event but does
|
|
21
|
+
* not crash the transport, allowing the session to continue.
|
|
22
|
+
*
|
|
23
|
+
* ### Binary frames (outbound)
|
|
24
|
+
* {@link sendAudio} sends the raw `Buffer` from an {@link EncodedAudioChunk}
|
|
25
|
+
* (or converts a `Float32Array` to a `Buffer` for raw {@link AudioFrame}s).
|
|
26
|
+
*
|
|
27
|
+
* ### Text frames (outbound)
|
|
28
|
+
* {@link sendControl} JSON-stringifies a {@link ServerTextMessage} and sends
|
|
29
|
+
* it as a text frame.
|
|
30
|
+
*
|
|
31
|
+
* ## Reconnection behaviour
|
|
32
|
+
* This transport does NOT implement automatic reconnection. If the underlying
|
|
33
|
+
* WebSocket closes, the transport transitions to `'closed'` and emits
|
|
34
|
+
* `'disconnected'`. The consumer (typically the orchestrator) is responsible
|
|
35
|
+
* for creating a new transport and session if reconnection is desired.
|
|
36
|
+
*
|
|
37
|
+
* ## Transport-agnostic design
|
|
38
|
+
* The transport accepts any object satisfying {@link WebSocketLike}, which is
|
|
39
|
+
* a minimal subset of the `ws` package's `WebSocket` interface. This makes it
|
|
40
|
+
* trivially testable with a plain `EventEmitter` mock and compatible with
|
|
41
|
+
* browser-style WebSocket polyfills.
|
|
13
42
|
*/
|
|
14
43
|
import { EventEmitter } from 'node:events';
|
|
15
44
|
import type { IStreamTransport, AudioFrame, EncodedAudioChunk, TransportControlMessage, ServerTextMessage } from './types.js';
|
|
16
45
|
/**
|
|
17
46
|
* Subset of the WebSocket API required by {@link WebSocketStreamTransport}.
|
|
18
47
|
* Both the `ws` npm package and the browser's native WebSocket satisfy this.
|
|
48
|
+
*
|
|
49
|
+
* By depending on this minimal interface rather than the full `ws.WebSocket`
|
|
50
|
+
* type, the transport avoids a hard dependency on any specific WebSocket
|
|
51
|
+
* library and remains easily mockable in tests.
|
|
52
|
+
*
|
|
53
|
+
* @see {@link WebSocketStreamTransport} which consumes this interface.
|
|
19
54
|
*/
|
|
20
55
|
export interface WebSocketLike extends NodeJS.EventEmitter {
|
|
21
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* WebSocket ready-state constant for the OPEN state.
|
|
58
|
+
* In the `ws` package this is `1`; in browsers it is also `1`.
|
|
59
|
+
* Optional because some mock implementations may not define it.
|
|
60
|
+
*/
|
|
22
61
|
readonly OPEN?: number;
|
|
23
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* WebSocket ready-state constant for the CLOSED state.
|
|
64
|
+
* In the `ws` package this is `3`.
|
|
65
|
+
* Optional because some mock implementations may not define it.
|
|
66
|
+
*/
|
|
24
67
|
readonly CLOSED?: number;
|
|
25
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Current ready state of the socket.
|
|
70
|
+
* - `0` = CONNECTING
|
|
71
|
+
* - `1` = OPEN
|
|
72
|
+
* - `2` = CLOSING
|
|
73
|
+
* - `3` = CLOSED
|
|
74
|
+
*/
|
|
26
75
|
readonly readyState: number;
|
|
27
76
|
/**
|
|
28
77
|
* Send data over the socket.
|
|
29
78
|
*
|
|
30
|
-
* @param data
|
|
31
|
-
* @param cb
|
|
79
|
+
* @param data - Binary `Buffer` or text `string` payload.
|
|
80
|
+
* @param cb - Optional completion callback used by the `ws` library.
|
|
81
|
+
* The callback receives an optional `Error` if the send fails.
|
|
32
82
|
*/
|
|
33
83
|
send(data: Buffer | string, cb?: (err?: Error) => void): void;
|
|
34
84
|
/**
|
|
35
85
|
* Initiate a graceful close handshake.
|
|
36
86
|
*
|
|
37
|
-
* @param code
|
|
38
|
-
* @param reason
|
|
87
|
+
* @param code - Optional numeric close code (default 1000 = normal closure).
|
|
88
|
+
* @param reason - Optional human-readable reason string.
|
|
39
89
|
*/
|
|
40
90
|
close(code?: number, reason?: string): void;
|
|
41
91
|
}
|
|
42
92
|
/**
|
|
43
93
|
* Constructor options for {@link WebSocketStreamTransport}.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const config: WebSocketStreamTransportConfig = { sampleRate: 16000 };
|
|
98
|
+
* const transport = new WebSocketStreamTransport(ws, config);
|
|
99
|
+
* ```
|
|
44
100
|
*/
|
|
45
101
|
export interface WebSocketStreamTransportConfig {
|
|
46
102
|
/**
|
|
47
103
|
* Sample rate (in Hz) used to populate {@link AudioFrame.sampleRate} on
|
|
48
104
|
* inbound binary messages. Must match the rate the remote client is sending.
|
|
105
|
+
*
|
|
106
|
+
* Common values: 16000 (telephony/STT), 24000 (TTS output), 48000 (high-fidelity).
|
|
107
|
+
*
|
|
49
108
|
* @example 16000
|
|
50
109
|
*/
|
|
51
110
|
sampleRate: number;
|
|
@@ -53,39 +112,64 @@ export interface WebSocketStreamTransportConfig {
|
|
|
53
112
|
/**
|
|
54
113
|
* Bidirectional voice pipeline transport backed by a WebSocket connection.
|
|
55
114
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
115
|
+
* ## Inbound wire format
|
|
116
|
+
*
|
|
117
|
+
* | Frame type | Processing |
|
|
118
|
+
* |------------|-----------------------------------------------------------|
|
|
119
|
+
* | Binary | Decoded as `Float32Array` PCM samples, wrapped in an {@link AudioFrame}, emitted as `'audio_frame'`. |
|
|
120
|
+
* | Text | `JSON.parse()`d as {@link ClientTextMessage}, emitted as `'control'`. |
|
|
121
|
+
*
|
|
122
|
+
* ## Outbound API
|
|
123
|
+
*
|
|
124
|
+
* | Method | Behaviour |
|
|
125
|
+
* |-----------------|--------------------------------------------------------|
|
|
126
|
+
* | {@link sendAudio} | Serialises audio to a binary `Buffer` and calls `ws.send()`. |
|
|
127
|
+
* | {@link sendControl} | JSON-stringifies the message and calls `ws.send()`. |
|
|
61
128
|
*
|
|
62
|
-
*
|
|
63
|
-
* - {@link sendAudio} — serialises an {@link EncodedAudioChunk} or
|
|
64
|
-
* {@link AudioFrame} to a binary `Buffer` and calls `ws.send()`.
|
|
65
|
-
* - {@link sendControl} — JSON-stringifies a {@link TransportControlMessage}
|
|
66
|
-
* or {@link ServerTextMessage} and calls `ws.send()`.
|
|
129
|
+
* ## Lifecycle events (re-emitted on `this`)
|
|
67
130
|
*
|
|
68
|
-
* ### Lifecycle events (re-emitted on `this`)
|
|
69
131
|
* | WS event | Transport emission |
|
|
70
132
|
* |----------|--------------------|
|
|
71
133
|
* | `open` | `'connected'` |
|
|
72
134
|
* | `close` | `'disconnected'` |
|
|
73
135
|
* | `error` | `'error'` |
|
|
74
136
|
*
|
|
75
|
-
* @fires audio_frame
|
|
76
|
-
* @fires control
|
|
77
|
-
* @fires connected
|
|
78
|
-
* @fires disconnected
|
|
79
|
-
* @fires error
|
|
137
|
+
* @fires audio_frame - `(frame: AudioFrame)` for every inbound binary message.
|
|
138
|
+
* @fires control - `(msg: ClientTextMessage)` for every inbound text message.
|
|
139
|
+
* @fires connected - Socket transitioned to OPEN state.
|
|
140
|
+
* @fires disconnected - Socket has been fully closed.
|
|
141
|
+
* @fires error - Socket-level error occurred.
|
|
142
|
+
*
|
|
143
|
+
* @see {@link IStreamTransport} for the interface contract.
|
|
144
|
+
* @see {@link VoicePipelineOrchestrator} which consumes this transport.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* import WebSocket from 'ws';
|
|
149
|
+
* const ws = new WebSocket('ws://localhost:8080/voice');
|
|
150
|
+
* const transport = new WebSocketStreamTransport(ws, { sampleRate: 16000 });
|
|
151
|
+
*
|
|
152
|
+
* transport.on('audio_frame', (frame) => sttSession.pushAudio(frame));
|
|
153
|
+
* transport.on('control', (msg) => handleClientMessage(msg));
|
|
154
|
+
* ```
|
|
80
155
|
*/
|
|
81
156
|
export declare class WebSocketStreamTransport extends EventEmitter implements IStreamTransport {
|
|
82
|
-
/**
|
|
157
|
+
/**
|
|
158
|
+
* Stable UUID assigned at construction time.
|
|
159
|
+
* Used as a correlation key in logs and metrics.
|
|
160
|
+
*/
|
|
83
161
|
readonly id: string;
|
|
84
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Current connection state. Updated internally by WebSocket event handlers.
|
|
164
|
+
* Read externally via the {@link state} getter.
|
|
165
|
+
*/
|
|
85
166
|
private _state;
|
|
86
167
|
/** The underlying WebSocket connection. */
|
|
87
168
|
private readonly _ws;
|
|
88
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* Audio sample rate propagated into every decoded {@link AudioFrame}.
|
|
171
|
+
* Configured once at construction and never changed.
|
|
172
|
+
*/
|
|
89
173
|
private readonly _sampleRate;
|
|
90
174
|
/**
|
|
91
175
|
* Create a new transport wrapping an existing WebSocket connection.
|
|
@@ -95,8 +179,8 @@ export declare class WebSocketStreamTransport extends EventEmitter implements IS
|
|
|
95
179
|
* is set to `'open'`; otherwise it is set to `'connecting'` and will
|
|
96
180
|
* transition to `'open'` when the `'open'` event fires.
|
|
97
181
|
*
|
|
98
|
-
* @param ws
|
|
99
|
-
* @param config
|
|
182
|
+
* @param ws - WebSocket connection (or compatible mock).
|
|
183
|
+
* @param config - Transport-level configuration (must include sampleRate).
|
|
100
184
|
*/
|
|
101
185
|
constructor(ws: WebSocketLike, config: WebSocketStreamTransportConfig);
|
|
102
186
|
/**
|
|
@@ -107,13 +191,15 @@ export declare class WebSocketStreamTransport extends EventEmitter implements IS
|
|
|
107
191
|
/**
|
|
108
192
|
* Send a synthesised audio chunk to the remote client for playback.
|
|
109
193
|
*
|
|
110
|
-
* If
|
|
111
|
-
* property), that buffer is sent directly. If it
|
|
112
|
-
* (has a `samples` Float32Array), the samples are copied
|
|
113
|
-
* and sent.
|
|
194
|
+
* If the payload is an {@link EncodedAudioChunk} (has an `audio` Buffer
|
|
195
|
+
* property), that buffer is sent directly as a binary frame. If it is an
|
|
196
|
+
* {@link AudioFrame} (has a `samples` Float32Array), the samples are copied
|
|
197
|
+
* into a new `Buffer` and sent.
|
|
114
198
|
*
|
|
115
|
-
* @param chunk
|
|
199
|
+
* @param chunk - Encoded audio chunk or raw PCM frame to deliver.
|
|
116
200
|
* @returns Resolves once the data has been handed to the OS socket buffer.
|
|
201
|
+
*
|
|
202
|
+
* @throws {Error} If the underlying `ws.send()` fails (e.g. socket closed).
|
|
117
203
|
*/
|
|
118
204
|
sendAudio(chunk: EncodedAudioChunk | AudioFrame): Promise<void>;
|
|
119
205
|
/**
|
|
@@ -123,8 +209,10 @@ export declare class WebSocketStreamTransport extends EventEmitter implements IS
|
|
|
123
209
|
* {@link TransportControlMessage} and {@link ServerTextMessage} are accepted
|
|
124
210
|
* since they share the same serialisation path.
|
|
125
211
|
*
|
|
126
|
-
* @param msg
|
|
212
|
+
* @param msg - Server-side protocol message.
|
|
127
213
|
* @returns Resolves once the message has been handed to the OS socket buffer.
|
|
214
|
+
*
|
|
215
|
+
* @throws {Error} If the underlying `ws.send()` fails (e.g. socket closed).
|
|
128
216
|
*/
|
|
129
217
|
sendControl(msg: TransportControlMessage | ServerTextMessage): Promise<void>;
|
|
130
218
|
/**
|
|
@@ -134,14 +222,18 @@ export declare class WebSocketStreamTransport extends EventEmitter implements IS
|
|
|
134
222
|
* underlying WebSocket's `close()` method. The `'disconnected'` event will
|
|
135
223
|
* fire once the socket's `'close'` event is received.
|
|
136
224
|
*
|
|
137
|
-
* @param code
|
|
138
|
-
* @param reason
|
|
225
|
+
* @param code - Optional numeric WebSocket close code (default 1000 = normal closure).
|
|
226
|
+
* @param reason - Optional human-readable close reason.
|
|
139
227
|
*/
|
|
140
228
|
close(code?: number, reason?: string): void;
|
|
141
229
|
/**
|
|
142
|
-
* Attach listeners to the underlying WebSocket for
|
|
143
|
-
* the voice pipeline.
|
|
144
|
-
*
|
|
230
|
+
* Attach listeners to the underlying WebSocket for all events relevant to
|
|
231
|
+
* the voice pipeline.
|
|
232
|
+
*
|
|
233
|
+
* All listener state is contained here. No cleanup method is currently
|
|
234
|
+
* needed because the transport's lifetime is tied to the socket's --
|
|
235
|
+
* when the socket closes, the transport transitions to `'closed'` and
|
|
236
|
+
* no further events are expected.
|
|
145
237
|
*/
|
|
146
238
|
private _attachWsListeners;
|
|
147
239
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketStreamTransport.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/WebSocketStreamTransport.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"WebSocketStreamTransport.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/WebSocketStreamTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EAEvB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAc,SAAQ,MAAM,CAAC,YAAY;IACxD;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IAE9D;;;;;OAKG;IACH,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAMD;;;;;;;;GAQG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,wBAAyB,SAAQ,YAAa,YAAW,gBAAgB;IAKpF;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAA+C;IAM7D,2CAA2C;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IAEpC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAMrC;;;;;;;;;;OAUG;gBACS,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,8BAA8B;IAmBrE;;;OAGG;IACH,IAAI,KAAK,IAAI,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAExD;IAED;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B/D;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,GAAG,EAAE,uBAAuB,GAAG,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5E;;;;;;;;;OASG;IACH,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAS3C;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;CAoE3B"}
|
|
@@ -6,10 +6,39 @@
|
|
|
6
6
|
* as Float32Array audio frames; text messages are parsed as
|
|
7
7
|
* {@link ClientTextMessage} control envelopes.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* ## Wire protocol
|
|
10
|
+
*
|
|
11
|
+
* ### Binary frames (inbound)
|
|
12
|
+
* Every binary WebSocket message is interpreted as a raw Float32Array of PCM
|
|
13
|
+
* samples. The sender (browser/client) must transmit audio as a `Float32Array`
|
|
14
|
+
* view serialised to its underlying `ArrayBuffer`. The transport reconstructs
|
|
15
|
+
* the `Float32Array` from the received `Buffer`, using `byteOffset` and
|
|
16
|
+
* `byteLength` to handle sliced buffers correctly.
|
|
17
|
+
*
|
|
18
|
+
* ### Text frames (inbound)
|
|
19
|
+
* Every text WebSocket message must be valid JSON conforming to
|
|
20
|
+
* {@link ClientTextMessage}. Malformed JSON emits an `'error'` event but does
|
|
21
|
+
* not crash the transport, allowing the session to continue.
|
|
22
|
+
*
|
|
23
|
+
* ### Binary frames (outbound)
|
|
24
|
+
* {@link sendAudio} sends the raw `Buffer` from an {@link EncodedAudioChunk}
|
|
25
|
+
* (or converts a `Float32Array` to a `Buffer` for raw {@link AudioFrame}s).
|
|
26
|
+
*
|
|
27
|
+
* ### Text frames (outbound)
|
|
28
|
+
* {@link sendControl} JSON-stringifies a {@link ServerTextMessage} and sends
|
|
29
|
+
* it as a text frame.
|
|
30
|
+
*
|
|
31
|
+
* ## Reconnection behaviour
|
|
32
|
+
* This transport does NOT implement automatic reconnection. If the underlying
|
|
33
|
+
* WebSocket closes, the transport transitions to `'closed'` and emits
|
|
34
|
+
* `'disconnected'`. The consumer (typically the orchestrator) is responsible
|
|
35
|
+
* for creating a new transport and session if reconnection is desired.
|
|
36
|
+
*
|
|
37
|
+
* ## Transport-agnostic design
|
|
38
|
+
* The transport accepts any object satisfying {@link WebSocketLike}, which is
|
|
39
|
+
* a minimal subset of the `ws` package's `WebSocket` interface. This makes it
|
|
40
|
+
* trivially testable with a plain `EventEmitter` mock and compatible with
|
|
41
|
+
* browser-style WebSocket polyfills.
|
|
13
42
|
*/
|
|
14
43
|
import { EventEmitter } from 'node:events';
|
|
15
44
|
import { randomUUID } from 'node:crypto';
|
|
@@ -19,30 +48,46 @@ import { randomUUID } from 'node:crypto';
|
|
|
19
48
|
/**
|
|
20
49
|
* Bidirectional voice pipeline transport backed by a WebSocket connection.
|
|
21
50
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
51
|
+
* ## Inbound wire format
|
|
52
|
+
*
|
|
53
|
+
* | Frame type | Processing |
|
|
54
|
+
* |------------|-----------------------------------------------------------|
|
|
55
|
+
* | Binary | Decoded as `Float32Array` PCM samples, wrapped in an {@link AudioFrame}, emitted as `'audio_frame'`. |
|
|
56
|
+
* | Text | `JSON.parse()`d as {@link ClientTextMessage}, emitted as `'control'`. |
|
|
57
|
+
*
|
|
58
|
+
* ## Outbound API
|
|
27
59
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
60
|
+
* | Method | Behaviour |
|
|
61
|
+
* |-----------------|--------------------------------------------------------|
|
|
62
|
+
* | {@link sendAudio} | Serialises audio to a binary `Buffer` and calls `ws.send()`. |
|
|
63
|
+
* | {@link sendControl} | JSON-stringifies the message and calls `ws.send()`. |
|
|
64
|
+
*
|
|
65
|
+
* ## Lifecycle events (re-emitted on `this`)
|
|
33
66
|
*
|
|
34
|
-
* ### Lifecycle events (re-emitted on `this`)
|
|
35
67
|
* | WS event | Transport emission |
|
|
36
68
|
* |----------|--------------------|
|
|
37
69
|
* | `open` | `'connected'` |
|
|
38
70
|
* | `close` | `'disconnected'` |
|
|
39
71
|
* | `error` | `'error'` |
|
|
40
72
|
*
|
|
41
|
-
* @fires audio_frame
|
|
42
|
-
* @fires control
|
|
43
|
-
* @fires connected
|
|
44
|
-
* @fires disconnected
|
|
45
|
-
* @fires error
|
|
73
|
+
* @fires audio_frame - `(frame: AudioFrame)` for every inbound binary message.
|
|
74
|
+
* @fires control - `(msg: ClientTextMessage)` for every inbound text message.
|
|
75
|
+
* @fires connected - Socket transitioned to OPEN state.
|
|
76
|
+
* @fires disconnected - Socket has been fully closed.
|
|
77
|
+
* @fires error - Socket-level error occurred.
|
|
78
|
+
*
|
|
79
|
+
* @see {@link IStreamTransport} for the interface contract.
|
|
80
|
+
* @see {@link VoicePipelineOrchestrator} which consumes this transport.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* import WebSocket from 'ws';
|
|
85
|
+
* const ws = new WebSocket('ws://localhost:8080/voice');
|
|
86
|
+
* const transport = new WebSocketStreamTransport(ws, { sampleRate: 16000 });
|
|
87
|
+
*
|
|
88
|
+
* transport.on('audio_frame', (frame) => sttSession.pushAudio(frame));
|
|
89
|
+
* transport.on('control', (msg) => handleClientMessage(msg));
|
|
90
|
+
* ```
|
|
46
91
|
*/
|
|
47
92
|
export class WebSocketStreamTransport extends EventEmitter {
|
|
48
93
|
// -------------------------------------------------------------------------
|
|
@@ -56,8 +101,8 @@ export class WebSocketStreamTransport extends EventEmitter {
|
|
|
56
101
|
* is set to `'open'`; otherwise it is set to `'connecting'` and will
|
|
57
102
|
* transition to `'open'` when the `'open'` event fires.
|
|
58
103
|
*
|
|
59
|
-
* @param ws
|
|
60
|
-
* @param config
|
|
104
|
+
* @param ws - WebSocket connection (or compatible mock).
|
|
105
|
+
* @param config - Transport-level configuration (must include sampleRate).
|
|
61
106
|
*/
|
|
62
107
|
constructor(ws, config) {
|
|
63
108
|
super();
|
|
@@ -65,13 +110,14 @@ export class WebSocketStreamTransport extends EventEmitter {
|
|
|
65
110
|
this._sampleRate = config.sampleRate;
|
|
66
111
|
this.id = randomUUID();
|
|
67
112
|
// Determine initial state from the socket's current ready-state value.
|
|
68
|
-
// The `ws` package uses numeric constants
|
|
113
|
+
// The `ws` package uses numeric constants: OPEN = 1, CLOSED = 3.
|
|
114
|
+
// We default to 1 if the OPEN constant is not defined (e.g. in mocks).
|
|
69
115
|
const OPEN_STATE = ws.OPEN ?? 1;
|
|
70
116
|
this._state = ws.readyState === OPEN_STATE ? 'open' : 'connecting';
|
|
71
117
|
this._attachWsListeners();
|
|
72
118
|
}
|
|
73
119
|
// -------------------------------------------------------------------------
|
|
74
|
-
// IStreamTransport
|
|
120
|
+
// IStreamTransport -- public surface
|
|
75
121
|
// -------------------------------------------------------------------------
|
|
76
122
|
/**
|
|
77
123
|
* Current connection state of the underlying WebSocket.
|
|
@@ -83,23 +129,27 @@ export class WebSocketStreamTransport extends EventEmitter {
|
|
|
83
129
|
/**
|
|
84
130
|
* Send a synthesised audio chunk to the remote client for playback.
|
|
85
131
|
*
|
|
86
|
-
* If
|
|
87
|
-
* property), that buffer is sent directly. If it
|
|
88
|
-
* (has a `samples` Float32Array), the samples are copied
|
|
89
|
-
* and sent.
|
|
132
|
+
* If the payload is an {@link EncodedAudioChunk} (has an `audio` Buffer
|
|
133
|
+
* property), that buffer is sent directly as a binary frame. If it is an
|
|
134
|
+
* {@link AudioFrame} (has a `samples` Float32Array), the samples are copied
|
|
135
|
+
* into a new `Buffer` and sent.
|
|
90
136
|
*
|
|
91
|
-
* @param chunk
|
|
137
|
+
* @param chunk - Encoded audio chunk or raw PCM frame to deliver.
|
|
92
138
|
* @returns Resolves once the data has been handed to the OS socket buffer.
|
|
139
|
+
*
|
|
140
|
+
* @throws {Error} If the underlying `ws.send()` fails (e.g. socket closed).
|
|
93
141
|
*/
|
|
94
142
|
sendAudio(chunk) {
|
|
95
143
|
return new Promise((resolve, reject) => {
|
|
96
144
|
let binary;
|
|
97
145
|
if ('audio' in chunk) {
|
|
98
|
-
// EncodedAudioChunk
|
|
146
|
+
// EncodedAudioChunk path: the audio property is already a Buffer
|
|
99
147
|
binary = chunk.audio;
|
|
100
148
|
}
|
|
101
149
|
else {
|
|
102
|
-
// AudioFrame
|
|
150
|
+
// AudioFrame path: convert Float32Array samples to a raw byte Buffer.
|
|
151
|
+
// We must use byteOffset and byteLength because the Float32Array may
|
|
152
|
+
// be a view into a larger SharedArrayBuffer or sliced Buffer.
|
|
103
153
|
const frame = chunk;
|
|
104
154
|
binary = Buffer.from(frame.samples.buffer, frame.samples.byteOffset, frame.samples.byteLength);
|
|
105
155
|
}
|
|
@@ -118,8 +168,10 @@ export class WebSocketStreamTransport extends EventEmitter {
|
|
|
118
168
|
* {@link TransportControlMessage} and {@link ServerTextMessage} are accepted
|
|
119
169
|
* since they share the same serialisation path.
|
|
120
170
|
*
|
|
121
|
-
* @param msg
|
|
171
|
+
* @param msg - Server-side protocol message.
|
|
122
172
|
* @returns Resolves once the message has been handed to the OS socket buffer.
|
|
173
|
+
*
|
|
174
|
+
* @throws {Error} If the underlying `ws.send()` fails (e.g. socket closed).
|
|
123
175
|
*/
|
|
124
176
|
sendControl(msg) {
|
|
125
177
|
return new Promise((resolve, reject) => {
|
|
@@ -138,8 +190,8 @@ export class WebSocketStreamTransport extends EventEmitter {
|
|
|
138
190
|
* underlying WebSocket's `close()` method. The `'disconnected'` event will
|
|
139
191
|
* fire once the socket's `'close'` event is received.
|
|
140
192
|
*
|
|
141
|
-
* @param code
|
|
142
|
-
* @param reason
|
|
193
|
+
* @param code - Optional numeric WebSocket close code (default 1000 = normal closure).
|
|
194
|
+
* @param reason - Optional human-readable close reason.
|
|
143
195
|
*/
|
|
144
196
|
close(code, reason) {
|
|
145
197
|
this._state = 'closing';
|
|
@@ -149,36 +201,45 @@ export class WebSocketStreamTransport extends EventEmitter {
|
|
|
149
201
|
// Private helpers
|
|
150
202
|
// -------------------------------------------------------------------------
|
|
151
203
|
/**
|
|
152
|
-
* Attach listeners to the underlying WebSocket for
|
|
153
|
-
* the voice pipeline.
|
|
154
|
-
*
|
|
204
|
+
* Attach listeners to the underlying WebSocket for all events relevant to
|
|
205
|
+
* the voice pipeline.
|
|
206
|
+
*
|
|
207
|
+
* All listener state is contained here. No cleanup method is currently
|
|
208
|
+
* needed because the transport's lifetime is tied to the socket's --
|
|
209
|
+
* when the socket closes, the transport transitions to `'closed'` and
|
|
210
|
+
* no further events are expected.
|
|
155
211
|
*/
|
|
156
212
|
_attachWsListeners() {
|
|
157
|
-
// `message`
|
|
213
|
+
// `message` -- inbound data from the remote peer
|
|
158
214
|
this._ws.on('message', (data) => {
|
|
159
215
|
if (typeof data === 'string') {
|
|
160
|
-
// Text frame
|
|
216
|
+
// Text frame: parse as ClientTextMessage JSON and propagate as 'control'.
|
|
217
|
+
// Malformed JSON emits an error but does NOT crash the transport,
|
|
218
|
+
// allowing the session to recover from a single bad message.
|
|
161
219
|
try {
|
|
162
220
|
const msg = JSON.parse(data);
|
|
163
221
|
this.emit('control', msg);
|
|
164
222
|
}
|
|
165
223
|
catch (err) {
|
|
166
|
-
|
|
167
|
-
this.emit('error', new Error(`WebSocketStreamTransport: failed to parse text message: ${String(err)}`));
|
|
224
|
+
this.emit('error', new Error(`WebSocketStreamTransport: failed to parse inbound text message as JSON: ${String(err)}`));
|
|
168
225
|
}
|
|
169
226
|
}
|
|
170
227
|
else {
|
|
171
|
-
// Binary frame
|
|
228
|
+
// Binary frame: interpret bytes as a Float32Array PCM sample buffer.
|
|
229
|
+
// The `ws` package delivers binary as a Node.js Buffer; browser-style
|
|
230
|
+
// WebSocket polyfills may deliver an ArrayBuffer instead.
|
|
172
231
|
let buffer;
|
|
173
232
|
if (Buffer.isBuffer(data)) {
|
|
174
233
|
buffer = data;
|
|
175
234
|
}
|
|
176
235
|
else {
|
|
177
|
-
// ArrayBuffer (browser-style)
|
|
236
|
+
// ArrayBuffer (browser-style) -- wrap in a Node.js Buffer
|
|
178
237
|
buffer = Buffer.from(data);
|
|
179
238
|
}
|
|
180
239
|
// Create a Float32Array view over the underlying ArrayBuffer.
|
|
181
|
-
// We use byteOffset
|
|
240
|
+
// We use byteOffset and byteLength to handle sliced Buffers correctly,
|
|
241
|
+
// since Buffer.from() may return a view into Node's internal pool
|
|
242
|
+
// rather than a fresh allocation.
|
|
182
243
|
const samples = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
183
244
|
const frame = {
|
|
184
245
|
samples,
|
|
@@ -188,17 +249,18 @@ export class WebSocketStreamTransport extends EventEmitter {
|
|
|
188
249
|
this.emit('audio_frame', frame);
|
|
189
250
|
}
|
|
190
251
|
});
|
|
191
|
-
// `open`
|
|
252
|
+
// `open` -- socket handshake complete (fires for late-open connections)
|
|
192
253
|
this._ws.on('open', () => {
|
|
193
254
|
this._state = 'open';
|
|
194
255
|
this.emit('connected');
|
|
195
256
|
});
|
|
196
|
-
// `close`
|
|
257
|
+
// `close` -- socket has been fully closed (either side initiated)
|
|
197
258
|
this._ws.on('close', () => {
|
|
198
259
|
this._state = 'closed';
|
|
199
260
|
this.emit('disconnected');
|
|
200
261
|
});
|
|
201
|
-
// `error`
|
|
262
|
+
// `error` -- transport-level socket error. Re-emitted verbatim so the
|
|
263
|
+
// orchestrator can log it and decide whether to tear down the session.
|
|
202
264
|
this._ws.on('error', (err) => {
|
|
203
265
|
this.emit('error', err);
|
|
204
266
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketStreamTransport.js","sourceRoot":"","sources":["../../src/voice-pipeline/WebSocketStreamTransport.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"WebSocketStreamTransport.js","sourceRoot":"","sources":["../../src/voice-pipeline/WebSocketStreamTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @module @framers/agentos/voice-pipeline
|
|
3
|
+
*
|
|
2
4
|
* Barrel exports for the AgentOS streaming voice pipeline.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
6
|
+
* This module provides all the building blocks needed to assemble a real-time
|
|
7
|
+
* voice conversation system:
|
|
8
|
+
*
|
|
9
|
+
* - **Types** -- All interfaces and type aliases defining the pipeline's contracts
|
|
10
|
+
* ({@link AudioFrame}, {@link IStreamTransport}, {@link IEndpointDetector}, etc.).
|
|
11
|
+
*
|
|
12
|
+
* - **Orchestrator** -- {@link VoicePipelineOrchestrator} is the central state machine
|
|
13
|
+
* that wires transport, STT, endpoint detection, TTS, and barge-in handling into
|
|
14
|
+
* a coordinated conversation loop.
|
|
15
|
+
*
|
|
16
|
+
* - **Endpoint Detectors** -- Two strategies for detecting turn boundaries:
|
|
17
|
+
* - {@link HeuristicEndpointDetector}: Rule-based (punctuation + silence timeout).
|
|
18
|
+
* - {@link AcousticEndpointDetector}: Purely acoustic (silence-only, no transcript analysis).
|
|
19
|
+
*
|
|
20
|
+
* - **Barge-in Handlers** -- Two strategies for handling user interruptions:
|
|
21
|
+
* - {@link HardCutBargeinHandler}: Immediate stop above a speech duration threshold.
|
|
22
|
+
* - {@link SoftFadeBargeinHandler}: Three-tier (ignore/pause/cancel) with configurable fade.
|
|
23
|
+
*
|
|
24
|
+
* - **Transport** -- {@link WebSocketStreamTransport}: WebSocket-based bidirectional
|
|
25
|
+
* audio/text transport implementing {@link IStreamTransport}.
|
|
26
|
+
*
|
|
27
|
+
* - **Error** -- {@link VoiceInterruptError}: Typed error for barge-in interruptions.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import {
|
|
32
|
+
* VoicePipelineOrchestrator,
|
|
33
|
+
* HeuristicEndpointDetector,
|
|
34
|
+
* HardCutBargeinHandler,
|
|
35
|
+
* WebSocketStreamTransport,
|
|
36
|
+
* } from '../voice-pipeline';
|
|
37
|
+
* ```
|
|
5
38
|
*/
|
|
6
39
|
export * from './types.js';
|
|
7
40
|
export { HardCutBargeinHandler } from './HardCutBargeinHandler.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAG3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
|