@guava-ai/guava-sdk 0.3.0 → 0.4.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.
Files changed (57) hide show
  1. package/bin/example-runner.js +16 -7
  2. package/dist/examples/credit-card-activation.js +94 -112
  3. package/dist/examples/credit-card-activation.js.map +1 -1
  4. package/dist/examples/property-insurance.js +12 -26
  5. package/dist/examples/property-insurance.js.map +1 -1
  6. package/dist/examples/scheduling-outbound.d.ts +1 -0
  7. package/dist/examples/scheduling-outbound.js +62 -0
  8. package/dist/examples/scheduling-outbound.js.map +1 -0
  9. package/dist/examples/thai-palace.js +25 -43
  10. package/dist/examples/thai-palace.js.map +1 -1
  11. package/dist/package.json +7 -5
  12. package/dist/src/action_item.d.ts +34 -12
  13. package/dist/src/action_item.js +34 -7
  14. package/dist/src/action_item.js.map +1 -1
  15. package/dist/src/call-controller.d.ts +137 -0
  16. package/dist/src/call-controller.js +433 -0
  17. package/dist/src/call-controller.js.map +1 -0
  18. package/dist/src/commands.d.ts +67 -27
  19. package/dist/src/commands.js +41 -27
  20. package/dist/src/commands.js.map +1 -1
  21. package/dist/src/events.d.ts +47 -30
  22. package/dist/src/events.js +42 -36
  23. package/dist/src/events.js.map +1 -1
  24. package/dist/src/example_data.d.ts +1 -0
  25. package/dist/src/example_data.js +33 -0
  26. package/dist/src/example_data.js.map +1 -1
  27. package/dist/src/helpers/openai.d.ts +12 -1
  28. package/dist/src/helpers/openai.js +168 -68
  29. package/dist/src/helpers/openai.js.map +1 -1
  30. package/dist/src/index.d.ts +6 -121
  31. package/dist/src/index.js +249 -483
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/logging.d.ts +2 -1
  34. package/dist/src/logging.js +32 -7
  35. package/dist/src/logging.js.map +1 -1
  36. package/dist/src/telemetry.d.ts +23 -0
  37. package/dist/src/telemetry.js +98 -0
  38. package/dist/src/telemetry.js.map +1 -0
  39. package/dist/src/utils.d.ts +3 -0
  40. package/dist/src/utils.js +28 -0
  41. package/dist/src/utils.js.map +1 -0
  42. package/examples/biome.json +5 -0
  43. package/examples/credit-card-activation.ts +20 -26
  44. package/examples/property-insurance.ts +6 -16
  45. package/examples/scheduling-outbound.ts +80 -0
  46. package/examples/thai-palace.ts +10 -13
  47. package/package.json +7 -5
  48. package/src/action_item.ts +53 -13
  49. package/src/call-controller.ts +451 -0
  50. package/src/commands.ts +58 -42
  51. package/src/events.ts +66 -51
  52. package/src/example_data.ts +42 -0
  53. package/src/helpers/openai.ts +73 -18
  54. package/src/index.ts +81 -403
  55. package/src/logging.ts +39 -7
  56. package/src/telemetry.ts +125 -0
  57. package/src/utils.ts +32 -0
package/dist/src/index.js CHANGED
@@ -1,447 +1,226 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
9
34
  };
10
35
  import WebSocket from "ws";
11
- import { getConsoleLogger } from "./logging.js";
12
- import { acceptInboundCallCommand, startOutboundCallCommand, setPersona, setTaskCommand, answerQuestionCommand, sendInstructionCommand, readScriptCommand, rejectInboundCallCommand, transferCommand, listenInboundCommand, inboundTunnelCommand, } from "./commands.js";
36
+ import { getDefaultLogger } from "./logging.js";
37
+ import { StartOutboundCallCommand, ListenInboundCommand, InboundTunnelCommand, } from "./commands.js";
13
38
  import * as z from "zod";
14
- import { errorEvent, sessionStartedEvent, decodeEvent, inboundTunnelEvent, } from "./events.js";
39
+ import { ErrorEvent, SessionStartedEvent, decodeEvent, InboundTunnelEvent } from "./events.js";
15
40
  import pkgdata from "../package.json" with { type: "json" };
