@absolutejs/voice 0.0.22-beta.631 → 0.0.22-beta.633
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/core/types.d.ts +18 -1
- package/dist/index.js +94 -1
- package/dist/manifest.d.ts +2 -0
- package/dist/manifest.js +8865 -0
- package/dist/manifest.json +385 -0
- package/dist/testing/index.js +85 -0
- package/package.json +17 -4
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contract": 1,
|
|
3
|
+
"identity": {
|
|
4
|
+
"accent": "#f43f5e",
|
|
5
|
+
"category": "voice",
|
|
6
|
+
"description": "Self-hosted voice assistant runtime for Elysia: browser and phone-call sessions over WebSockets, pluggable speech-to-text (`voice/stt`), text-to-speech (`voice/tts`), and speech-to-speech (`voice/realtime`) vendor adapters, turn detection, barge-in, greetings, reconnect recovery, session stores (`voice/session-store`), traces, evals, and live-monitor surfaces — the orchestration hosted voice platforms keep behind their dashboards, inside your own server.",
|
|
7
|
+
"docsUrl": "https://github.com/absolutejs/voice",
|
|
8
|
+
"name": "@absolutejs/voice",
|
|
9
|
+
"tagline": "Let people talk to your site and get spoken answers."
|
|
10
|
+
},
|
|
11
|
+
"implements": [
|
|
12
|
+
{
|
|
13
|
+
"contract": "voice/session-store",
|
|
14
|
+
"factory": "createVoiceMemoryStore",
|
|
15
|
+
"from": "@absolutejs/voice",
|
|
16
|
+
"title": "In memory (development only — calls are forgotten on restart)",
|
|
17
|
+
"wiring": {
|
|
18
|
+
"code": "createVoiceMemoryStore()",
|
|
19
|
+
"imports": [
|
|
20
|
+
{
|
|
21
|
+
"from": "@absolutejs/voice",
|
|
22
|
+
"names": [
|
|
23
|
+
"createVoiceMemoryStore"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"contract": "voice/session-store",
|
|
31
|
+
"factory": "createVoiceSQLiteSessionStore",
|
|
32
|
+
"from": "@absolutejs/voice",
|
|
33
|
+
"settings": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"required": [
|
|
36
|
+
"path"
|
|
37
|
+
],
|
|
38
|
+
"properties": {
|
|
39
|
+
"path": {
|
|
40
|
+
"description": "Database file on this machine where call sessions are kept. Created if missing.",
|
|
41
|
+
"examples": [
|
|
42
|
+
"./var/voice-sessions.sqlite"
|
|
43
|
+
],
|
|
44
|
+
"title": "Database file",
|
|
45
|
+
"type": "string"
|
|
46
|
+
},
|
|
47
|
+
"tableName": {
|
|
48
|
+
"description": "Table the sessions are stored in.",
|
|
49
|
+
"title": "Table name",
|
|
50
|
+
"type": "string"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"title": "SQLite file on this machine",
|
|
55
|
+
"wiring": {
|
|
56
|
+
"code": "createVoiceSQLiteSessionStore(${settings})",
|
|
57
|
+
"imports": [
|
|
58
|
+
{
|
|
59
|
+
"from": "@absolutejs/voice",
|
|
60
|
+
"names": [
|
|
61
|
+
"createVoiceSQLiteSessionStore"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"contract": "voice/session-store",
|
|
69
|
+
"factory": "createVoicePostgresSessionStore",
|
|
70
|
+
"from": "@absolutejs/voice",
|
|
71
|
+
"requires": {
|
|
72
|
+
"env": [
|
|
73
|
+
{
|
|
74
|
+
"description": "Postgres connection string (call sessions are stored here)",
|
|
75
|
+
"example": "postgres://user:pass@host/db",
|
|
76
|
+
"key": "DATABASE_URL",
|
|
77
|
+
"secret": true
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"services": [
|
|
81
|
+
{
|
|
82
|
+
"description": "Stores call sessions durably",
|
|
83
|
+
"id": "postgres"
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
"settings": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"properties": {
|
|
90
|
+
"schemaName": {
|
|
91
|
+
"description": "Postgres schema the table lives in.",
|
|
92
|
+
"title": "Schema name",
|
|
93
|
+
"type": "string"
|
|
94
|
+
},
|
|
95
|
+
"tableName": {
|
|
96
|
+
"description": "Table the sessions are stored in.",
|
|
97
|
+
"title": "Table name",
|
|
98
|
+
"type": "string"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"title": "Postgres",
|
|
103
|
+
"wiring": {
|
|
104
|
+
"code": "createVoicePostgresSessionStore({ connectionString: ${env.DATABASE_URL} ?? \"\", ...${settings} })",
|
|
105
|
+
"imports": [
|
|
106
|
+
{
|
|
107
|
+
"from": "@absolutejs/voice",
|
|
108
|
+
"names": [
|
|
109
|
+
"createVoicePostgresSessionStore"
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"contract": "voice/session-store",
|
|
117
|
+
"factory": "createVoiceFileSessionStore",
|
|
118
|
+
"from": "@absolutejs/voice",
|
|
119
|
+
"settings": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"required": [
|
|
122
|
+
"directory"
|
|
123
|
+
],
|
|
124
|
+
"properties": {
|
|
125
|
+
"directory": {
|
|
126
|
+
"description": "Folder on this machine where each call session is kept as a JSON file. Created if missing.",
|
|
127
|
+
"examples": [
|
|
128
|
+
"./var/voice-sessions"
|
|
129
|
+
],
|
|
130
|
+
"title": "Storage folder",
|
|
131
|
+
"type": "string"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"title": "JSON files on this machine",
|
|
136
|
+
"wiring": {
|
|
137
|
+
"code": "createVoiceFileSessionStore(${settings})",
|
|
138
|
+
"imports": [
|
|
139
|
+
{
|
|
140
|
+
"from": "@absolutejs/voice",
|
|
141
|
+
"names": [
|
|
142
|
+
"createVoiceFileSessionStore"
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"requires": {
|
|
150
|
+
"peers": [
|
|
151
|
+
{
|
|
152
|
+
"name": "elysia",
|
|
153
|
+
"range": ">= 1.4.18",
|
|
154
|
+
"reason": "plugin host"
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"settings": {
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"backchannelBargeInGuard": {
|
|
162
|
+
"description": "Keep the assistant talking through listening cues like \"mm-hm\" or \"right\" instead of treating them as interruptions.",
|
|
163
|
+
"title": "Ignore listening cues",
|
|
164
|
+
"type": "boolean"
|
|
165
|
+
},
|
|
166
|
+
"greeting": {
|
|
167
|
+
"description": "Spoken by the assistant the moment a call connects, before the caller says anything.",
|
|
168
|
+
"examples": [
|
|
169
|
+
"Hi! How can I help you today?"
|
|
170
|
+
],
|
|
171
|
+
"title": "Opening greeting",
|
|
172
|
+
"type": "string"
|
|
173
|
+
},
|
|
174
|
+
"normalizeNumbers": {
|
|
175
|
+
"description": "Rewrite spoken numbers to digits in transcripts (\"ten million\" becomes \"10 million\") so downstream systems read them consistently.",
|
|
176
|
+
"title": "Write numbers as digits",
|
|
177
|
+
"type": "boolean"
|
|
178
|
+
},
|
|
179
|
+
"path": {
|
|
180
|
+
"default": "/voice/realtime",
|
|
181
|
+
"description": "The WebSocket route callers connect to.",
|
|
182
|
+
"title": "Call route",
|
|
183
|
+
"type": "string"
|
|
184
|
+
},
|
|
185
|
+
"preset": {
|
|
186
|
+
"description": "Tuned bundle of turn-taking and latency defaults for the kind of calls you take — casual chat, guided intake, dictation, noisy rooms, or phone lines.",
|
|
187
|
+
"title": "Conversation preset",
|
|
188
|
+
"anyOf": [
|
|
189
|
+
{
|
|
190
|
+
"const": "default",
|
|
191
|
+
"type": "string"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"const": "chat",
|
|
195
|
+
"type": "string"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"const": "guided-intake",
|
|
199
|
+
"type": "string"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"const": "dictation",
|
|
203
|
+
"type": "string"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"const": "noisy-room",
|
|
207
|
+
"type": "string"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"const": "pstn-balanced",
|
|
211
|
+
"type": "string"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"const": "pstn-fast",
|
|
215
|
+
"type": "string"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"const": "reliability",
|
|
219
|
+
"type": "string"
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
"resumeGreeting": {
|
|
224
|
+
"description": "Spoken when a dropped call reconnects, so the caller knows they are back.",
|
|
225
|
+
"examples": [
|
|
226
|
+
"Sorry, we got cut off — where were we?"
|
|
227
|
+
],
|
|
228
|
+
"title": "Reconnect greeting",
|
|
229
|
+
"type": "string"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"slots": {
|
|
234
|
+
"realtime": {
|
|
235
|
+
"configPath": "realtime",
|
|
236
|
+
"contract": "voice/realtime",
|
|
237
|
+
"description": "One speech-to-speech model that listens and talks in a single stream — an alternative to picking separate speech-to-text and text-to-speech services.",
|
|
238
|
+
"known": [
|
|
239
|
+
"@absolutejs/voice-openai",
|
|
240
|
+
"@absolutejs/voice-gemini"
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
"session": {
|
|
244
|
+
"configPath": "session",
|
|
245
|
+
"contract": "voice/session-store",
|
|
246
|
+
"description": "Where each call's transcript and state is kept while it runs (and after, for review)",
|
|
247
|
+
"known": [
|
|
248
|
+
"@absolutejs/voice#memory",
|
|
249
|
+
"@absolutejs/voice#sqlite",
|
|
250
|
+
"@absolutejs/voice#postgres",
|
|
251
|
+
"@absolutejs/voice#file"
|
|
252
|
+
],
|
|
253
|
+
"required": true
|
|
254
|
+
},
|
|
255
|
+
"stt": {
|
|
256
|
+
"configPath": "stt",
|
|
257
|
+
"contract": "voice/stt",
|
|
258
|
+
"description": "The service that turns the caller's speech into text. Not needed when a speech-to-speech (realtime) adapter is chosen instead.",
|
|
259
|
+
"known": [
|
|
260
|
+
"@absolutejs/voice-deepgram",
|
|
261
|
+
"@absolutejs/voice-assemblyai",
|
|
262
|
+
"@absolutejs/voice-gladia",
|
|
263
|
+
"@absolutejs/voice-soniox",
|
|
264
|
+
"@absolutejs/voice-speechmatics",
|
|
265
|
+
"@absolutejs/voice-azure",
|
|
266
|
+
"@absolutejs/voice-google-speech",
|
|
267
|
+
"@absolutejs/voice-openai-whisper"
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
"tts": {
|
|
271
|
+
"configPath": "tts",
|
|
272
|
+
"contract": "voice/tts",
|
|
273
|
+
"description": "The service that speaks the assistant's replies out loud. Not needed when a speech-to-speech (realtime) adapter is chosen instead.",
|
|
274
|
+
"known": [
|
|
275
|
+
"@absolutejs/voice-elevenlabs",
|
|
276
|
+
"@absolutejs/voice-cartesia",
|
|
277
|
+
"@absolutejs/voice-azure",
|
|
278
|
+
"@absolutejs/voice-lmnt",
|
|
279
|
+
"@absolutejs/voice-playht",
|
|
280
|
+
"@absolutejs/voice-rime",
|
|
281
|
+
"@absolutejs/voice-smallest",
|
|
282
|
+
"@absolutejs/voice-neets"
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"wiring": [
|
|
287
|
+
{
|
|
288
|
+
"description": "Pick a speech-to-text and a text-to-speech service, choose where call sessions are stored, and write the onTurn handler — the assistant's brain.",
|
|
289
|
+
"id": "default",
|
|
290
|
+
"server": {
|
|
291
|
+
"code": "// Every call's transcript and state lives in the session store.\nconst voiceSessions = ${slot.session};\n\n// Mount with .use(voiceAssistant); callers connect over WebSocket at `path`.\nconst voiceAssistant = voice({\n\tonTurn: async (session, turn) => {\n\t\t// TODO: generate the assistant's reply (call your LLM here).\n\t\treturn { assistantText: \"You said: \" + turn.text };\n\t},\n\tsession: voiceSessions,\n\tstt: ${slot.stt},\n\ttts: ${slot.tts},\n\t...${settings}\n});",
|
|
292
|
+
"imports": [
|
|
293
|
+
{
|
|
294
|
+
"from": "@absolutejs/voice",
|
|
295
|
+
"names": [
|
|
296
|
+
"voice"
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
],
|
|
300
|
+
"placement": "module-scope"
|
|
301
|
+
},
|
|
302
|
+
"title": "Voice assistant with separate STT + TTS"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"description": "Use one speech-to-speech model (OpenAI Realtime, Gemini Live) that listens and talks in a single stream instead of separate STT and TTS services.",
|
|
306
|
+
"id": "realtime",
|
|
307
|
+
"server": {
|
|
308
|
+
"code": "// Every call's transcript and state lives in the session store.\nconst voiceSessions = ${slot.session};\n\n// Mount with .use(voiceAssistant); callers connect over WebSocket at `path`.\nconst voiceAssistant = voice({\n\tonTurn: async (session, turn) => {\n\t\t// TODO: generate the assistant's reply (call your LLM here).\n\t\treturn { assistantText: \"You said: \" + turn.text };\n\t},\n\trealtime: ${slot.realtime},\n\tsession: voiceSessions,\n\t...${settings}\n});",
|
|
309
|
+
"imports": [
|
|
310
|
+
{
|
|
311
|
+
"from": "@absolutejs/voice",
|
|
312
|
+
"names": [
|
|
313
|
+
"voice"
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
],
|
|
317
|
+
"placement": "module-scope"
|
|
318
|
+
},
|
|
319
|
+
"title": "Voice assistant with a speech-to-speech model"
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
"tools": {
|
|
323
|
+
"delete_session": {
|
|
324
|
+
"annotations": {
|
|
325
|
+
"destructiveHint": true,
|
|
326
|
+
"idempotentHint": true
|
|
327
|
+
},
|
|
328
|
+
"description": "Delete one call session (its transcript and state) by id. Deleting an unknown id succeeds.",
|
|
329
|
+
"input": {
|
|
330
|
+
"type": "object",
|
|
331
|
+
"required": [
|
|
332
|
+
"id"
|
|
333
|
+
],
|
|
334
|
+
"properties": {
|
|
335
|
+
"id": {
|
|
336
|
+
"minLength": 1,
|
|
337
|
+
"type": "string"
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"kind": "runtime"
|
|
342
|
+
},
|
|
343
|
+
"get_session": {
|
|
344
|
+
"annotations": {
|
|
345
|
+
"readOnlyHint": true
|
|
346
|
+
},
|
|
347
|
+
"description": "Fetch one call session by id: status, timing, and the committed conversation turns (caller text and assistant reply), most recent last.",
|
|
348
|
+
"input": {
|
|
349
|
+
"type": "object",
|
|
350
|
+
"required": [
|
|
351
|
+
"id"
|
|
352
|
+
],
|
|
353
|
+
"properties": {
|
|
354
|
+
"id": {
|
|
355
|
+
"minLength": 1,
|
|
356
|
+
"type": "string"
|
|
357
|
+
},
|
|
358
|
+
"maxTurns": {
|
|
359
|
+
"maximum": 200,
|
|
360
|
+
"minimum": 1,
|
|
361
|
+
"type": "integer"
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
"kind": "runtime"
|
|
366
|
+
},
|
|
367
|
+
"list_sessions": {
|
|
368
|
+
"annotations": {
|
|
369
|
+
"readOnlyHint": true
|
|
370
|
+
},
|
|
371
|
+
"description": "List call sessions with status, creation time, last activity, and turn count — most recently active first.",
|
|
372
|
+
"input": {
|
|
373
|
+
"type": "object",
|
|
374
|
+
"properties": {
|
|
375
|
+
"limit": {
|
|
376
|
+
"maximum": 200,
|
|
377
|
+
"minimum": 1,
|
|
378
|
+
"type": "integer"
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
"kind": "runtime"
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
package/dist/testing/index.js
CHANGED
|
@@ -6751,6 +6751,9 @@ var createVoiceSession = (options) => {
|
|
|
6751
6751
|
if (callSilenceTimeoutMs === undefined || callSilenceFired) {
|
|
6752
6752
|
return;
|
|
6753
6753
|
}
|
|
6754
|
+
if (callerPaused) {
|
|
6755
|
+
return;
|
|
6756
|
+
}
|
|
6754
6757
|
clearCallSilenceWatchdog();
|
|
6755
6758
|
callSilenceWatchdog = setTimeout(fireCallSilenceTimeout, callSilenceTimeoutMs);
|
|
6756
6759
|
};
|
|
@@ -6796,6 +6799,9 @@ var createVoiceSession = (options) => {
|
|
|
6796
6799
|
if (stuckCloseAfterMs === undefined || stuckCloseFired) {
|
|
6797
6800
|
return;
|
|
6798
6801
|
}
|
|
6802
|
+
if (callerPaused) {
|
|
6803
|
+
return;
|
|
6804
|
+
}
|
|
6799
6805
|
clearStuckCloseWatchdog();
|
|
6800
6806
|
stuckCloseWatchdog = setTimeout(fireStuckClose, stuckCloseAfterMs);
|
|
6801
6807
|
};
|
|
@@ -6818,6 +6824,9 @@ var createVoiceSession = (options) => {
|
|
|
6818
6824
|
idleRepromptsUsed = 0;
|
|
6819
6825
|
}
|
|
6820
6826
|
clearIdleRepromptWatchdog();
|
|
6827
|
+
if (callerPaused) {
|
|
6828
|
+
return;
|
|
6829
|
+
}
|
|
6821
6830
|
if (idleRepromptsUsed >= idleRepromptMax) {
|
|
6822
6831
|
return;
|
|
6823
6832
|
}
|
|
@@ -6838,6 +6847,60 @@ var createVoiceSession = (options) => {
|
|
|
6838
6847
|
});
|
|
6839
6848
|
}, idleRepromptAfterMs);
|
|
6840
6849
|
};
|
|
6850
|
+
const PAUSE_MAX_MS_DEFAULT = 600000;
|
|
6851
|
+
const pauseMaxMs = options.pause?.maxMs && options.pause.maxMs > 0 ? options.pause.maxMs : PAUSE_MAX_MS_DEFAULT;
|
|
6852
|
+
let callerPaused = false;
|
|
6853
|
+
let pauseTimeout = null;
|
|
6854
|
+
const clearPauseTimeout = () => {
|
|
6855
|
+
if (pauseTimeout) {
|
|
6856
|
+
clearTimeout(pauseTimeout);
|
|
6857
|
+
pauseTimeout = null;
|
|
6858
|
+
}
|
|
6859
|
+
};
|
|
6860
|
+
const pauseInternal = async () => {
|
|
6861
|
+
if (callerPaused) {
|
|
6862
|
+
return;
|
|
6863
|
+
}
|
|
6864
|
+
const snapshot = await readSession();
|
|
6865
|
+
if (snapshot.status === "completed" || snapshot.status === "failed" || snapshot.call?.endedAt) {
|
|
6866
|
+
return;
|
|
6867
|
+
}
|
|
6868
|
+
callerPaused = true;
|
|
6869
|
+
clearCallSilenceWatchdog();
|
|
6870
|
+
clearStuckCloseWatchdog();
|
|
6871
|
+
clearIdleRepromptWatchdog();
|
|
6872
|
+
clearPauseTimeout();
|
|
6873
|
+
pauseTimeout = setTimeout(() => {
|
|
6874
|
+
pauseTimeout = null;
|
|
6875
|
+
api.close("pause-timeout");
|
|
6876
|
+
}, pauseMaxMs);
|
|
6877
|
+
await appendTrace({
|
|
6878
|
+
payload: {
|
|
6879
|
+
action: "call.paused",
|
|
6880
|
+
maxMs: pauseMaxMs
|
|
6881
|
+
},
|
|
6882
|
+
session: snapshot,
|
|
6883
|
+
type: "call.lifecycle"
|
|
6884
|
+
});
|
|
6885
|
+
};
|
|
6886
|
+
const resumeInternal = async () => {
|
|
6887
|
+
if (!callerPaused) {
|
|
6888
|
+
return;
|
|
6889
|
+
}
|
|
6890
|
+
callerPaused = false;
|
|
6891
|
+
clearPauseTimeout();
|
|
6892
|
+
kickCallSilenceWatchdog();
|
|
6893
|
+
kickStuckCloseWatchdog();
|
|
6894
|
+
kickIdleRepromptWatchdog(true);
|
|
6895
|
+
const snapshot = await readSession();
|
|
6896
|
+
await appendTrace({
|
|
6897
|
+
payload: {
|
|
6898
|
+
action: "call.resumed"
|
|
6899
|
+
},
|
|
6900
|
+
session: snapshot,
|
|
6901
|
+
type: "call.lifecycle"
|
|
6902
|
+
});
|
|
6903
|
+
};
|
|
6841
6904
|
const recordingConfig = options.recording;
|
|
6842
6905
|
const recordingChannels = new Set(recordingConfig?.channels ?? ["assistant", "user"]);
|
|
6843
6906
|
const recordingMaxBytes = recordingConfig?.maxBytesPerChannel ?? 50 * 1024 * 1024;
|
|
@@ -7345,6 +7408,7 @@ var createVoiceSession = (options) => {
|
|
|
7345
7408
|
clearCallSilenceWatchdog();
|
|
7346
7409
|
clearStuckCloseWatchdog();
|
|
7347
7410
|
clearIdleRepromptWatchdog();
|
|
7411
|
+
clearPauseTimeout();
|
|
7348
7412
|
clearAmdEvaluationTimer();
|
|
7349
7413
|
await closeTTSSession("failed");
|
|
7350
7414
|
await closeAdapter("failed");
|
|
@@ -7456,6 +7520,7 @@ var createVoiceSession = (options) => {
|
|
|
7456
7520
|
clearCallSilenceWatchdog();
|
|
7457
7521
|
clearStuckCloseWatchdog();
|
|
7458
7522
|
clearIdleRepromptWatchdog();
|
|
7523
|
+
clearPauseTimeout();
|
|
7459
7524
|
clearAmdEvaluationTimer();
|
|
7460
7525
|
await closeTTSSession("complete");
|
|
7461
7526
|
await closeAdapter("complete");
|
|
@@ -8416,6 +8481,19 @@ var createVoiceSession = (options) => {
|
|
|
8416
8481
|
}
|
|
8417
8482
|
kickStuckCloseWatchdog();
|
|
8418
8483
|
kickIdleRepromptWatchdog(true);
|
|
8484
|
+
if (callerPaused) {
|
|
8485
|
+
await appendTrace({
|
|
8486
|
+
payload: {
|
|
8487
|
+
action: "turn.skipped",
|
|
8488
|
+
reason: "caller-paused",
|
|
8489
|
+
status: "skipped"
|
|
8490
|
+
},
|
|
8491
|
+
session,
|
|
8492
|
+
turnId: turn.id,
|
|
8493
|
+
type: "call.lifecycle"
|
|
8494
|
+
});
|
|
8495
|
+
return;
|
|
8496
|
+
}
|
|
8419
8497
|
const liveOpsControl = await options.liveOps?.getControl(options.id);
|
|
8420
8498
|
if (liveOpsControl?.assistantPaused || liveOpsControl?.operatorTakeover) {
|
|
8421
8499
|
await appendTrace({
|
|
@@ -9266,6 +9344,7 @@ var createVoiceSession = (options) => {
|
|
|
9266
9344
|
clearCallSilenceWatchdog();
|
|
9267
9345
|
clearStuckCloseWatchdog();
|
|
9268
9346
|
clearIdleRepromptWatchdog();
|
|
9347
|
+
clearPauseTimeout();
|
|
9269
9348
|
clearAmdEvaluationTimer();
|
|
9270
9349
|
if (options.noiseSuppressor?.close) {
|
|
9271
9350
|
try {
|
|
@@ -9331,9 +9410,15 @@ var createVoiceSession = (options) => {
|
|
|
9331
9410
|
markVoicemail: async (input) => runSerial("api.markVoicemail", async () => {
|
|
9332
9411
|
await markVoicemailInternal(input);
|
|
9333
9412
|
}),
|
|
9413
|
+
pause: async () => runSerial("api.pause", async () => {
|
|
9414
|
+
await pauseInternal();
|
|
9415
|
+
}),
|
|
9334
9416
|
receiveAudio: async (audio) => runSerial("api.receiveAudio", async () => {
|
|
9335
9417
|
await receiveAudioInternal(audio);
|
|
9336
9418
|
}),
|
|
9419
|
+
resume: async () => runSerial("api.resume", async () => {
|
|
9420
|
+
await resumeInternal();
|
|
9421
|
+
}),
|
|
9337
9422
|
snapshot: async () => runSerial("api.snapshot", async () => readSession()),
|
|
9338
9423
|
transfer: async (input) => runSerial("api.transfer", async () => {
|
|
9339
9424
|
await transferInternal(input);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/voice",
|
|
3
|
-
"version": "0.0.22-beta.
|
|
3
|
+
"version": "0.0.22-beta.633",
|
|
4
4
|
"description": "Voice primitives and Elysia plugin for AbsoluteJS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "BSL-1.1",
|
|
20
20
|
"author": "Alex Kahn",
|
|
21
21
|
"scripts": {
|
|
22
|
-
"build": "rm -rf dist && bun build ./src/index.ts ./src/client/index.ts ./src/react/index.ts ./src/vue/index.ts ./src/svelte/index.ts ./src/angular/index.ts ./src/testing/index.ts ./src/drizzle/index.ts --outdir dist --target bun --external elysia --external react --external vue --external @angular/core --external @absolutejs/absolute --external @absolutejs/ai --external @absolutejs/media --external drizzle-orm && bun build ./src/client/htmxBootstrap.ts --outdir dist/client --target browser --format esm && bun build ./src/embed/index.ts --outfile dist/embed/voice-widget.js --target browser --format iife --minify && bun build ./src/embed/index.ts --outdir dist/embed --target browser --format esm && tsc --emitDeclarationOnly --project tsconfig.json",
|
|
22
|
+
"build": "rm -rf dist && bun build ./src/index.ts ./src/manifest.ts ./src/client/index.ts ./src/react/index.ts ./src/vue/index.ts ./src/svelte/index.ts ./src/angular/index.ts ./src/testing/index.ts ./src/drizzle/index.ts --outdir dist --root ./src --target bun --external elysia --external react --external vue --external @angular/core --external @absolutejs/absolute --external @absolutejs/ai --external @absolutejs/media --external drizzle-orm && bun build ./src/client/htmxBootstrap.ts --outdir dist/client --target browser --format esm && bun build ./src/embed/index.ts --outfile dist/embed/voice-widget.js --target browser --format iife --minify && bun build ./src/embed/index.ts --outdir dist/embed --target browser --format esm && tsc --emitDeclarationOnly --project tsconfig.json && absolute-manifest emit",
|
|
23
23
|
"config": "absolute config",
|
|
24
24
|
"format": "absolute prettier --write",
|
|
25
25
|
"knip": "knip",
|
|
@@ -75,7 +75,12 @@
|
|
|
75
75
|
"import": "./dist/embed/index.js",
|
|
76
76
|
"types": "./dist/embed/index.d.ts"
|
|
77
77
|
},
|
|
78
|
-
"./embed/voice-widget.js": "./dist/embed/voice-widget.js"
|
|
78
|
+
"./embed/voice-widget.js": "./dist/embed/voice-widget.js",
|
|
79
|
+
"./manifest": {
|
|
80
|
+
"import": "./dist/manifest.js",
|
|
81
|
+
"types": "./dist/manifest.d.ts"
|
|
82
|
+
},
|
|
83
|
+
"./manifest.json": "./dist/manifest.json"
|
|
79
84
|
},
|
|
80
85
|
"typesVersions": {
|
|
81
86
|
"*": {
|
|
@@ -102,6 +107,9 @@
|
|
|
102
107
|
],
|
|
103
108
|
"embed": [
|
|
104
109
|
"dist/embed/index.d.ts"
|
|
110
|
+
],
|
|
111
|
+
"manifest": [
|
|
112
|
+
"dist/manifest.d.ts"
|
|
105
113
|
]
|
|
106
114
|
}
|
|
107
115
|
},
|
|
@@ -132,7 +140,9 @@
|
|
|
132
140
|
}
|
|
133
141
|
},
|
|
134
142
|
"dependencies": {
|
|
135
|
-
"@absolutejs/
|
|
143
|
+
"@absolutejs/manifest": "^0.1.0",
|
|
144
|
+
"@absolutejs/media": "0.0.1-beta.19",
|
|
145
|
+
"@sinclair/typebox": "^0.34.0"
|
|
136
146
|
},
|
|
137
147
|
"devDependencies": {
|
|
138
148
|
"@absolutejs/absolute": "0.19.0-beta.1009",
|
|
@@ -157,5 +167,8 @@
|
|
|
157
167
|
"typescript": "^5.9.3",
|
|
158
168
|
"typescript-eslint": "^8.57.2",
|
|
159
169
|
"vue": "3.5.27"
|
|
170
|
+
},
|
|
171
|
+
"absolutejs": {
|
|
172
|
+
"manifestContract": 1
|
|
160
173
|
}
|
|
161
174
|
}
|