@guava-ai/guava-sdk 0.37.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -2
- package/dist/guava-webrtc.esm.min.js +2 -0
- package/dist/guava-webrtc.esm.min.js.map +7 -0
- package/dist/guava-webrtc.min.js +2 -0
- package/dist/guava-webrtc.min.js.map +7 -0
- package/dist/src/call.d.ts +5 -4
- package/dist/src/call.js +4 -4
- package/dist/src/call.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/sip.d.ts +15 -0
- package/dist/src/sip.js +52 -0
- package/dist/src/sip.js.map +1 -0
- package/dist/src/socket/call-info.d.ts +2 -0
- package/dist/src/socket/call-info.js +1 -0
- package/dist/src/socket/call-info.js.map +1 -1
- package/dist/src/socket/listen-inbound.d.ts +2 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/webrtc/index.d.ts +74 -0
- package/dist/src/webrtc/index.js +398 -0
- package/dist/src/webrtc/index.js.map +1 -0
- package/dist/src/webrtc/tones.d.ts +38 -0
- package/dist/src/webrtc/tones.js +224 -0
- package/dist/src/webrtc/tones.js.map +1 -0
- package/package.json +16 -3
- package/src/call.ts +7 -6
- package/src/index.ts +1 -0
- package/src/sip.ts +62 -0
- package/src/socket/call-info.ts +1 -0
- package/src/version.ts +1 -1
- package/src/webrtc/index.ts +497 -0
- package/src/webrtc/tones.ts +284 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LeavingTone = exports.JingleTone = exports.RingbackTone = void 0;
|
|
4
|
+
// Seconds from the start of bar 0 to the given 0-indexed beat.
|
|
5
|
+
function beatTime(beat, bpm) {
|
|
6
|
+
return beat * (60 / bpm);
|
|
7
|
+
}
|
|
8
|
+
// North American ringback: 440 + 480 Hz, 2s on, 4s off.
|
|
9
|
+
class RingbackTone {
|
|
10
|
+
context = null;
|
|
11
|
+
intervalId = null;
|
|
12
|
+
volume = 0.15;
|
|
13
|
+
onSeconds = 2;
|
|
14
|
+
periodSeconds = 6;
|
|
15
|
+
// Delay before the first ring, so near-instant connections stay silent.
|
|
16
|
+
startDelay = 0.3;
|
|
17
|
+
start() {
|
|
18
|
+
if (this.context)
|
|
19
|
+
return;
|
|
20
|
+
const context = new AudioContext();
|
|
21
|
+
const gain = context.createGain();
|
|
22
|
+
gain.gain.value = 0;
|
|
23
|
+
gain.connect(context.destination);
|
|
24
|
+
for (const frequency of [440, 480]) {
|
|
25
|
+
const oscillator = context.createOscillator();
|
|
26
|
+
oscillator.frequency.value = frequency;
|
|
27
|
+
oscillator.connect(gain);
|
|
28
|
+
oscillator.start();
|
|
29
|
+
}
|
|
30
|
+
let nextRing = context.currentTime + this.startDelay;
|
|
31
|
+
const ring = () => {
|
|
32
|
+
gain.gain.setValueAtTime(this.volume, nextRing);
|
|
33
|
+
gain.gain.setValueAtTime(0, nextRing + this.onSeconds);
|
|
34
|
+
nextRing += this.periodSeconds;
|
|
35
|
+
};
|
|
36
|
+
ring();
|
|
37
|
+
this.intervalId = setInterval(ring, this.periodSeconds * 1000);
|
|
38
|
+
this.context = context;
|
|
39
|
+
}
|
|
40
|
+
stop() {
|
|
41
|
+
if (!this.context)
|
|
42
|
+
return;
|
|
43
|
+
if (this.intervalId) {
|
|
44
|
+
clearInterval(this.intervalId);
|
|
45
|
+
this.intervalId = null;
|
|
46
|
+
}
|
|
47
|
+
// Closing the context stops the oscillators.
|
|
48
|
+
void this.context.close();
|
|
49
|
+
this.context = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.RingbackTone = RingbackTone;
|
|
53
|
+
class JingleTone {
|
|
54
|
+
context = null;
|
|
55
|
+
masterGain = null;
|
|
56
|
+
intervalId = null;
|
|
57
|
+
running = false;
|
|
58
|
+
pulseCount = 0;
|
|
59
|
+
// Audio-clock time of the next bar to schedule.
|
|
60
|
+
nextBarStart = 0;
|
|
61
|
+
// Configuration
|
|
62
|
+
volume = 0.3;
|
|
63
|
+
bpm = 120;
|
|
64
|
+
beatsPerBar = 4;
|
|
65
|
+
// Delay before the first note, so near-instant connections stay silent.
|
|
66
|
+
startDelay = 0.3;
|
|
67
|
+
start() {
|
|
68
|
+
if (this.running)
|
|
69
|
+
return;
|
|
70
|
+
this.running = true;
|
|
71
|
+
this.pulseCount = 0;
|
|
72
|
+
this.context = new AudioContext();
|
|
73
|
+
this.masterGain = this.context.createGain();
|
|
74
|
+
this.masterGain.gain.value = this.volume;
|
|
75
|
+
this.masterGain.connect(this.context.destination);
|
|
76
|
+
this.nextBarStart = this.context.currentTime + this.startDelay;
|
|
77
|
+
// Stay one bar ahead so timer jitter doesn't shift the beat.
|
|
78
|
+
this.playPulse();
|
|
79
|
+
this.playPulse();
|
|
80
|
+
const barLength = beatTime(this.beatsPerBar, this.bpm);
|
|
81
|
+
this.intervalId = setInterval(() => {
|
|
82
|
+
this.playPulse();
|
|
83
|
+
}, barLength * 1000);
|
|
84
|
+
}
|
|
85
|
+
noteToFrequency(note) {
|
|
86
|
+
const match = note.match(/^([A-G])(#|b)?(-?\d+)$/);
|
|
87
|
+
if (!match) {
|
|
88
|
+
throw new Error(`Invalid note: ${note}`);
|
|
89
|
+
}
|
|
90
|
+
const [, name, accidental, octave] = match;
|
|
91
|
+
const semitones = {
|
|
92
|
+
C: 0,
|
|
93
|
+
D: 2,
|
|
94
|
+
E: 4,
|
|
95
|
+
F: 5,
|
|
96
|
+
G: 7,
|
|
97
|
+
A: 9,
|
|
98
|
+
B: 11,
|
|
99
|
+
};
|
|
100
|
+
const offset = accidental === "#" ? 1 : accidental === "b" ? -1 : 0;
|
|
101
|
+
const midi = (Number(octave) + 1) * 12 + semitones[name] + offset;
|
|
102
|
+
return 440 * Math.pow(2, (midi - 69) / 12);
|
|
103
|
+
}
|
|
104
|
+
stop() {
|
|
105
|
+
if (!this.running)
|
|
106
|
+
return;
|
|
107
|
+
this.running = false;
|
|
108
|
+
if (this.intervalId) {
|
|
109
|
+
clearInterval(this.intervalId);
|
|
110
|
+
this.intervalId = null;
|
|
111
|
+
}
|
|
112
|
+
const context = this.context;
|
|
113
|
+
const gain = this.masterGain;
|
|
114
|
+
if (!context || !gain)
|
|
115
|
+
return;
|
|
116
|
+
// Smooth fade-out
|
|
117
|
+
const now = context.currentTime;
|
|
118
|
+
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
119
|
+
gain.gain.linearRampToValueAtTime(0, now + 0.15);
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
void context.close();
|
|
122
|
+
}, 200);
|
|
123
|
+
this.context = null;
|
|
124
|
+
this.masterGain = null;
|
|
125
|
+
}
|
|
126
|
+
playPulse() {
|
|
127
|
+
if (!this.context || !this.masterGain)
|
|
128
|
+
return;
|
|
129
|
+
const bar = this.nextBarStart;
|
|
130
|
+
const beat = (n) => bar + beatTime(n, this.bpm);
|
|
131
|
+
const beatLength = beatTime(1, this.bpm);
|
|
132
|
+
// Notes on beats 1-3, silence on beat 4.
|
|
133
|
+
if (this.pulseCount % 4 === 0 || this.pulseCount % 4 === 2) {
|
|
134
|
+
this.playNote("C5", beat(0), beatLength);
|
|
135
|
+
this.playNote("E5", beat(0.5), beatLength);
|
|
136
|
+
this.playNote("G4", beat(1), beatLength);
|
|
137
|
+
}
|
|
138
|
+
else if (this.pulseCount % 4 === 3) {
|
|
139
|
+
this.playNote("E5", beat(0), beatLength);
|
|
140
|
+
this.playNote("G5", beat(0.5), beatLength);
|
|
141
|
+
this.playNote("C5", beat(1), beatLength);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.playNote("E5", beat(0), beatLength);
|
|
145
|
+
this.playNote("G4", beat(0.5), beatLength);
|
|
146
|
+
this.playNote("C5", beat(1), beatLength);
|
|
147
|
+
}
|
|
148
|
+
this.pulseCount++;
|
|
149
|
+
this.nextBarStart = beat(this.beatsPerBar);
|
|
150
|
+
}
|
|
151
|
+
playNote(note, startTime, duration) {
|
|
152
|
+
if (!this.context || !this.masterGain)
|
|
153
|
+
return;
|
|
154
|
+
const oscillator = this.context.createOscillator();
|
|
155
|
+
const envelope = this.context.createGain();
|
|
156
|
+
oscillator.type = "sine";
|
|
157
|
+
oscillator.frequency.value = this.noteToFrequency(note);
|
|
158
|
+
envelope.gain.setValueAtTime(0, startTime);
|
|
159
|
+
envelope.gain.linearRampToValueAtTime(0.7, startTime + 0.05);
|
|
160
|
+
envelope.gain.exponentialRampToValueAtTime(0.001, startTime + duration);
|
|
161
|
+
oscillator.connect(envelope);
|
|
162
|
+
envelope.connect(this.masterGain);
|
|
163
|
+
oscillator.start(startTime);
|
|
164
|
+
oscillator.stop(startTime + duration + 0.01);
|
|
165
|
+
oscillator.onended = () => {
|
|
166
|
+
oscillator.disconnect();
|
|
167
|
+
envelope.disconnect();
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.JingleTone = JingleTone;
|
|
172
|
+
class LeavingTone {
|
|
173
|
+
play() {
|
|
174
|
+
const ctx = new AudioContext();
|
|
175
|
+
if (ctx.state === "suspended") {
|
|
176
|
+
void ctx.resume();
|
|
177
|
+
}
|
|
178
|
+
const now = ctx.currentTime;
|
|
179
|
+
// First note: D4
|
|
180
|
+
const first = this.playNote(ctx, {
|
|
181
|
+
frequency: 293.665,
|
|
182
|
+
startTime: now,
|
|
183
|
+
attack: 0.065,
|
|
184
|
+
decay: 0.085,
|
|
185
|
+
volume: 0.2,
|
|
186
|
+
});
|
|
187
|
+
// Second note: A3, starts 139ms later
|
|
188
|
+
const second = this.playNote(ctx, {
|
|
189
|
+
frequency: 220.0,
|
|
190
|
+
startTime: now + 0.139,
|
|
191
|
+
attack: 0.018,
|
|
192
|
+
decay: 0.075,
|
|
193
|
+
volume: 0.2,
|
|
194
|
+
});
|
|
195
|
+
void Promise.all([first, second]).then(() => ctx.close());
|
|
196
|
+
}
|
|
197
|
+
// Resolves once the note has finished playing.
|
|
198
|
+
playNote(ctx, { frequency, startTime, attack, decay, volume, }) {
|
|
199
|
+
const oscillator = ctx.createOscillator();
|
|
200
|
+
const gain = ctx.createGain();
|
|
201
|
+
oscillator.type = "sine";
|
|
202
|
+
oscillator.frequency.setValueAtTime(frequency, startTime);
|
|
203
|
+
// Smooth onset
|
|
204
|
+
gain.gain.setValueAtTime(0, startTime);
|
|
205
|
+
gain.gain.linearRampToValueAtTime(volume, startTime + attack);
|
|
206
|
+
// Exponential decay
|
|
207
|
+
const peakTime = startTime + attack;
|
|
208
|
+
const endTime = peakTime + decay * 5;
|
|
209
|
+
gain.gain.setTargetAtTime(0.00001, peakTime, decay);
|
|
210
|
+
oscillator.connect(gain);
|
|
211
|
+
gain.connect(ctx.destination);
|
|
212
|
+
oscillator.start(startTime);
|
|
213
|
+
oscillator.stop(endTime);
|
|
214
|
+
return new Promise((resolve) => {
|
|
215
|
+
oscillator.onended = () => {
|
|
216
|
+
oscillator.disconnect();
|
|
217
|
+
gain.disconnect();
|
|
218
|
+
resolve();
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
exports.LeavingTone = LeavingTone;
|
|
224
|
+
//# sourceMappingURL=tones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tones.js","sourceRoot":"","sources":["../../../src/webrtc/tones.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@guava-ai/guava-sdk",
|
|
3
3
|
"description": "The official TypeScript SDK for building Guava voice agents.",
|
|
4
4
|
"homepage": "https://docs.goguava.ai/typescript-sdk/",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.39.0",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"main": "dist/src/index.js",
|
|
8
8
|
"types": "dist/src/index.d.ts",
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"types": "./dist/src/helpers/openai.d.ts",
|
|
20
20
|
"default": "./dist/src/helpers/openai.js"
|
|
21
21
|
},
|
|
22
|
+
"./webrtc": {
|
|
23
|
+
"types": "./dist/src/webrtc/index.d.ts",
|
|
24
|
+
"default": "./dist/src/webrtc/index.js"
|
|
25
|
+
},
|
|
22
26
|
"./example-data": {
|
|
23
27
|
"types": "./dist/src/example-data.d.ts",
|
|
24
28
|
"default": "./dist/src/example-data.js"
|
|
@@ -37,7 +41,9 @@
|
|
|
37
41
|
"@types/jest": "^30.0.0",
|
|
38
42
|
"@types/node": "^25.0.7",
|
|
39
43
|
"@types/ws": "^8.18.1",
|
|
44
|
+
"esbuild": "^0.28.2",
|
|
40
45
|
"jest": "^30.4.2",
|
|
46
|
+
"jest-environment-jsdom": "^30.5.2",
|
|
41
47
|
"openai": "^6.16.0",
|
|
42
48
|
"ts-jest": "^29.4.11",
|
|
43
49
|
"tsx": "^4.21.0",
|
|
@@ -70,7 +76,10 @@
|
|
|
70
76
|
"test:e2e": "jest --config jest.e2e.config.cjs",
|
|
71
77
|
"check": "npm run typecheck && npm audit --omit=dev && npm run lint && npm run check-format",
|
|
72
78
|
"clean": "rm -rf ./dist",
|
|
73
|
-
"build": "tsx scripts/update-version.ts && tsc && chmod +x ./dist/bin/example-runner.js",
|
|
79
|
+
"build": "tsx scripts/update-version.ts && tsc && chmod +x ./dist/bin/example-runner.js && npm run build-webrtc && tsx scripts/update-readme.ts",
|
|
80
|
+
"build-webrtc": "npm run build-webrtc:esm && npm run build-webrtc:iife",
|
|
81
|
+
"build-webrtc:esm": "esbuild src/webrtc/index.ts --bundle --minify --sourcemap --target=es2020 --format=esm --outfile=dist/guava-webrtc.esm.min.js",
|
|
82
|
+
"build-webrtc:iife": "esbuild src/webrtc/index.ts --bundle --minify --sourcemap --target=es2020 --format=iife --global-name=Guava --outfile=dist/guava-webrtc.min.js",
|
|
74
83
|
"prepare": "npm run clean && npm run build",
|
|
75
84
|
"release": "npm publish --ignore-scripts=false"
|
|
76
85
|
},
|
|
@@ -85,5 +94,9 @@
|
|
|
85
94
|
"/dist",
|
|
86
95
|
"/examples",
|
|
87
96
|
"/bin"
|
|
88
|
-
]
|
|
97
|
+
],
|
|
98
|
+
"repository": {
|
|
99
|
+
"type": "git",
|
|
100
|
+
"url": "git+https://github.com/goguava-ai/typescript-sdk.git"
|
|
101
|
+
}
|
|
89
102
|
}
|
package/src/call.ts
CHANGED
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
import { Say } from "./action-item.ts";
|
|
26
26
|
import { telemetryClient } from "./telemetry.ts";
|
|
27
27
|
import type { CallInfo } from "./socket/call-info.ts";
|
|
28
|
+
import type { SipUri } from "./sip.ts";
|
|
28
29
|
|
|
29
30
|
export type TaskObjective =
|
|
30
31
|
| { objective: string }
|
|
@@ -248,13 +249,13 @@ export class Call {
|
|
|
248
249
|
* This method does not interfere with turn-taking — use this when
|
|
249
250
|
* you want the agent to naturally transfer the call.
|
|
250
251
|
*
|
|
251
|
-
* @param destination - The phone number
|
|
252
|
+
* @param destination - The phone number, SIP address, or SipUri to transfer the call to.
|
|
252
253
|
* @param instructions - Optional guidance for how the agent should handle the transfer.
|
|
253
254
|
*/
|
|
254
|
-
async transfer(destination: string, instructions?: string) {
|
|
255
|
+
async transfer(destination: string | SipUri, instructions?: string) {
|
|
255
256
|
await this.sendCommand(TransferCommand, {
|
|
256
257
|
command_type: "transfer-call",
|
|
257
|
-
to_number: destination,
|
|
258
|
+
to_number: destination.toString(),
|
|
258
259
|
transfer_message:
|
|
259
260
|
instructions ?? "Notify the caller that you will be transferring them, and then transfer.",
|
|
260
261
|
soft_transfer: true,
|
|
@@ -267,14 +268,14 @@ export class Call {
|
|
|
267
268
|
* Unlike transfer(), this does not respect turn-taking. The agent immediately
|
|
268
269
|
* initiates the transfer, saying the script (if provided).
|
|
269
270
|
*
|
|
270
|
-
* @param destination - The phone number
|
|
271
|
+
* @param destination - The phone number, SIP address, or SipUri to transfer the call to.
|
|
271
272
|
* @param finalScript - Optional message for the agent to deliver immediately
|
|
272
273
|
* before transferring. Defaults to no message.
|
|
273
274
|
*/
|
|
274
|
-
async immediateTransfer(destination: string, finalScript?: string) {
|
|
275
|
+
async immediateTransfer(destination: string | SipUri, finalScript?: string) {
|
|
275
276
|
await this.sendCommand(TransferCommand, {
|
|
276
277
|
command_type: "transfer-call",
|
|
277
|
-
to_number: destination,
|
|
278
|
+
to_number: destination.toString(),
|
|
278
279
|
transfer_message: finalScript ?? "",
|
|
279
280
|
soft_transfer: false,
|
|
280
281
|
});
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { Say, Field } from "./action-item.ts";
|
|
|
13
13
|
export { Logger, getConsoleLogger, getDefaultLogger } from "./logging.ts";
|
|
14
14
|
export { Agent, CallInfo } from "./agent.ts";
|
|
15
15
|
export { SIPCallInfo } from "./socket/call-info.ts";
|
|
16
|
+
export { SipUri } from "./sip.ts";
|
|
16
17
|
export { Call, DEFAULT_REACH_PERSON_OUTCOMES } from "./call.ts";
|
|
17
18
|
export type { ReachPersonOutcome } from "./call.ts";
|
|
18
19
|
export type { SpeedPreset, Language } from "./commands.ts";
|
package/src/sip.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Characters allowed unescaped in each component beyond RFC 3261's `unreserved`,
|
|
2
|
+
// which encodeURIComponent already leaves alone. See RFC 3261 section 25.1.
|
|
3
|
+
const USER_SAFE = "&=+$,;?/";
|
|
4
|
+
const PARAM_SAFE = "[]/:&+$";
|
|
5
|
+
const HEADER_SAFE = "[]/?:+$";
|
|
6
|
+
|
|
7
|
+
function sipEscape(value: string, safe: string): string {
|
|
8
|
+
return encodeURIComponent(value).replace(/%[0-9A-F]{2}/g, (escaped) => {
|
|
9
|
+
// Not decodeURIComponent, which throws on single bytes of multibyte UTF-8 sequences.
|
|
10
|
+
const char = String.fromCharCode(Number.parseInt(escaped.slice(1), 16));
|
|
11
|
+
return safe.includes(char) ? char : escaped;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class SipUri {
|
|
16
|
+
host: string;
|
|
17
|
+
user?: string;
|
|
18
|
+
/** A `null` value renders as a valueless param, e.g. `;lr`. */
|
|
19
|
+
uriParams: Record<string, string | null>;
|
|
20
|
+
/** A `null` value renders as a valueless header, e.g. `?X-Foo=`. */
|
|
21
|
+
headers: Record<string, string | null>;
|
|
22
|
+
|
|
23
|
+
constructor(args: {
|
|
24
|
+
host: string;
|
|
25
|
+
user?: string;
|
|
26
|
+
uriParams?: Record<string, string | null>;
|
|
27
|
+
headers?: Record<string, string | null>;
|
|
28
|
+
}) {
|
|
29
|
+
this.host = args.host;
|
|
30
|
+
this.user = args.user;
|
|
31
|
+
this.uriParams = args.uriParams ?? {};
|
|
32
|
+
this.headers = args.headers ?? {};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
toString(): string {
|
|
36
|
+
let url = "sip:";
|
|
37
|
+
if (this.user !== undefined) {
|
|
38
|
+
url += `${sipEscape(this.user, USER_SAFE)}@`;
|
|
39
|
+
}
|
|
40
|
+
url += this.host;
|
|
41
|
+
|
|
42
|
+
for (const [name, value] of Object.entries(this.uriParams)) {
|
|
43
|
+
url += `;${sipEscape(name, PARAM_SAFE)}`;
|
|
44
|
+
if (value !== null) {
|
|
45
|
+
url += `=${sipEscape(value, PARAM_SAFE)}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Headers always need an "=", so a null value renders as "name=".
|
|
50
|
+
const headers = Object.entries(this.headers);
|
|
51
|
+
if (headers.length > 0) {
|
|
52
|
+
url += `?${headers
|
|
53
|
+
.map(
|
|
54
|
+
([name, value]) =>
|
|
55
|
+
`${sipEscape(name, HEADER_SAFE)}=${sipEscape(value === null ? "" : String(value), HEADER_SAFE)}`,
|
|
56
|
+
)
|
|
57
|
+
.join("&")}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return url;
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/socket/call-info.ts
CHANGED
|
@@ -11,6 +11,7 @@ export type PSTNCallInfo = z.infer<typeof PSTNCallInfo>;
|
|
|
11
11
|
export const WebRTCCallInfo = z.object({
|
|
12
12
|
call_type: z.literal("webrtc"),
|
|
13
13
|
webrtc_code: z.string(),
|
|
14
|
+
custom_payload: z.record(z.string(), z.unknown()).default({}),
|
|
14
15
|
});
|
|
15
16
|
export type WebRTCCallInfo = z.infer<typeof WebRTCCallInfo>;
|
|
16
17
|
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.
|
|
1
|
+
export const SDK_VERSION = "0.39.0";
|