16
41
  import os from "node:os";
17
- const DEFAULT_BASE_URL = "https://guava-dev.gridspace.com/";
18
- const DEFAULT_LOG_LEVEL = "debug";
42
+ import { getBaseUrl, fetchOrThrow } from "./utils.js";
43
+ import { telemetryClient } from "./telemetry.js";
44
+ export { CallController } from "./call-controller.js";
45
+ export { Say, Field } from "./action_item.js";
46
+ const SDK_NAME = "typescript-sdk";
47
+ let firstClient = false;
19
48
  /**
20
49
  * @description convenience function for stringifying data according to a schema
21
50
  */
22
51
  function stringifyZod(schema, data) {
23
52
  return JSON.stringify(schema.parse(data));
24
53
  }
25
- /**
26
- * Interface between Guava services and user-supplied code
27
- */
28
- export class CallController {
29
- constructor(logger) {
30
- this._commandQueue = [];
31
- this._fieldValues = {};
32
- // Set up the default logger.
33
- this.logger = logger;
34
- }
35
- /**
36
- * @description Supply a function used to consume commands from the internal command queue.
37
- *
38
- * The function is expected to remove from the argument array commands that it has handled (iterating
39
- * through the result of `Array.splice(0)` is sufficient)
40
- */
41
- setDrain(newDrain) {
42
- this._drain = newDrain;
43
- this.flush();
44
- }
45
- /**
46
- * @description [inbound] receive a call, and process further.
47
- */
48
- acceptCall() {
49
- return __awaiter(this, void 0, void 0, function* () {
50
- yield this.sendCommand(acceptInboundCallCommand, {
51
- command_type: "accept-inbound",
52
- });
53
- });
54
- }
55
- /**
56
- * @description read a span of text verbatim
57
- */
58
- readScript(script) {
59
- return __awaiter(this, void 0, void 0, function* () {
60
- yield this.sendCommand(readScriptCommand, {
61
- command_type: "read-script",
62
- script: script,
63
- });
64
- });
65
- }
66
- /**
67
- * @description [inbound] reject a call
68
- */
69
- rejectCall() {
70
- return __awaiter(this, void 0, void 0, function* () {
71
- yield this.sendCommand(rejectInboundCallCommand, {
72
- command_type: "reject-inbound",
73
- });
74
- });
75
- }
76
- addInfo(_info) {
77
- return __awaiter(this, void 0, void 0, function* () {
78
- throw new Error("not implemeneted");
79
- });
80
- }
81
- /**
82
- * @description read a span of text non-verbatim
83
- */
84
- sendInstruction(instruction) {
85
- return __awaiter(this, void 0, void 0, function* () {
86
- yield this.sendCommand(sendInstructionCommand, {
87
- command_type: "send-instruction",
88
- instruction: instruction,
89
- });
90
- });
91
- }
92
- /**
93
- * @description provide identifiers the agent will use to identify the virtual agent
94
- */
95
- setPersona(args) {
96
- return __awaiter(this, void 0, void 0, function* () {
97
- yield this.sendCommand(setPersona, {
98
- command_type: "set-persona",
99
- organization_name: args.organizationName,
100
- agent_name: args.agentName,
101
- agent_purpose: args.agentPurpose,
102
- });
103
- });
104
- }
105
- /**
106
- * @description direct the agent to collect information
107
- * @param goal {} an objective string and/or a checklist of information to collect
108
- * @param on_complete {} a callback to call once the information is available from the agent
109
- * @param args {} arguments to pass through to the `on_complete` callback
110
- */
111
- setTask(goal, on_complete = () => { }, ...args) {
112
- var _a;
113
- this._current_task_id = Math.random().toString(16).substring(2, 8);
114
- this._on_complete_current_task = on_complete.bind(this, ...args);
115
- if (!("checklist" in goal)) {
116
- this.sendCommand(setTaskCommand, {
117
- command_type: "set-task",
118
- task_id: this._current_task_id,
119
- objective: goal.objective,
120
- action_items: [],
121
- });
122
- }
123
- else {
124
- const action_items = goal.checklist.map((item) => typeof item == "string"
125
- ? {
126
- item_type: "todo",
127
- description: item,
128
- }
129
- : item);
130
- this.sendCommand(setTaskCommand, {
131
- command_type: "set-task",
132
- task_id: this._current_task_id,
133
- objective: (_a = goal.objective) !== null && _a !== void 0 ? _a : "",
134
- action_items,
135
- });
136
- }
137
- }
138
- /**
139
- * @description direct the agent to collect information, continuing execution once the agent has collected the information
140
- * @param goal {} an objective string and/or a checklist of information to collect
141
- */
142
- awaitTask(goal) {
143
- return __awaiter(this, void 0, void 0, function* () {
144
- return new Promise((resolve) => {
145
- this.setTask(goal, (_args) => {
146
- resolve();
147
- });
148
- });
149
- });
150
- }
151
- /**
152
- * @description retrieve a piece of information that the agent has collected
153
- * @param key {string} key of the field checklist item
154
- */
155
- getField(key) {
156
- if (key in this._fieldValues) {
157
- return this._fieldValues[key];
158
- }
159
- else {
160
- return null;
54
+ const http_start = /^http:\/\//;
55
+ const https_start = /^https:\/\//;
56
+ let Client = (() => {
57
+ let _classDecorators = [telemetryClient.trackClass()];
58
+ let _classDescriptor;
59
+ let _classExtraInitializers = [];
60
+ let _classThis;
61
+ var Client = class {
62
+ static { _classThis = this; }
63
+ static {
64
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
65
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
66
+ Client = _classThis = _classDescriptor.value;
67
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
68
+ __runInitializers(_classThis, _classExtraInitializers);
161
69
  }
162
- }
163
- /**
164
- * @description [inbound] hang up an accepted call
165
- */
166
- hangup() {
167
- return __awaiter(this, arguments, void 0, function* (final_instructions = "") {
168
- let instructions;
169
- if (final_instructions) {
170
- instructions = `Start ending the conversation. Here are your final instructions: ${final_instructions} Once you've completed the final instructions, naturally end the conversation and hang up the call.`;
70
+ _apiKey;
71
+ _baseUrl;
72
+ _logger;
73
+ _ws;
74
+ _controller;
75
+ messageHandler;
76
+ constructor(apiKey, baseUrl, logger, captureWarnings = true) {
77
+ // Set up the default logger.
78
+ if (logger) {
79
+ this._logger = logger;
171
80
  }
172
81
  else {
173
- instructions = "Naturally end the conversation and hang up the call.";
82
+ this._logger = getDefaultLogger();
174
83
  }
175
- this.sendInstruction(instructions);
176
- });
177
- }
178
- /**
179
- * @description transfer an accepted call
180
- */
181
- transfer(to_number, transfer_message) {
182
- const message = transfer_message !== null && transfer_message !== void 0 ? transfer_message : "I'm transferring you now";
183
- this.sendCommand(transferCommand, {
184
- command_type: "transfer-call",
185
- transfer_message: message,
186
- to_number: to_number,
187
- });
188
- }
189
- sendCommand(schema, data) {
190
- return __awaiter(this, void 0, void 0, function* () {
191
- const command = schema.parse(data);
192
- this._commandQueue.push(command);
193
- this.logger.debug(`Command queued: ${JSON.stringify(command)}`);
194
- yield this.flush();
195
- });
196
- }
197
- flush() {
198
- return __awaiter(this, void 0, void 0, function* () {
199
- var _a;
200
- yield ((_a = this._drain) === null || _a === void 0 ? void 0 : _a.call(this, this._commandQueue));
201
- });
202
- }
203
- onEvent(event) {
204
- return __awaiter(this, void 0, void 0, function* () {
205
- if (event.event_type == "caller-speech") {
206
- this.onCallerSpeech(event);
84
+ // Resolve the API base URL.
85
+ if (baseUrl) {
86
+ this._baseUrl = baseUrl;
207
87
  }
208
- else if (event.event_type == "agent-speech") {
209
- this.onAgentSpeech(event);
88
+ else {
89
+ this._baseUrl = getBaseUrl();
210
90
  }
211
- else if (event.event_type == "agent-question") {
212
- try {
213
- this.logger.info(`Received question from bot: ${event.question}`);
214
- const answer = yield this.onQuestion(event.question);
215
- yield this.sendCommand(answerQuestionCommand, {
216
- command_type: "answer-question",
217
- question_id: event.question_id,
218
- answer: answer,
219
- });
220
- }
221
- catch (e) {
222
- this.logger.error("Error occured while answering question.");
223
- yield this.sendCommand(answerQuestionCommand, {
224
- command_type: "answer-question",
225
- question_id: event.question_id,
226
- answer: "An error occured and the question could not be answered.",
227
- });
228
- }
91
+ // Resolve the API key.
92
+ if (apiKey) {
93
+ this._apiKey = apiKey;
229
94
  }
230
- else if (event.event_type == "intent") {
231
- this.logger.info(`Received intent ${event.intent_id} from bot: ${event.intent_summary}`);
232
- const intent_response = yield this.onIntent(event.intent_summary);
233
- if (intent_response) {
234
- const response_str = `Responding to intent ${event.intent_id}: ${intent_response}`;
235
- this.logger.info(response_str);
236
- this.sendInstruction(intent_response);
237
- }
95
+ else if (process.env.GUAVA_API_KEY) {
96
+ this._apiKey = process.env.GUAVA_API_KEY;
238
97
  }
239
- else if (event.event_type == "task-done") {
240
- // ignore obsolete task_completed events
241
- if (event.task_id == this._current_task_id) {
242
- // assertion is implied
243
- const on_complete = this._on_complete_current_task;
244
- this._on_complete_current_task = undefined;
245
- if (on_complete) {
246
- on_complete();
247
- }
248
- }
98
+ else {
99
+ throw new Error("Guava API key must be provided either as argument to client constructor, or in environment variable GUAVA_API_KEY.");
249
100
  }
250
- else if (event.event_type == "action-item-done") {
251
- this._fieldValues[event.key] = event.payload;
252
- if (event.key && event.payload) {
253
- this.logger.info(`Field ${event.key} updated with value: ${event.payload}`);
101
+ if (!firstClient) {
102
+ firstClient = true;
103
+ if (captureWarnings) {
104
+ process.on("warning", (warning) => {
105
+ this._logger.warn(warning.toString());
106
+ });
254
107
  }
108
+ telemetryClient.setSdkHeaders(this.headers());
109
+ this._checkSdkDeprecation();
255
110
  }
256
- else if (event.event_type == "inbound-call") {
257
- this.onIncomingCall(event.caller_number);
258
- }
259
- else if (event.event_type == "outbound-call-connected" ||
260
- event.event_type == "bot-session-ended") {
261
- // no-op, don't warn
111
+ }
112
+ getWebsocketBase() {
113
+ if (http_start.test(this._baseUrl)) {
114
+ return `ws://${this._baseUrl.substring("ws://".length)}`;
262
115
  }
263
- else if (event.event_type == "error") {
264
- this.logger.error(`The Guava agent reported an error: ${event.content}`);
116
+ else if (https_start.test(this._baseUrl)) {
117
+ return `wss://${this._baseUrl.substring("wss://".length)}`;
265
118
  }
266
119
  else {
267
- this.logger.warn(`Unhandled event: ${JSON.stringify(event)}`);
120
+ throw new Error(`Invalid base URL: ${this._baseUrl}}`);
268
121
  }
269
- });
270
- }
271
- // callbacks
272
- /**
273
- * @abstract
274
- * @description called when an inbound call is received. The overriding function must start
275
- * with `await super.onIncomingCall(from_number)`
276
- */
277
- onIncomingCall(from_number) {
278
- return __awaiter(this, void 0, void 0, function* () {
279
- yield this.onCallStart();
280
- });
281
- }
282
- /**
283
- * @abstract
284
- * @description called when a call is connected by the API, whether inbound or outbound
285
- */
286
- onCallStart() {
287
- return __awaiter(this, void 0, void 0, function* () { });
288
- }
289
- /**
290
- * @abstract
291
- * @description called when the caller speaks to the agent.
292
- */
293
- onCallerSpeech(event) {
294
- return __awaiter(this, void 0, void 0, function* () { });
295
- }
296
- /**
297
- * @abstract
298
- * @description called when the agent speaks to the caller.
299
- */
300
- onAgentSpeech(event) {
301
- return __awaiter(this, void 0, void 0, function* () { });
302
- }
303
- /**
304
- * @abstract
305
- * @description called when the caller expresses a task they wish to execute
306
- */
307
- onIntent(intent) {
308
- return __awaiter(this, void 0, void 0, function* () {
309
- return "Unfortunately I'm not able to help with that.";
310
- });
311
- }
312
- /**
313
- * @abstract
314
- * @description called when the agent needs to respond to a question that it doesn't know
315
- * the answer to.
316
- */
317
- onQuestion(question) {
318
- return __awaiter(this, void 0, void 0, function* () {
319
- return "I don't have an answer to that question.";
320
- });
321
- }
322
- }
323
- const http_start = /^http:\/\//;
324
- const https_start = /^https:\/\//;
325
- export class Client {
326
- constructor(apiKey, baseUrl, logger) {
327
- // Set up the default logger.
328
- if (logger) {
329
- this._logger = logger;
330
122
  }
331
- else {
332
- this._logger = getConsoleLogger(DEFAULT_LOG_LEVEL);
123
+ getHttpBase() {
124
+ return this._baseUrl;
333
125
  }
334
- // Resolve the API base URL.
335
- if (baseUrl) {
336
- this._baseUrl = baseUrl;
126
+ headers() {
127
+ return {
128
+ Authorization: `Bearer ${this._apiKey}`,
129
+ "x-guava-platform": os.platform(),
130
+ "x-guava-runtime": process.release.name,
131
+ "x-guava-runtime-version": process.version,
132
+ "x-guava-sdk": SDK_NAME,
133
+ "x-guava-sdk-version": pkgdata.version,
134
+ };
337
135
  }
338
- else if (process.env.GUAVA_BASE_URL) {
339
- this._baseUrl = process.env.GUAVA_BASE_URL;
340
- }
341
- else {
342
- this._baseUrl = DEFAULT_BASE_URL;
343
- }
344
- // Resolve the API key.
345
- if (apiKey) {
346
- this._apiKey = apiKey;
347
- }
348
- else if (process.env.GUAVA_API_KEY) {
349
- this._apiKey = process.env.GUAVA_API_KEY;
350
- }
351
- else {
352
- throw new Error("Guava API key must be provided either as argument to client constructor, or in environment variable GUAVA_API_KEY.");
353
- }
354
- }
355
- getWebsocketBase() {
356
- if (http_start.test(this._baseUrl)) {
357
- return `ws://${this._baseUrl.substring("ws://".length)}`;
358
- }
359
- else if (https_start.test(this._baseUrl)) {
360
- return `wss://${this._baseUrl.substring("wss://".length)}`;
361
- }
362
- else {
363
- throw new Error(`Invalid base URL: ${this._baseUrl}}`);
364
- }
365
- }
366
- getHttpBase() {
367
- return this._baseUrl;
368
- }
369
- headers() {
370
- return {
371
- Authorization: `Bearer ${this._apiKey}`,
372
- "x-guava-platform": os.platform(),
373
- "x-guava-runtime": process.release.name,
374
- "x-guava-runtime-version": process.version,
375
- "x-guava-sdk": "typescript-sdk",
376
- "x-guava-sdk-version": pkgdata.version,
377
- };
378
- }
379
- /**
380
- * @description use the Guava API to call out to a number
381
- */
382
- createOutbound(fromNumber, toNumber, callControllerFactory) {
383
- const url = new URL("v1/create-outbound", this.getWebsocketBase());
384
- const ws = new WebSocket(url, {
385
- headers: this.headers(),
386
- });
387
- const callController = (callControllerFactory !== null && callControllerFactory !== void 0 ? callControllerFactory : ((_) => undefined))(this._logger);
388
- ws.addEventListener("open", (_ev) => __awaiter(this, void 0, void 0, function* () {
389
- ws.send(stringifyZod(startOutboundCallCommand, {
390
- command_type: "start-outbound",
391
- to_number: toNumber,
392
- from_number: fromNumber,
393
- }));
394
- yield (callController === null || callController === void 0 ? void 0 : callController.onCallStart());
395
- }));
396
- ws.addEventListener("close", (_ev) => {
397
- // we are closing the socket, so don't trigger any other listeners
398
- ws.removeAllListeners();
399
- this._ws = undefined;
400
- this._controller = undefined;
401
- });
402
- this._ws = ws;
403
- this._controller = callController;
404
- this.replaceHandler(this.uninitializedOutbound.bind(this));
405
- // set the callController drain function to send all commands
406
- // through the websocket
407
- callController === null || callController === void 0 ? void 0 : callController.setDrain((commands) => __awaiter(this, void 0, void 0, function* () {
408
- for (const command of commands.splice(0)) {
409
- this._logger.debug(`Sending command ${JSON.stringify(command)}`);
410
- ws.send(JSON.stringify(command));
136
+ async _checkSdkDeprecation() {
137
+ this._logger.debug(`Checking deprecation for SDK ${SDK_NAME}, ${pkgdata.version}.`);
138
+ try {
139
+ const url = new URL("v1/check-sdk-deprecation", this.getHttpBase());
140
+ url.searchParams.set("sdk_name", SDK_NAME);
141
+ url.searchParams.set("sdk_version", pkgdata.version);
142
+ const response = await fetchOrThrow(url, {
143
+ method: "POST",
144
+ headers: this.headers(),
145
+ });
146
+ const body = (await response.json());
147
+ if (body.deprecation_status === "supported") {
148
+ this._logger.info("SDK version still supported.");
149
+ }
150
+ else if (body.deprecation_status === "deprecated") {
151
+ process.emitWarning("This SDK version is deprecated. Please update to a newer version of the SDK.");
152
+ }
153
+ else {
154
+ this._logger.warn("SDK deprecation status unknown.");
155
+ }
156
+ }
157
+ catch (e) {
158
+ this._logger.error("Encountered issue while checking for deprecation.");
411
159
  }
412
- }));
413
- }
414
- replaceHandler(newHandler) {
415
- var _a, _b;
416
- if (this.messageHandler) {
417
- (_a = this._ws) === null || _a === void 0 ? void 0 : _a.removeEventListener("message", this.messageHandler);
418
- }
419
- if (newHandler) {
420
- (_b = this._ws) === null || _b === void 0 ? void 0 : _b.addEventListener("message", newHandler);
421
160
  }
422
- this.messageHandler = newHandler;
423
- }
424
- // eventlistener handlers for server events
425
- // (a state machine in functions)
426
- uninitializedOutbound(ev) {
427
- // for correctness (and type correctness)
428
- if (!this._ws) {
429
- throw new Error("[internal] Uninitialized WebSocket");
161
+ /**
162
+ * @description use the Guava API to call out to a number
163
+ */
164
+ createOutbound(fromNumber, toNumber, callController) {
165
+ const url = new URL("v1/create-outbound", this.getWebsocketBase());
166
+ const ws = new WebSocket(url, {
167
+ headers: this.headers(),
168
+ });
169
+ ws.addEventListener("open", async (_ev) => {
170
+ ws.send(stringifyZod(StartOutboundCallCommand, {
171
+ command_type: "start-outbound",
172
+ to_number: toNumber,
173
+ from_number: fromNumber,
174
+ }));
175
+ // set the callController drain function to send all commands
176
+ // through the now open websocket
177
+ callController.setDrain(async (commands) => {
178
+ for (const command of commands.splice(0)) {
179
+ this._logger.debug(`Sending command ${JSON.stringify(command)}`);
180
+ ws.send(JSON.stringify(command));
181
+ }
182
+ });
183
+ await callController.onCallStart();
184
+ });
185
+ ws.addEventListener("close", (_ev) => {
186
+ // we are closing the socket, so don't trigger any other listeners
187
+ ws.removeAllListeners();
188
+ this._ws = undefined;
189
+ this._controller = undefined;
190
+ });
191
+ this._ws = ws;
192
+ this._controller = callController;
193
+ this.replaceHandler(this.uninitializedOutbound.bind(this));
430
194
  }
431
- const session_started = z
432
- .union([sessionStartedEvent, errorEvent])
433
- .parse(JSON.parse(ev.data.toString("utf8")));
434
- if (session_started.event_type == "error") {
435
- throw new Error(`Outbound call failed: ${session_started.content}`);
195
+ replaceHandler(newHandler) {
196
+ if (this.messageHandler) {
197
+ this._ws?.removeEventListener("message", this.messageHandler);
198
+ }
199
+ if (newHandler) {
200
+ this._ws?.addEventListener("message", newHandler);
201
+ }
202
+ this.messageHandler = newHandler;
436
203
  }
437
- else {
438
- this._logger.info(`Started session with ID: ${session_started.session_id}`);
439
- // move to next state
440
- this.replaceHandler(this.initializedOutbound.bind(this));
204
+ // eventlistener handlers for server events
205
+ // (a state machine in functions)
206
+ uninitializedOutbound(ev) {
207
+ // for correctness (and type correctness)
208
+ if (!this._ws) {
209
+ throw new Error("[internal] Uninitialized WebSocket");
210
+ }
211
+ const session_started = z
212
+ .union([SessionStartedEvent, ErrorEvent])
213
+ .parse(JSON.parse(ev.data.toString("utf8")));
214
+ if (session_started.event_type === "error") {
215
+ throw new Error(`Outbound call failed: ${session_started.content}`);
216
+ }
217
+ else {
218
+ this._logger.info(`Started session with ID: ${session_started.session_id}`);
219
+ // move to next state
220
+ this.replaceHandler(this.initializedOutbound.bind(this));
221
+ }
441
222
  }
442
- }
443
- initializedOutbound(ev) {
444
- return __awaiter(this, void 0, void 0, function* () {
223
+ async initializedOutbound(ev) {
445
224
  // for correctness (and type correctness)
446
225
  if (!this._ws) {
447
226
  throw new Error("[internal] Uninitialized WebSocket");
@@ -450,90 +229,77 @@ export class Client {
450
229
  const event = decodeEvent(ev.data);
451
230
  if (event) {
452
231
  if (this._controller) {
453
- yield this._controller.onEvent(event);
232
+ await this._controller.onEvent(event);
454
233
  }
455
- if (event.event_type == "outbound-call-failed" ||
456
- event.event_type == "bot-session-ended") {
234
+ if (event.event_type === "outbound-call-failed" || event.event_type === "bot-session-ended") {
457
235
  // shutdown the websocket
458
236
  this._ws.close();
459
237
  }
460
238
  }
461
- });
462
- }
463
- setInboundHandler(agent_number, public_url, inbound_token) {
464
- return __awaiter(this, void 0, void 0, function* () {
465
- const response = yield fetch(new URL(`v1/inbound-handler/${agent_number}`, this.getHttpBase()), {
466
- method: "PUT",
239
+ }
240
+ /**
241
+ * @description use the Guava API to receive calls at a given number
242
+ */
243
+ listenInbound(conn, controllerClassFactory) {
244
+ const callControllers = {};
245
+ // return a way to *stop* listening
246
+ const url = new URL("v1/listen-inbound", this.getWebsocketBase());
247
+ const ws = new WebSocket(url, {
467
248
  headers: this.headers(),
468
- body: JSON.stringify({
469
- handler_url: public_url,
470
- handler_token: inbound_token,
471
- }),
472
249
  });
473
- if (!response.ok) {
474
- throw new Error("Failed to set inbound handler");
475
- }
476
- });
477
- }
478
- /**
479
- * @description use the Guava API to receive calls at a given number
480
- */
481
- listenInbound(conn, controllerClassFactory) {
482
- const callControllers = {};
483
- // return a way to *stop* listening
484
- const url = new URL("v1/listen-inbound", this.getWebsocketBase());
485
- const ws = new WebSocket(url, {
486
- headers: this.headers(),
487
- });
488
- let agent_number;
489
- let webrtc_code;
490
- if ("agent_number" in conn) {
491
- agent_number = conn.agent_number;
492
- }
493
- else {
494
- webrtc_code = conn.webrtc_code;
495
- }
496
- this._logger.info(`Listening for calls to ${agent_number !== null && agent_number !== void 0 ? agent_number : webrtc_code}`);
497
- if (webrtc_code) {
498
- const debugurl = new URL(`debug-webrtc?webrtc_code=${webrtc_code}`, this.getHttpBase());
499
- this._logger.debug(`WebRTC DebugURL: ${debugurl}`);
500
- }
501
- ws.addEventListener("open", (_ev) => {
502
- ws.send(stringifyZod(listenInboundCommand, {
503
- command_type: "listen-inbound",
504
- agent_number: agent_number,
505
- webrtc_code: webrtc_code,
506
- }));
507
- });
508
- ws.addEventListener("close", (_ev) => {
509
- ws.removeAllListeners();
510
- });
511
- ws.addEventListener("message", (ev) => {
512
- const tunnel_event = inboundTunnelEvent.parse(JSON.parse(ev.data.toString("utf8")));
513
- if (!(tunnel_event.call_id in callControllers)) {
514
- this._logger.info(`Received tunnel event for new call ID: ${tunnel_event.call_id}. Creating call controller.`);
515
- const newController = controllerClassFactory(this._logger);
516
- newController.setDrain((commands) => __awaiter(this, void 0, void 0, function* () {
517
- for (const command of commands.splice(0)) {
518
- this._logger.debug(`Sending command: ${JSON.stringify(command)} for call ID: ${tunnel_event.call_id}`);
519
- ws.send(stringifyZod(inboundTunnelCommand, {
520
- call_id: tunnel_event.call_id,
521
- command,
522
- }));
523
- }
524
- }));
525
- callControllers[tunnel_event.call_id] = newController;
526
- newController.onEvent(tunnel_event.event);
250
+ let agent_number;
251
+ let webrtc_code;
252
+ if ("agent_number" in conn) {
253
+ agent_number = conn.agent_number;
527
254
  }
528
255
  else {
529
- // no threading, so manually forward to onEvent!
530
- callControllers[tunnel_event.call_id].onEvent(tunnel_event.event);
256
+ webrtc_code = conn.webrtc_code;
531
257
  }
532
- });
533
- return new InboundListener(ws);
534
- }
535
- }
258
+ this._logger.info(`Listening for calls to ${agent_number ?? webrtc_code}`);
259
+ if (webrtc_code) {
260
+ const debugurl = new URL(`debug-webrtc?webrtc_code=${webrtc_code}`, this.getHttpBase());
261
+ this._logger.debug(`WebRTC DebugURL: ${debugurl}`);
262
+ }
263
+ ws.addEventListener("open", (_ev) => {
264
+ ws.send(stringifyZod(ListenInboundCommand, {
265
+ command_type: "listen-inbound",
266
+ agent_number: agent_number,
267
+ webrtc_code: webrtc_code,
268
+ }));
269
+ });
270
+ ws.addEventListener("close", (_ev) => {
271
+ ws.removeAllListeners();
272
+ });
273
+ ws.addEventListener("message", (ev) => {
274
+ const tunnel_event = InboundTunnelEvent.parse(JSON.parse(ev.data.toString("utf8")));
275
+ if (!(tunnel_event.call_id in callControllers)) {
276
+ this._logger.info(`Received tunnel event for new call ID: ${tunnel_event.call_id}. Creating call controller.`);
277
+ const newController = controllerClassFactory(this._logger);
278
+ newController.setDrain(async (commands) => {
279
+ for (const command of commands.splice(0)) {
280
+ this._logger.debug(`Sending command: ${JSON.stringify(command)} for call ID: ${tunnel_event.call_id}`);
281
+ ws.send(stringifyZod(InboundTunnelCommand, {
282
+ call_id: tunnel_event.call_id,
283
+ command,
284
+ }));
285
+ }
286
+ });
287
+ callControllers[tunnel_event.call_id] = newController;
288
+ newController.onEvent(tunnel_event.event);
289
+ }
290
+ else {
291
+ // no threading, so manually forward to onEvent!
292
+ callControllers[tunnel_event.call_id].onEvent(tunnel_event.event);
293
+ }
294
+ });
295
+ return new InboundListener(ws);
296
+ }
297
+ };
298
+ return Client = _classThis;
299
+ })();
300
+ export { Client };
536
301
  class InboundListener {
302
+ ws;
537
303
  constructor(ws) {
538
304
  this.ws = ws;
539
305
  }