@framers/agentos 0.1.75 → 0.1.76
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/README.md +139 -34
- package/dist/core/agency/AgentCommunicationBus.d.ts +1 -0
- package/dist/core/agency/AgentCommunicationBus.d.ts.map +1 -1
- package/dist/core/agency/AgentCommunicationBus.js +62 -8
- package/dist/core/agency/AgentCommunicationBus.js.map +1 -1
- package/dist/core/agency/IAgentCommunicationBus.d.ts +1 -1
- package/dist/core/agency/IAgentCommunicationBus.d.ts.map +1 -1
- package/dist/orchestration/runtime/LoopController.d.ts +10 -10
- package/dist/orchestration/runtime/LoopController.d.ts.map +1 -1
- package/dist/orchestration/runtime/LoopController.js +1 -1
- package/dist/orchestration/runtime/LoopController.js.map +1 -1
- package/dist/orchestration/runtime/index.d.ts +1 -1
- package/dist/orchestration/runtime/index.d.ts.map +1 -1
- package/dist/orchestration/runtime/index.js.map +1 -1
- package/dist/speech/FallbackProxy.d.ts +104 -0
- package/dist/speech/FallbackProxy.d.ts.map +1 -0
- package/dist/speech/FallbackProxy.js +151 -0
- package/dist/speech/FallbackProxy.js.map +1 -0
- package/dist/speech/SpeechProviderResolver.d.ts +103 -0
- package/dist/speech/SpeechProviderResolver.d.ts.map +1 -0
- package/dist/speech/SpeechProviderResolver.js +256 -0
- package/dist/speech/SpeechProviderResolver.js.map +1 -0
- package/dist/speech/SpeechRuntime.d.ts +23 -1
- package/dist/speech/SpeechRuntime.d.ts.map +1 -1
- package/dist/speech/SpeechRuntime.js +82 -8
- package/dist/speech/SpeechRuntime.js.map +1 -1
- package/dist/speech/index.d.ts +6 -0
- package/dist/speech/index.d.ts.map +1 -1
- package/dist/speech/index.js +6 -0
- package/dist/speech/index.js.map +1 -1
- package/dist/speech/providerCatalog.d.ts.map +1 -1
- package/dist/speech/providerCatalog.js +15 -1
- package/dist/speech/providerCatalog.js.map +1 -1
- package/dist/speech/providers/AssemblyAISTTProvider.d.ts +49 -0
- package/dist/speech/providers/AssemblyAISTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/AssemblyAISTTProvider.js +151 -0
- package/dist/speech/providers/AssemblyAISTTProvider.js.map +1 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.d.ts +48 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.js +90 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.js.map +1 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.d.ts +60 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.d.ts.map +1 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.js +127 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.js.map +1 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts +55 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.js +102 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.js.map +1 -0
- package/dist/speech/types.d.ts +35 -0
- package/dist/speech/types.d.ts.map +1 -1
- package/dist/voice/CallManager.d.ts +1 -1
- package/dist/voice/CallManager.d.ts.map +1 -1
- package/dist/voice/CallManager.js +9 -0
- package/dist/voice/CallManager.js.map +1 -1
- package/dist/voice/MediaStreamParser.d.ts +83 -0
- package/dist/voice/MediaStreamParser.d.ts.map +1 -0
- package/dist/voice/MediaStreamParser.js +2 -0
- package/dist/voice/MediaStreamParser.js.map +1 -0
- package/dist/voice/TelephonyStreamTransport.d.ts +112 -0
- package/dist/voice/TelephonyStreamTransport.d.ts.map +1 -0
- package/dist/voice/TelephonyStreamTransport.js +208 -0
- package/dist/voice/TelephonyStreamTransport.js.map +1 -0
- package/dist/voice/index.d.ts +10 -0
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +11 -0
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts +43 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.js +92 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.js.map +1 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts +51 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.js +103 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.js.map +1 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts +50 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.js +144 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.js.map +1 -0
- package/dist/voice/providers/plivo.d.ts +77 -0
- package/dist/voice/providers/plivo.d.ts.map +1 -0
- package/dist/voice/providers/plivo.js +180 -0
- package/dist/voice/providers/plivo.js.map +1 -0
- package/dist/voice/providers/telnyx.d.ts +93 -0
- package/dist/voice/providers/telnyx.d.ts.map +1 -0
- package/dist/voice/providers/telnyx.js +193 -0
- package/dist/voice/providers/telnyx.js.map +1 -0
- package/dist/voice/providers/twilio.d.ts +79 -0
- package/dist/voice/providers/twilio.d.ts.map +1 -0
- package/dist/voice/providers/twilio.js +191 -0
- package/dist/voice/providers/twilio.js.map +1 -0
- package/dist/voice/twiml.d.ts +69 -0
- package/dist/voice/twiml.d.ts.map +1 -0
- package/dist/voice/twiml.js +92 -0
- package/dist/voice/twiml.js.map +1 -0
- package/dist/voice/types.d.ts +9 -1
- package/dist/voice/types.d.ts.map +1 -1
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts +90 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.js +123 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +67 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts.map +1 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.js +55 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.js.map +1 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +128 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.js +240 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +96 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts.map +1 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js +69 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js.map +1 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +122 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js +317 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +148 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts.map +1 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.js +207 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.js.map +1 -0
- package/dist/voice-pipeline/index.d.ts +13 -0
- package/dist/voice-pipeline/index.d.ts.map +1 -0
- package/dist/voice-pipeline/index.js +13 -0
- package/dist/voice-pipeline/index.js.map +1 -0
- package/dist/voice-pipeline/types.d.ts +905 -0
- package/dist/voice-pipeline/types.d.ts.map +1 -0
- package/dist/voice-pipeline/types.js +23 -0
- package/dist/voice-pipeline/types.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/WebSocketStreamTransport
|
|
3
|
+
*
|
|
4
|
+
* A concrete {@link IStreamTransport} implementation that wraps a WebSocket
|
|
5
|
+
* connection (or any WebSocket-compatible object). Binary messages are decoded
|
|
6
|
+
* as Float32Array audio frames; text messages are parsed as
|
|
7
|
+
* {@link ClientTextMessage} control envelopes.
|
|
8
|
+
*
|
|
9
|
+
* The transport is intentionally transport-agnostic: it accepts any object that
|
|
10
|
+
* exposes the `ws` package's `WebSocket` interface (readyState, send, close, and
|
|
11
|
+
* the standard event emitter surface). This makes it trivially testable with a
|
|
12
|
+
* plain `EventEmitter` mock.
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'node:events';
|
|
15
|
+
import type { IStreamTransport, AudioFrame, EncodedAudioChunk, TransportControlMessage, ServerTextMessage } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Subset of the WebSocket API required by {@link WebSocketStreamTransport}.
|
|
18
|
+
* Both the `ws` npm package and the browser's native WebSocket satisfy this.
|
|
19
|
+
*/
|
|
20
|
+
export interface WebSocketLike extends NodeJS.EventEmitter {
|
|
21
|
+
/** WebSocket ready-state constant for the OPEN state (= 1). */
|
|
22
|
+
readonly OPEN?: number;
|
|
23
|
+
/** WebSocket ready-state constant for the CLOSED state (= 3). */
|
|
24
|
+
readonly CLOSED?: number;
|
|
25
|
+
/** Current ready state of the socket. */
|
|
26
|
+
readonly readyState: number;
|
|
27
|
+
/**
|
|
28
|
+
* Send data over the socket.
|
|
29
|
+
*
|
|
30
|
+
* @param data — Binary `Buffer` or text `string` payload.
|
|
31
|
+
* @param cb — Optional completion callback used by the `ws` library.
|
|
32
|
+
*/
|
|
33
|
+
send(data: Buffer | string, cb?: (err?: Error) => void): void;
|
|
34
|
+
/**
|
|
35
|
+
* Initiate a graceful close handshake.
|
|
36
|
+
*
|
|
37
|
+
* @param code — Optional numeric close code (default 1000).
|
|
38
|
+
* @param reason — Optional human-readable reason string.
|
|
39
|
+
*/
|
|
40
|
+
close(code?: number, reason?: string): void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Constructor options for {@link WebSocketStreamTransport}.
|
|
44
|
+
*/
|
|
45
|
+
export interface WebSocketStreamTransportConfig {
|
|
46
|
+
/**
|
|
47
|
+
* Sample rate (in Hz) used to populate {@link AudioFrame.sampleRate} on
|
|
48
|
+
* inbound binary messages. Must match the rate the remote client is sending.
|
|
49
|
+
* @example 16000
|
|
50
|
+
*/
|
|
51
|
+
sampleRate: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Bidirectional voice pipeline transport backed by a WebSocket connection.
|
|
55
|
+
*
|
|
56
|
+
* ### Inbound wire format
|
|
57
|
+
* - **Binary frame** → decoded as a `Float32Array` view, wrapped in an
|
|
58
|
+
* {@link AudioFrame}, and re-emitted as `'audio_frame'`.
|
|
59
|
+
* - **Text frame** → `JSON.parse()`d and re-emitted as `'control'` carrying
|
|
60
|
+
* the raw {@link ClientTextMessage} object.
|
|
61
|
+
*
|
|
62
|
+
* ### Outbound API
|
|
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()`.
|
|
67
|
+
*
|
|
68
|
+
* ### Lifecycle events (re-emitted on `this`)
|
|
69
|
+
* | WS event | Transport emission |
|
|
70
|
+
* |----------|--------------------|
|
|
71
|
+
* | `open` | `'connected'` |
|
|
72
|
+
* | `close` | `'disconnected'` |
|
|
73
|
+
* | `error` | `'error'` |
|
|
74
|
+
*
|
|
75
|
+
* @fires audio_frame — `(frame: AudioFrame)` for every inbound binary message.
|
|
76
|
+
* @fires control — `(msg: ClientTextMessage)` for every inbound text message.
|
|
77
|
+
* @fires connected — Socket transitioned to OPEN state.
|
|
78
|
+
* @fires disconnected — Socket has been fully closed.
|
|
79
|
+
* @fires error — Socket-level error occurred.
|
|
80
|
+
*/
|
|
81
|
+
export declare class WebSocketStreamTransport extends EventEmitter implements IStreamTransport {
|
|
82
|
+
/** Stable UUID assigned at construction time. */
|
|
83
|
+
readonly id: string;
|
|
84
|
+
/** Current connection state. Updated in response to WebSocket events. */
|
|
85
|
+
private _state;
|
|
86
|
+
/** The underlying WebSocket connection. */
|
|
87
|
+
private readonly _ws;
|
|
88
|
+
/** Audio sample rate propagated into every decoded {@link AudioFrame}. */
|
|
89
|
+
private readonly _sampleRate;
|
|
90
|
+
/**
|
|
91
|
+
* Create a new transport wrapping an existing WebSocket connection.
|
|
92
|
+
*
|
|
93
|
+
* The constructor inspects `ws.readyState` to determine the initial
|
|
94
|
+
* {@link state}: if the socket is already OPEN (readyState === 1) the state
|
|
95
|
+
* is set to `'open'`; otherwise it is set to `'connecting'` and will
|
|
96
|
+
* transition to `'open'` when the `'open'` event fires.
|
|
97
|
+
*
|
|
98
|
+
* @param ws — WebSocket connection (or compatible mock).
|
|
99
|
+
* @param config — Transport-level configuration.
|
|
100
|
+
*/
|
|
101
|
+
constructor(ws: WebSocketLike, config: WebSocketStreamTransportConfig);
|
|
102
|
+
/**
|
|
103
|
+
* Current connection state of the underlying WebSocket.
|
|
104
|
+
* Read-only from the outside; updated internally by WS event handlers.
|
|
105
|
+
*/
|
|
106
|
+
get state(): 'connecting' | 'open' | 'closing' | 'closed';
|
|
107
|
+
/**
|
|
108
|
+
* Send a synthesised audio chunk to the remote client for playback.
|
|
109
|
+
*
|
|
110
|
+
* If `chunk` carries an {@link EncodedAudioChunk} (has an `audio` Buffer
|
|
111
|
+
* property), that buffer is sent directly. If it carries an {@link AudioFrame}
|
|
112
|
+
* (has a `samples` Float32Array), the samples are copied into a new `Buffer`
|
|
113
|
+
* and sent.
|
|
114
|
+
*
|
|
115
|
+
* @param chunk — Encoded audio chunk or raw PCM frame to deliver.
|
|
116
|
+
* @returns Resolves once the data has been handed to the OS socket buffer.
|
|
117
|
+
*/
|
|
118
|
+
sendAudio(chunk: EncodedAudioChunk | AudioFrame): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Send a JSON control message to the remote client.
|
|
121
|
+
*
|
|
122
|
+
* The message is JSON-stringified before transmission. Both
|
|
123
|
+
* {@link TransportControlMessage} and {@link ServerTextMessage} are accepted
|
|
124
|
+
* since they share the same serialisation path.
|
|
125
|
+
*
|
|
126
|
+
* @param msg — Server-side protocol message.
|
|
127
|
+
* @returns Resolves once the message has been handed to the OS socket buffer.
|
|
128
|
+
*/
|
|
129
|
+
sendControl(msg: TransportControlMessage | ServerTextMessage): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Initiate a graceful close of the transport.
|
|
132
|
+
*
|
|
133
|
+
* Sets internal state to `'closing'` immediately, then delegates to the
|
|
134
|
+
* underlying WebSocket's `close()` method. The `'disconnected'` event will
|
|
135
|
+
* fire once the socket's `'close'` event is received.
|
|
136
|
+
*
|
|
137
|
+
* @param code — Optional numeric WebSocket close code (default 1000).
|
|
138
|
+
* @param reason — Optional human-readable close reason.
|
|
139
|
+
*/
|
|
140
|
+
close(code?: number, reason?: string): void;
|
|
141
|
+
/**
|
|
142
|
+
* Attach listeners to the underlying WebSocket for the events that matter to
|
|
143
|
+
* the voice pipeline. All listener state is contained here; no cleanup method
|
|
144
|
+
* is currently needed since the transport's lifetime is tied to the socket's.
|
|
145
|
+
*/
|
|
146
|
+
private _attachWsListeners;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=WebSocketStreamTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketStreamTransport.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/WebSocketStreamTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;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;AAOpB;;;GAGG;AACH,MAAM,WAAW,aAAc,SAAQ,MAAM,CAAC,YAAY;IACxD,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAMD;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,wBAAyB,SAAQ,YAAa,YAAW,gBAAgB;IAKpF,iDAAiD;IACjD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,yEAAyE;IACzE,OAAO,CAAC,MAAM,CAA+C;IAM7D,2CAA2C;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IAEpC,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAMrC;;;;;;;;;;OAUG;gBACS,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,8BAA8B;IAkBrE;;;OAGG;IACH,IAAI,KAAK,IAAI,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAExD;IAED;;;;;;;;;;OAUG;IACH,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB/D;;;;;;;;;OASG;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;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;CAyD3B"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/WebSocketStreamTransport
|
|
3
|
+
*
|
|
4
|
+
* A concrete {@link IStreamTransport} implementation that wraps a WebSocket
|
|
5
|
+
* connection (or any WebSocket-compatible object). Binary messages are decoded
|
|
6
|
+
* as Float32Array audio frames; text messages are parsed as
|
|
7
|
+
* {@link ClientTextMessage} control envelopes.
|
|
8
|
+
*
|
|
9
|
+
* The transport is intentionally transport-agnostic: it accepts any object that
|
|
10
|
+
* exposes the `ws` package's `WebSocket` interface (readyState, send, close, and
|
|
11
|
+
* the standard event emitter surface). This makes it trivially testable with a
|
|
12
|
+
* plain `EventEmitter` mock.
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'node:events';
|
|
15
|
+
import { randomUUID } from 'node:crypto';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Implementation
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Bidirectional voice pipeline transport backed by a WebSocket connection.
|
|
21
|
+
*
|
|
22
|
+
* ### Inbound wire format
|
|
23
|
+
* - **Binary frame** → decoded as a `Float32Array` view, wrapped in an
|
|
24
|
+
* {@link AudioFrame}, and re-emitted as `'audio_frame'`.
|
|
25
|
+
* - **Text frame** → `JSON.parse()`d and re-emitted as `'control'` carrying
|
|
26
|
+
* the raw {@link ClientTextMessage} object.
|
|
27
|
+
*
|
|
28
|
+
* ### Outbound API
|
|
29
|
+
* - {@link sendAudio} — serialises an {@link EncodedAudioChunk} or
|
|
30
|
+
* {@link AudioFrame} to a binary `Buffer` and calls `ws.send()`.
|
|
31
|
+
* - {@link sendControl} — JSON-stringifies a {@link TransportControlMessage}
|
|
32
|
+
* or {@link ServerTextMessage} and calls `ws.send()`.
|
|
33
|
+
*
|
|
34
|
+
* ### Lifecycle events (re-emitted on `this`)
|
|
35
|
+
* | WS event | Transport emission |
|
|
36
|
+
* |----------|--------------------|
|
|
37
|
+
* | `open` | `'connected'` |
|
|
38
|
+
* | `close` | `'disconnected'` |
|
|
39
|
+
* | `error` | `'error'` |
|
|
40
|
+
*
|
|
41
|
+
* @fires audio_frame — `(frame: AudioFrame)` for every inbound binary message.
|
|
42
|
+
* @fires control — `(msg: ClientTextMessage)` for every inbound text message.
|
|
43
|
+
* @fires connected — Socket transitioned to OPEN state.
|
|
44
|
+
* @fires disconnected — Socket has been fully closed.
|
|
45
|
+
* @fires error — Socket-level error occurred.
|
|
46
|
+
*/
|
|
47
|
+
export class WebSocketStreamTransport extends EventEmitter {
|
|
48
|
+
// -------------------------------------------------------------------------
|
|
49
|
+
// Constructor
|
|
50
|
+
// -------------------------------------------------------------------------
|
|
51
|
+
/**
|
|
52
|
+
* Create a new transport wrapping an existing WebSocket connection.
|
|
53
|
+
*
|
|
54
|
+
* The constructor inspects `ws.readyState` to determine the initial
|
|
55
|
+
* {@link state}: if the socket is already OPEN (readyState === 1) the state
|
|
56
|
+
* is set to `'open'`; otherwise it is set to `'connecting'` and will
|
|
57
|
+
* transition to `'open'` when the `'open'` event fires.
|
|
58
|
+
*
|
|
59
|
+
* @param ws — WebSocket connection (or compatible mock).
|
|
60
|
+
* @param config — Transport-level configuration.
|
|
61
|
+
*/
|
|
62
|
+
constructor(ws, config) {
|
|
63
|
+
super();
|
|
64
|
+
this._ws = ws;
|
|
65
|
+
this._sampleRate = config.sampleRate;
|
|
66
|
+
this.id = randomUUID();
|
|
67
|
+
// Determine initial state from the socket's current ready-state value.
|
|
68
|
+
// The `ws` package uses numeric constants; OPEN = 1, CLOSED = 3.
|
|
69
|
+
const OPEN_STATE = ws.OPEN ?? 1;
|
|
70
|
+
this._state = ws.readyState === OPEN_STATE ? 'open' : 'connecting';
|
|
71
|
+
this._attachWsListeners();
|
|
72
|
+
}
|
|
73
|
+
// -------------------------------------------------------------------------
|
|
74
|
+
// IStreamTransport — public surface
|
|
75
|
+
// -------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Current connection state of the underlying WebSocket.
|
|
78
|
+
* Read-only from the outside; updated internally by WS event handlers.
|
|
79
|
+
*/
|
|
80
|
+
get state() {
|
|
81
|
+
return this._state;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Send a synthesised audio chunk to the remote client for playback.
|
|
85
|
+
*
|
|
86
|
+
* If `chunk` carries an {@link EncodedAudioChunk} (has an `audio` Buffer
|
|
87
|
+
* property), that buffer is sent directly. If it carries an {@link AudioFrame}
|
|
88
|
+
* (has a `samples` Float32Array), the samples are copied into a new `Buffer`
|
|
89
|
+
* and sent.
|
|
90
|
+
*
|
|
91
|
+
* @param chunk — Encoded audio chunk or raw PCM frame to deliver.
|
|
92
|
+
* @returns Resolves once the data has been handed to the OS socket buffer.
|
|
93
|
+
*/
|
|
94
|
+
sendAudio(chunk) {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
let binary;
|
|
97
|
+
if ('audio' in chunk) {
|
|
98
|
+
// EncodedAudioChunk — already a Buffer
|
|
99
|
+
binary = chunk.audio;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// AudioFrame — convert Float32Array samples to a byte Buffer
|
|
103
|
+
const frame = chunk;
|
|
104
|
+
binary = Buffer.from(frame.samples.buffer, frame.samples.byteOffset, frame.samples.byteLength);
|
|
105
|
+
}
|
|
106
|
+
this._ws.send(binary, (err) => {
|
|
107
|
+
if (err)
|
|
108
|
+
reject(err);
|
|
109
|
+
else
|
|
110
|
+
resolve();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Send a JSON control message to the remote client.
|
|
116
|
+
*
|
|
117
|
+
* The message is JSON-stringified before transmission. Both
|
|
118
|
+
* {@link TransportControlMessage} and {@link ServerTextMessage} are accepted
|
|
119
|
+
* since they share the same serialisation path.
|
|
120
|
+
*
|
|
121
|
+
* @param msg — Server-side protocol message.
|
|
122
|
+
* @returns Resolves once the message has been handed to the OS socket buffer.
|
|
123
|
+
*/
|
|
124
|
+
sendControl(msg) {
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
this._ws.send(JSON.stringify(msg), (err) => {
|
|
127
|
+
if (err)
|
|
128
|
+
reject(err);
|
|
129
|
+
else
|
|
130
|
+
resolve();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Initiate a graceful close of the transport.
|
|
136
|
+
*
|
|
137
|
+
* Sets internal state to `'closing'` immediately, then delegates to the
|
|
138
|
+
* underlying WebSocket's `close()` method. The `'disconnected'` event will
|
|
139
|
+
* fire once the socket's `'close'` event is received.
|
|
140
|
+
*
|
|
141
|
+
* @param code — Optional numeric WebSocket close code (default 1000).
|
|
142
|
+
* @param reason — Optional human-readable close reason.
|
|
143
|
+
*/
|
|
144
|
+
close(code, reason) {
|
|
145
|
+
this._state = 'closing';
|
|
146
|
+
this._ws.close(code, reason);
|
|
147
|
+
}
|
|
148
|
+
// -------------------------------------------------------------------------
|
|
149
|
+
// Private helpers
|
|
150
|
+
// -------------------------------------------------------------------------
|
|
151
|
+
/**
|
|
152
|
+
* Attach listeners to the underlying WebSocket for the events that matter to
|
|
153
|
+
* the voice pipeline. All listener state is contained here; no cleanup method
|
|
154
|
+
* is currently needed since the transport's lifetime is tied to the socket's.
|
|
155
|
+
*/
|
|
156
|
+
_attachWsListeners() {
|
|
157
|
+
// `message` — inbound data from the remote peer
|
|
158
|
+
this._ws.on('message', (data) => {
|
|
159
|
+
if (typeof data === 'string') {
|
|
160
|
+
// Text frame — parse as ClientTextMessage and propagate as 'control'
|
|
161
|
+
try {
|
|
162
|
+
const msg = JSON.parse(data);
|
|
163
|
+
this.emit('control', msg);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
// Malformed JSON — emit as an error but do not crash the transport
|
|
167
|
+
this.emit('error', new Error(`WebSocketStreamTransport: failed to parse text message: ${String(err)}`));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// Binary frame — interpret bytes as a Float32Array PCM sample buffer
|
|
172
|
+
let buffer;
|
|
173
|
+
if (Buffer.isBuffer(data)) {
|
|
174
|
+
buffer = data;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// ArrayBuffer (browser-style)
|
|
178
|
+
buffer = Buffer.from(data);
|
|
179
|
+
}
|
|
180
|
+
// Create a Float32Array view over the underlying ArrayBuffer.
|
|
181
|
+
// We use byteOffset/length to handle sliced Buffers correctly.
|
|
182
|
+
const samples = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
|
183
|
+
const frame = {
|
|
184
|
+
samples,
|
|
185
|
+
sampleRate: this._sampleRate,
|
|
186
|
+
timestamp: Date.now(),
|
|
187
|
+
};
|
|
188
|
+
this.emit('audio_frame', frame);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// `open` — socket handshake complete
|
|
192
|
+
this._ws.on('open', () => {
|
|
193
|
+
this._state = 'open';
|
|
194
|
+
this.emit('connected');
|
|
195
|
+
});
|
|
196
|
+
// `close` — socket has been fully closed (either side)
|
|
197
|
+
this._ws.on('close', () => {
|
|
198
|
+
this._state = 'closed';
|
|
199
|
+
this.emit('disconnected');
|
|
200
|
+
});
|
|
201
|
+
// `error` — transport-level socket error
|
|
202
|
+
this._ws.on('error', (err) => {
|
|
203
|
+
this.emit('error', err);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=WebSocketStreamTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketStreamTransport.js","sourceRoot":"","sources":["../../src/voice-pipeline/WebSocketStreamTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel exports for the AgentOS streaming voice pipeline.
|
|
3
|
+
*
|
|
4
|
+
* @module @framers/agentos/voice-pipeline
|
|
5
|
+
*/
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export { HardCutBargeinHandler } from './HardCutBargeinHandler.js';
|
|
8
|
+
export { SoftFadeBargeinHandler } from './SoftFadeBargeinHandler.js';
|
|
9
|
+
export { HeuristicEndpointDetector } from './HeuristicEndpointDetector.js';
|
|
10
|
+
export { AcousticEndpointDetector } from './AcousticEndpointDetector.js';
|
|
11
|
+
export { WebSocketStreamTransport } from './WebSocketStreamTransport.js';
|
|
12
|
+
export { VoicePipelineOrchestrator } from './VoicePipelineOrchestrator.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel exports for the AgentOS streaming voice pipeline.
|
|
3
|
+
*
|
|
4
|
+
* @module @framers/agentos/voice-pipeline
|
|
5
|
+
*/
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export { HardCutBargeinHandler } from './HardCutBargeinHandler.js';
|
|
8
|
+
export { SoftFadeBargeinHandler } from './SoftFadeBargeinHandler.js';
|
|
9
|
+
export { HeuristicEndpointDetector } from './HeuristicEndpointDetector.js';
|
|
10
|
+
export { AcousticEndpointDetector } from './AcousticEndpointDetector.js';
|
|
11
|
+
export { WebSocketStreamTransport } from './WebSocketStreamTransport.js';
|
|
12
|
+
export { VoicePipelineOrchestrator } from './VoicePipelineOrchestrator.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
|