@guava-ai/guava-sdk 0.23.0 → 0.25.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 (91) hide show
  1. package/README.md +13 -3
  2. package/bin/example-runner.ts +138 -11
  3. package/dist/bin/example-runner.d.ts +23 -6
  4. package/dist/bin/example-runner.js +112 -9
  5. package/dist/bin/example-runner.js.map +1 -1
  6. package/dist/examples/{example.test.js → agent-testing.test.js} +2 -2
  7. package/dist/examples/agent-testing.test.js.map +1 -0
  8. package/dist/examples/help-desk.js +22 -8
  9. package/dist/examples/help-desk.js.map +1 -1
  10. package/dist/examples/multiple-agents.d.ts +1 -0
  11. package/dist/examples/multiple-agents.js +52 -0
  12. package/dist/examples/multiple-agents.js.map +1 -0
  13. package/dist/examples/property-insurance.js +14 -3
  14. package/dist/examples/property-insurance.js.map +1 -1
  15. package/dist/examples/restaurant-waitlist.js +9 -1
  16. package/dist/examples/restaurant-waitlist.js.map +1 -1
  17. package/dist/examples/scheduling-outbound.js +5 -6
  18. package/dist/examples/scheduling-outbound.js.map +1 -1
  19. package/dist/src/action-item.d.ts +4 -4
  20. package/dist/src/action-item.js +1 -1
  21. package/dist/src/action-item.js.map +1 -1
  22. package/dist/src/agent.d.ts +31 -11
  23. package/dist/src/agent.js +278 -135
  24. package/dist/src/agent.js.map +1 -1
  25. package/dist/src/call.d.ts +12 -1
  26. package/dist/src/call.js +87 -24
  27. package/dist/src/call.js.map +1 -1
  28. package/dist/src/client.d.ts +3 -0
  29. package/dist/src/client.js +6 -0
  30. package/dist/src/client.js.map +1 -1
  31. package/dist/src/commands.d.ts +37 -9
  32. package/dist/src/commands.js +13 -3
  33. package/dist/src/commands.js.map +1 -1
  34. package/dist/src/events.d.ts +28 -1
  35. package/dist/src/events.js +10 -2
  36. package/dist/src/events.js.map +1 -1
  37. package/dist/src/example-utils.d.ts +2 -0
  38. package/dist/src/example-utils.js +115 -0
  39. package/dist/src/example-utils.js.map +1 -0
  40. package/dist/src/health.d.ts +35 -0
  41. package/dist/src/health.js +99 -0
  42. package/dist/src/health.js.map +1 -0
  43. package/dist/src/helpers/index.d.ts +1 -0
  44. package/dist/src/helpers/index.js +9 -0
  45. package/dist/src/helpers/index.js.map +1 -0
  46. package/dist/src/helpers/llm.d.ts +43 -1
  47. package/dist/src/helpers/llm.js +247 -0
  48. package/dist/src/helpers/llm.js.map +1 -1
  49. package/dist/src/helpers/server-rag.d.ts +25 -0
  50. package/dist/src/helpers/server-rag.js +119 -0
  51. package/dist/src/helpers/server-rag.js.map +1 -0
  52. package/dist/src/index.d.ts +3 -1
  53. package/dist/src/index.js +4 -1
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/runner.d.ts +10 -0
  56. package/dist/src/runner.js +101 -0
  57. package/dist/src/runner.js.map +1 -0
  58. package/dist/src/socket/protocol.d.ts +4 -4
  59. package/dist/src/testing/mocks.d.ts +6 -0
  60. package/dist/src/testing/mocks.js +12 -0
  61. package/dist/src/testing/mocks.js.map +1 -1
  62. package/dist/src/testing/session.d.ts +7 -0
  63. package/dist/src/testing/session.js +3 -22
  64. package/dist/src/testing/session.js.map +1 -1
  65. package/dist/src/version.d.ts +1 -1
  66. package/dist/src/version.js +1 -1
  67. package/examples/{example.test.ts → agent-testing.test.ts} +1 -1
  68. package/examples/help-desk.ts +21 -12
  69. package/examples/multiple-agents.ts +18 -0
  70. package/examples/property-insurance.ts +15 -3
  71. package/examples/restaurant-waitlist.ts +10 -1
  72. package/examples/scheduling-outbound.ts +5 -6
  73. package/package.json +9 -1
  74. package/src/action-item.ts +1 -1
  75. package/src/agent.ts +273 -147
  76. package/src/call.ts +115 -24
  77. package/src/client.ts +7 -0
  78. package/src/commands.ts +16 -2
  79. package/src/events.ts +11 -1
  80. package/src/example-utils.ts +87 -0
  81. package/src/health.ts +120 -0
  82. package/src/helpers/index.ts +2 -0
  83. package/src/helpers/llm.ts +170 -1
  84. package/src/helpers/server-rag.ts +132 -0
  85. package/src/index.ts +3 -1
  86. package/src/runner.ts +39 -0
  87. package/src/testing/mocks.ts +17 -0
  88. package/src/testing/session.ts +2 -22
  89. package/src/version.ts +1 -1
  90. package/dist/examples/example.test.js.map +0 -1
  91. /package/dist/examples/{example.test.d.ts → agent-testing.test.d.ts} +0 -0
package/dist/src/agent.js CHANGED
@@ -122,22 +122,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
122
122
  return (mod && mod.__esModule) ? mod : { "default": mod };
123
123
  };
124
124
  Object.defineProperty(exports, "__esModule", { value: true });
125
- exports.Agent = void 0;
125
+ exports.Agent = exports._roleplayActionSchema = void 0;
126
126
  const ws_1 = __importDefault(require("ws"));
127
- const index_ts_1 = require("./index.js");
128
- const logging_ts_1 = require("./logging.js");
129
- const utils_ts_1 = require("./utils.js");
130
- const webrtc_helper_ts_1 = require("./webrtc-helper.js");
131
127
  const commands_ts_1 = require("./commands.js");
132
128
  const events_ts_1 = require("./events.js");
133
- const telemetry_ts_1 = require("./telemetry.js");
134
- const client_ts_1 = require("./socket/client.js");
135
129
  const guavadialer_events_ts_1 = require("./guavadialer-events.js");
130
+ const health_ts_1 = require("./health.js");
131
+ const llm_ts_1 = require("./helpers/llm.js");
132
+ const index_ts_1 = require("./index.js");
133
+ const logging_ts_1 = require("./logging.js");
134
+ const client_ts_1 = require("./socket/client.js");
136
135
  const ListenInbound = __importStar(require("./socket/listen-inbound.js"));
137
- const session_ts_1 = require("./testing/session.js");
138
- const protocol_ts_1 = require("./testing/protocol.js");
136
+ const telemetry_ts_1 = require("./telemetry.js");
139
137
  const chat_ts_1 = require("./testing/chat.js");
140
- const llm_ts_1 = require("./helpers/llm.js");
138
+ const protocol_ts_1 = require("./testing/protocol.js");
139
+ const session_ts_1 = require("./testing/session.js");
140
+ const z = __importStar(require("zod"));
141
+ const utils_ts_1 = require("./utils.js");
142
+ const webrtc_helper_ts_1 = require("./webrtc-helper.js");
143
+ exports._roleplayActionSchema = z.object({
144
+ action: z.enum(["speak", "hangup"]),
145
+ utterance: z.string().optional(),
146
+ });
141
147
  let Agent = (() => {
142
148
  let _classDecorators = [telemetry_ts_1.telemetryClient.trackClass()];
143
149
  let _classDescriptor;
@@ -155,6 +161,8 @@ let Agent = (() => {
155
161
  _name;
156
162
  _organization;
157
163
  _purpose;
164
+ _voice;
165
+ _acceptDtmfForNumbers;
158
166
  _logger;
159
167
  _client = new index_ts_1.Client();
160
168
  _onCallReceived = async () => ({
@@ -170,12 +178,16 @@ let Agent = (() => {
170
178
  _onActionRequested;
171
179
  _onActionGeneric;
172
180
  _onActionHandlers = {};
181
+ _onValidateHandlers = {};
173
182
  _onSessionEnd;
183
+ _onEscalate;
174
184
  _onDtmf;
175
185
  constructor(args) {
176
186
  this._name = args?.name;
177
187
  this._organization = args?.organization;
178
188
  this._purpose = args?.purpose;
189
+ this._voice = args?.voice;
190
+ this._acceptDtmfForNumbers = args?.acceptDtmf ?? true;
179
191
  this._logger = (0, logging_ts_1.getDefaultLogger)();
180
192
  }
181
193
  onCallReceived(callback) {
@@ -206,6 +218,9 @@ let Agent = (() => {
206
218
  this._onTaskCompleteGeneric = callbackOrTaskName;
207
219
  }
208
220
  }
221
+ onValidate(fieldKey, callback) {
222
+ this._onValidateHandlers[fieldKey] = callback;
223
+ }
209
224
  onSearchQuery(fieldKey, callback) {
210
225
  this._searchQueryHandlers[fieldKey] = callback;
211
226
  }
@@ -228,6 +243,9 @@ let Agent = (() => {
228
243
  onSessionEnd(callback) {
229
244
  this._onSessionEnd = callback;
230
245
  }
246
+ onEscalate(callback) {
247
+ this._onEscalate = callback;
248
+ }
231
249
  onDtmf(callback) {
232
250
  this._onDtmf = callback;
233
251
  }
@@ -292,18 +310,64 @@ let Agent = (() => {
292
310
  });
293
311
  }
294
312
  async listenPhone(phoneNumber) {
295
- return this._listenInbound({ agent_number: phoneNumber });
313
+ const env_1 = { stack: [], error: void 0, hasError: false };
314
+ try {
315
+ const healthCtx = new health_ts_1.HealthContext();
316
+ const _server = __addDisposableResource(env_1, await (0, health_ts_1.getHealthServer)(healthCtx), true);
317
+ return this._listenInbound(healthCtx, { agent_number: phoneNumber });
318
+ }
319
+ catch (e_1) {
320
+ env_1.error = e_1;
321
+ env_1.hasError = true;
322
+ }
323
+ finally {
324
+ const result_1 = __disposeResources(env_1);
325
+ if (result_1)
326
+ await result_1;
327
+ }
296
328
  }
297
329
  async listenWebrtc(webrtcCode) {
298
- if (!webrtcCode) {
299
- this._logger.info("No WebRTC code provided. Creating a temporary one.");
300
- webrtcCode = await this._client.createWebrtcAgent(3600);
330
+ const env_2 = { stack: [], error: void 0, hasError: false };
331
+ try {
332
+ if (!webrtcCode) {
333
+ this._logger.info("No WebRTC code provided. Creating a temporary one.");
334
+ webrtcCode = await this._client.createWebrtcAgent(3600);
335
+ }
336
+ const healthCtx = new health_ts_1.HealthContext();
337
+ const _server = __addDisposableResource(env_2, await (0, health_ts_1.getHealthServer)(healthCtx), true);
338
+ return this._listenInbound(healthCtx, { webrtc_code: webrtcCode });
339
+ }
340
+ catch (e_2) {
341
+ env_2.error = e_2;
342
+ env_2.hasError = true;
343
+ }
344
+ finally {
345
+ const result_2 = __disposeResources(env_2);
346
+ if (result_2)
347
+ await result_2;
348
+ }
349
+ }
350
+ async listenSip(sipCode) {
351
+ const env_3 = { stack: [], error: void 0, hasError: false };
352
+ try {
353
+ const healthCtx = new health_ts_1.HealthContext();
354
+ const _server = __addDisposableResource(env_3, await (0, health_ts_1.getHealthServer)(healthCtx), true);
355
+ return this._listenInbound(healthCtx, { sip_code: sipCode });
356
+ }
357
+ catch (e_3) {
358
+ env_3.error = e_3;
359
+ env_3.hasError = true;
360
+ }
361
+ finally {
362
+ const result_3 = __disposeResources(env_3);
363
+ if (result_3)
364
+ await result_3;
301
365
  }
302
- return this._listenInbound({ webrtc_code: webrtcCode });
303
366
  }
304
367
  async callLocal() {
305
368
  const webrtcCode = await this._client.createWebrtcAgent(300);
306
- this._listenInbound({ webrtc_code: webrtcCode }).catch((err) => {
369
+ // No health-server for call_local.
370
+ this._listenInbound(new health_ts_1.HealthContext(), { webrtc_code: webrtcCode }).catch((err) => {
307
371
  this._logger.error("Listen loop error: %s", err);
308
372
  });
309
373
  await (0, webrtc_helper_ts_1.runWebrtcHelper)(webrtcCode, (0, utils_ts_1.getBaseUrl)());
@@ -320,15 +384,31 @@ let Agent = (() => {
320
384
  }
321
385
  }
322
386
  else if (event.event_type === "task-done") {
323
- this._logger.info(`Task ${event.task_id} completed.`);
324
- if (this._onTaskCompleteGeneric !== undefined) {
325
- await this._onTaskCompleteGeneric(call, event.task_id);
387
+ const errors = [];
388
+ for (const fieldKey of call._fieldKeysByTaskId[event.task_id] ?? []) {
389
+ const handler = this._onValidateHandlers[fieldKey];
390
+ if (handler) {
391
+ const result = await handler(call, await call.getField(fieldKey));
392
+ if (result !== true) {
393
+ const [, error] = result;
394
+ errors.push(error);
395
+ }
396
+ }
326
397
  }
327
- else if (event.task_id in this._onTaskCompleteHandlers) {
328
- await this._onTaskCompleteHandlers[event.task_id](call);
398
+ if (errors.length > 0) {
399
+ await call.retryTask(errors.join(" "));
329
400
  }
330
401
  else {
331
- this._logger.warn(`No handler registered for completion of task '${event.task_id}'`);
402
+ this._logger.info(`Task ${event.task_id} completed.`);
403
+ if (this._onTaskCompleteGeneric !== undefined) {
404
+ await this._onTaskCompleteGeneric(call, event.task_id);
405
+ }
406
+ else if (event.task_id in this._onTaskCompleteHandlers) {
407
+ await this._onTaskCompleteHandlers[event.task_id](call);
408
+ }
409
+ else {
410
+ this._logger.warn(`No handler registered for completion of task '${event.task_id}'`);
411
+ }
332
412
  }
333
413
  }
334
414
  else if (event.event_type === "agent-question") {
@@ -379,15 +459,20 @@ let Agent = (() => {
379
459
  }
380
460
  else if (event.event_type === "action-request") {
381
461
  this._logger.info(`Received action request ${event.intent_id}: ${event.intent_summary}`);
382
- let suggestion;
462
+ let suggestions = [];
383
463
  if (this._onActionRequested !== undefined) {
384
- suggestion = await this._onActionRequested(call, event.intent_summary);
464
+ const result = await this._onActionRequested(call, event.intent_summary);
465
+ if (Array.isArray(result)) {
466
+ suggestions = result;
467
+ }
468
+ else if (result !== null && result !== undefined) {
469
+ suggestions = [result];
470
+ }
385
471
  }
386
472
  await call.sendCommand(commands_ts_1.ActionSuggestionCommand, {
387
473
  command_type: "action-suggestion",
388
474
  intent_id: event.intent_id,
389
- action_key: suggestion?.key ?? null,
390
- action_description: suggestion?.description ?? "",
475
+ actions: suggestions.map((s) => ({ key: s.key, description: s.description ?? "" })),
391
476
  });
392
477
  }
393
478
  else if (event.event_type === "execute-action") {
@@ -421,6 +506,17 @@ let Agent = (() => {
421
506
  await this._onDtmf(call, event);
422
507
  }
423
508
  }
509
+ else if (event.event_type === "escalate") {
510
+ if (this._onEscalate !== undefined) {
511
+ await this._onEscalate(call, event);
512
+ }
513
+ else if (event.requested_by === "agent") {
514
+ await call.sendInstruction("No escalation target set. Apologize for not being able to help, ask them to try calling another time, and hang up the call immediately.");
515
+ }
516
+ else {
517
+ await call.sendInstruction("Let them know there are no representatives available to take their call. Ask them if they would prefer to continue or to call another time.");
518
+ }
519
+ }
424
520
  else if (event.event_type === "error") {
425
521
  this._logger.error(`The Guava agent reported an error: ${event.content}`);
426
522
  }
@@ -428,30 +524,33 @@ let Agent = (() => {
428
524
  this._logger.warn(`The Guava agent reported a warning: ${event.content}`);
429
525
  }
430
526
  }
431
- async _initCall(variables = {}) {
432
- const call = new index_ts_1.Call(variables);
527
+ async _initCall(callId, callInfo, variables = {}) {
528
+ const call = new index_ts_1.Call(callId, callInfo, variables);
433
529
  await call.setPersona({
434
530
  agentName: this._name,
435
531
  agentPurpose: this._purpose,
436
532
  organizationName: this._organization,
533
+ voice: this._voice,
437
534
  });
438
535
  await call.sendCommand(commands_ts_1.RegisteredHooksCommand, {
439
536
  command_type: "registered-hooks",
440
537
  has_on_question: this._onQuestion !== undefined,
441
538
  has_on_intent: false,
442
539
  has_on_action_requested: this._onActionRequested !== undefined,
540
+ has_on_escalate: this._onEscalate !== undefined,
541
+ accept_dtmf_for_numbers: this._acceptDtmfForNumbers,
443
542
  });
444
543
  if (this._onCallStart !== undefined) {
445
544
  await this._onCallStart(call);
446
545
  }
447
546
  return call;
448
547
  }
449
- async _attachToCall(callId, initialVariables = {}, testSession, route = "v2/connect-call") {
450
- const env_1 = { stack: [], error: void 0, hasError: false };
548
+ async _attachToCall(callId, callInfo, initialVariables = {}, testSession, route = "v2/connect-call") {
549
+ const env_4 = { stack: [], error: void 0, hasError: false };
451
550
  try {
452
- const call = await this._initCall(initialVariables);
551
+ const call = await this._initCall(callId, callInfo, initialVariables);
453
552
  const url = new URL(`${route}/${callId}`, this._client.getWebsocketBase());
454
- const socket = __addDisposableResource(env_1, await new client_ts_1.GuavaSocket(`call-connection-${callId}`, url.toString(), this._client, (cmd) => cmd, (payload) => (0, events_ts_1.decodeEventDict)(payload), 18000).connect(), true);
553
+ const socket = __addDisposableResource(env_4, await new client_ts_1.GuavaSocket(`call-connection-${callId}`, url.toString(), this._client, (cmd) => cmd, (payload) => (0, events_ts_1.decodeEventDict)(payload), 18000).connect(), true);
455
554
  await call.setDrain(async (commands) => {
456
555
  for (const command of commands.splice(0)) {
457
556
  socket.send(command);
@@ -473,67 +572,79 @@ let Agent = (() => {
473
572
  throw e;
474
573
  }
475
574
  }
476
- catch (e_1) {
477
- env_1.error = e_1;
478
- env_1.hasError = true;
575
+ catch (e_4) {
576
+ env_4.error = e_4;
577
+ env_4.hasError = true;
479
578
  }
480
579
  finally {
481
- const result_1 = __disposeResources(env_1);
482
- if (result_1)
483
- await result_1;
580
+ const result_4 = __disposeResources(env_4);
581
+ if (result_4)
582
+ await result_4;
484
583
  }
485
584
  }
486
- async _listenInbound(conn) {
487
- const env_2 = { stack: [], error: void 0, hasError: false };
585
+ async _listenInbound(healthCtx, conn) {
586
+ const url = new URL("v2/listen-inbound", this._client.getWebsocketBase());
587
+ if ("agent_number" in conn) {
588
+ url.searchParams.set("phone_number", conn.agent_number);
589
+ }
590
+ else if ("webrtc_code" in conn) {
591
+ url.searchParams.set("webrtc_code", conn.webrtc_code);
592
+ }
593
+ else {
594
+ url.searchParams.set("sip_code", conn.sip_code);
595
+ }
488
596
  try {
489
- const url = new URL("v2/listen-inbound", this._client.getWebsocketBase());
490
- if ("agent_number" in conn) {
491
- url.searchParams.set("phone_number", conn.agent_number);
492
- }
493
- else {
494
- url.searchParams.set("webrtc_code", conn.webrtc_code);
495
- }
496
- const socket = __addDisposableResource(env_2, await new client_ts_1.GuavaSocket("listen-inbound", url.toString(), this._client, (msg) => msg, ListenInbound.decodeServerMessage).connect(), true);
597
+ const env_5 = { stack: [], error: void 0, hasError: false };
497
598
  try {
498
- for await (const msg of socket) {
499
- switch (msg.message_type) {
500
- case "listen-started":
501
- if ("agent_number" in conn) {
502
- this._logger.info("Listening on %s (%d other listeners).", conn.agent_number, msg.other_listeners);
503
- }
504
- else {
505
- this._logger.info("Listening on WebRTC code %s (%d other listeners).", conn.webrtc_code, msg.other_listeners);
506
- const debugUrl = new URL(`debug-webrtc?webrtc_code=${conn.webrtc_code}`, this._client.getHttpBase());
507
- this._logger.info("Call your agent at: %s", debugUrl);
599
+ const socket = __addDisposableResource(env_5, await new client_ts_1.GuavaSocket("listen-inbound", url.toString(), this._client, (msg) => msg, ListenInbound.decodeServerMessage).connect(), true);
600
+ try {
601
+ for await (const msg of socket) {
602
+ switch (msg.message_type) {
603
+ case "listen-started":
604
+ healthCtx.ready();
605
+ if ("agent_number" in conn) {
606
+ this._logger.info("Listening on %s (%d other listeners).", conn.agent_number, msg.other_listeners);
607
+ }
608
+ else if ("webrtc_code" in conn) {
609
+ this._logger.info("Listening on WebRTC code %s (%d other listeners).", conn.webrtc_code, msg.other_listeners);
610
+ const debugUrl = new URL(`debug-webrtc?webrtc_code=${conn.webrtc_code}`, this._client.getHttpBase());
611
+ this._logger.info("Call your agent at: %s", debugUrl);
612
+ }
613
+ else {
614
+ this._logger.info("Listening on SIP code %s (%d other listeners).", conn.sip_code, msg.other_listeners);
615
+ }
616
+ break;
617
+ case "incoming-call":
618
+ socket.send({ message_type: "claim-call", call_id: msg.call_id });
619
+ break;
620
+ case "assign-call": {
621
+ const { call_id, call_info } = msg;
622
+ this._logger.info("Received call (session ID: %s)", call_id);
623
+ this._handleAssignedCall(call_id, call_info, socket).catch((err) => {
624
+ this._logger.error("Error handling assigned call %s: %s", call_id, err);
625
+ });
626
+ break;
508
627
  }
509
- break;
510
- case "incoming-call":
511
- socket.send({ message_type: "claim-call", call_id: msg.call_id });
512
- break;
513
- case "assign-call": {
514
- const { call_id, call_info } = msg;
515
- this._logger.info("Received call (session ID: %s)", call_id);
516
- this._handleAssignedCall(call_id, call_info, socket).catch((err) => {
517
- this._logger.error("Error handling assigned call %s: %s", call_id, err);
518
- });
519
- break;
520
628
  }
521
629
  }
522
630
  }
631
+ catch (e) {
632
+ if (!(e instanceof client_ts_1.GuavaSocketClosedError))
633
+ throw e;
634
+ }
523
635
  }
524
- catch (e) {
525
- if (!(e instanceof client_ts_1.GuavaSocketClosedError))
526
- throw e;
636
+ catch (e_5) {
637
+ env_5.error = e_5;
638
+ env_5.hasError = true;
639
+ }
640
+ finally {
641
+ const result_5 = __disposeResources(env_5);
642
+ if (result_5)
643
+ await result_5;
527
644
  }
528
- }
529
- catch (e_2) {
530
- env_2.error = e_2;
531
- env_2.hasError = true;
532
645
  }
533
646
  finally {
534
- const result_2 = __disposeResources(env_2);
535
- if (result_2)
536
- await result_2;
647
+ healthCtx.stopped();
537
648
  }
538
649
  }
539
650
  async _handleAssignedCall(callId, callInfo, socket, initialVariables = {}) {
@@ -545,7 +656,7 @@ let Agent = (() => {
545
656
  else {
546
657
  this._logger.info("Accepting call %s.", callId);
547
658
  socket.send({ message_type: "answer-call", call_id: callId });
548
- await this._attachToCall(callId, initialVariables);
659
+ await this._attachToCall(callId, callInfo, initialVariables);
549
660
  }
550
661
  }
551
662
  /**
@@ -553,8 +664,7 @@ let Agent = (() => {
553
664
  */
554
665
  async callPhone(fromNumber, toNumber, variables = {}) {
555
666
  const url = new URL("v2/create-outbound", this._client.getHttpBase());
556
- if (fromNumber)
557
- url.searchParams.set("from_number", fromNumber);
667
+ url.searchParams.set("from_number", fromNumber);
558
668
  url.searchParams.set("to_number", toNumber);
559
669
  const response = await (0, utils_ts_1.fetchOrThrow)(url, {
560
670
  method: "POST",
@@ -562,7 +672,13 @@ let Agent = (() => {
562
672
  });
563
673
  const { call_id } = (await response.json());
564
674
  this._logger.info("Outbound call created with session ID: %s", call_id);
565
- await this._attachToCall(call_id, variables);
675
+ const callInfo = {
676
+ call_type: "pstn",
677
+ from_number: fromNumber ?? null,
678
+ to_number: toNumber,
679
+ caller_id: null,
680
+ };
681
+ await this._attachToCall(call_id, callInfo, variables);
566
682
  }
567
683
  /**
568
684
  * Run the agent against a live test session.
@@ -594,7 +710,13 @@ let Agent = (() => {
594
710
  });
595
711
  const sessionStarted = protocol_ts_1.SessionStarted.parse(JSON.parse(rawFirst));
596
712
  const testSession = new session_ts_1.TestSession(ws, this._client);
597
- const attachPromise = this._attachToCall(sessionStarted.session_id, variables, testSession).catch((err) => {
713
+ const testCallInfo = {
714
+ call_type: "pstn",
715
+ from_number: null,
716
+ to_number: "+15555555555",
717
+ caller_id: null,
718
+ };
719
+ const attachPromise = this._attachToCall(sessionStarted.session_id, testCallInfo, variables, testSession).catch((err) => {
598
720
  this._logger.error("Error in _attachToCall during test: %s", err);
599
721
  });
600
722
  try {
@@ -626,15 +748,6 @@ let Agent = (() => {
626
748
  * assert(session.executedActions.includes("sales"));
627
749
  */
628
750
  async roleplay(roleplayPrompt, variables = {}) {
629
- const roleplaySchema = {
630
- type: "object",
631
- properties: {
632
- action: { type: "string", enum: ["speak", "hangup"] },
633
- utterance: { type: "string" },
634
- },
635
- required: ["action"],
636
- additionalProperties: false,
637
- };
638
751
  return this.test(async (session) => {
639
752
  let snapshotLen = 0;
640
753
  try {
@@ -655,8 +768,8 @@ Conversation:
655
768
  ${transcript || "(The agent has not spoken yet)"}
656
769
 
657
770
  Choose "speak" and provide your next utterance, or choose "hangup" if the conversation has naturally concluded.`;
658
- const raw = await (0, llm_ts_1._generate)(this._client, prompt, roleplaySchema);
659
- const action = JSON.parse(raw);
771
+ const raw = await (0, llm_ts_1._generate)(this._client, prompt, z.toJSONSchema(exports._roleplayActionSchema));
772
+ const action = exports._roleplayActionSchema.parse(JSON.parse(raw));
660
773
  if (action.action === "hangup") {
661
774
  this._logger.info("(Roleplay Session) [caller hangs up]");
662
775
  break;
@@ -716,6 +829,8 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
716
829
  name: this._name,
717
830
  organization: this._organization,
718
831
  purpose: this._purpose,
832
+ voice: this._voice,
833
+ acceptDtmf: this._acceptDtmfForNumbers,
719
834
  });
720
835
  cloned._onCallReceived = this._onCallReceived;
721
836
  cloned._onCallStart = this._onCallStart;
@@ -725,61 +840,75 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
725
840
  cloned._onTaskCompleteGeneric = this._onTaskCompleteGeneric;
726
841
  cloned._onTaskCompleteHandlers = { ...this._onTaskCompleteHandlers };
727
842
  cloned._searchQueryHandlers = { ...this._searchQueryHandlers };
843
+ cloned._onValidateHandlers = { ...this._onValidateHandlers };
728
844
  cloned._onActionRequested = this._onActionRequested;
729
845
  cloned._onActionGeneric = this._onActionGeneric;
730
846
  cloned._onActionHandlers = { ...this._onActionHandlers };
731
847
  cloned._onSessionEnd = this._onSessionEnd;
848
+ cloned._onEscalate = this._onEscalate;
732
849
  cloned._onDtmf = this._onDtmf;
733
850
  return cloned;
734
851
  }
735
- async _serveCampaign(campaignCode) {
736
- const env_3 = { stack: [], error: void 0, hasError: false };
852
+ async _serveCampaign(healthCtx, campaignCode) {
737
853
  try {
738
- const campaignUrl = new URL(`v1/campaigns/${campaignCode}`, this._client.getHttpBase());
739
- const campaignResponse = await (0, utils_ts_1.fetchOrThrow)(campaignUrl, {
740
- headers: await this._client.headers(),
741
- });
742
- const campaign = (await campaignResponse.json());
743
- const wsUrl = new URL(`v1/serve-campaign/${campaign.id}`, this._client.getWebsocketBase());
744
- this._logger.info("Connecting to campaign '%s' (id: %s).", campaign.name, campaign.id);
745
- const socket = __addDisposableResource(env_3, await new client_ts_1.GuavaSocket("serve-campaign", wsUrl.toString(), this._client, (msg) => msg, guavadialer_events_ts_1.decodeServerMessage).connect(), true);
746
- const activeCalls = [];
854
+ const env_6 = { stack: [], error: void 0, hasError: false };
747
855
  try {
748
- for await (const msg of socket) {
749
- switch (msg.message_type) {
750
- case "listen-started":
751
- this._logger.info("Listening for calls on campaign '%s'. Ready.", campaign.name);
752
- break;
753
- case "initiate-and-assign-call": {
754
- const { call_id, contact_data } = msg;
755
- const data = contact_data;
756
- const logPhone = data?.phone_number ?? "?";
757
- this._logger.info("Ready to make call, id %s — initiating call setup and dispatch for contact %s.", call_id, logPhone);
758
- activeCalls.push((async () => {
759
- socket.send({ message_type: "controller-ready", call_id });
760
- const variables = data?.data ?? {};
761
- await this._attachToCall(call_id, variables, undefined, "v2/connect-campaign-call");
762
- })());
763
- break;
856
+ const campaignUrl = new URL(`v1/campaigns/${campaignCode}`, this._client.getHttpBase());
857
+ const campaignResponse = await (0, utils_ts_1.fetchOrThrow)(campaignUrl, {
858
+ headers: await this._client.headers(),
859
+ });
860
+ const campaign = (await campaignResponse.json());
861
+ const wsUrl = new URL(`v1/serve-campaign/${campaign.id}`, this._client.getWebsocketBase());
862
+ this._logger.info("Connecting to campaign '%s' (id: %s).", campaign.name, campaign.id);
863
+ const socket = __addDisposableResource(env_6, await new client_ts_1.GuavaSocket("serve-campaign", wsUrl.toString(), this._client, (msg) => msg, guavadialer_events_ts_1.decodeServerMessage).connect(), true);
864
+ const activeCalls = [];
865
+ try {
866
+ for await (const msg of socket) {
867
+ switch (msg.message_type) {
868
+ case "listen-started":
869
+ this._logger.info("Listening for calls on campaign '%s'. Ready.", campaign.name);
870
+ healthCtx.ready();
871
+ break;
872
+ case "initiate-and-assign-call": {
873
+ const { call_id, contact_data } = msg;
874
+ const data = contact_data;
875
+ const logPhone = data?.phone_number ?? "?";
876
+ this._logger.info("Ready to make call, id %s — initiating call setup and dispatch for contact %s.", call_id, logPhone);
877
+ activeCalls.push((async () => {
878
+ socket.send({ message_type: "controller-ready", call_id });
879
+ const variables = data?.data ?? {};
880
+ const campaignCallInfo = {
881
+ call_type: "pstn",
882
+ from_number: null,
883
+ to_number: data?.phone_number ?? "",
884
+ caller_id: null,
885
+ };
886
+ await this._attachToCall(call_id, campaignCallInfo, variables, undefined, "v2/connect-campaign-call");
887
+ })());
888
+ break;
889
+ }
764
890
  }
765
891
  }
766
892
  }
893
+ catch (e) {
894
+ if (!(e instanceof client_ts_1.GuavaSocketClosedError))
895
+ throw e;
896
+ this._logger.info("Campaign '%s' disconnected.", campaign.name);
897
+ }
898
+ await Promise.all(activeCalls);
767
899
  }
768
- catch (e) {
769
- if (!(e instanceof client_ts_1.GuavaSocketClosedError))
770
- throw e;
771
- this._logger.info("Campaign '%s' disconnected.", campaign.name);
900
+ catch (e_6) {
901
+ env_6.error = e_6;
902
+ env_6.hasError = true;
903
+ }
904
+ finally {
905
+ const result_6 = __disposeResources(env_6);
906
+ if (result_6)
907
+ await result_6;
772
908
  }
773
- await Promise.all(activeCalls);
774
- }
775
- catch (e_3) {
776
- env_3.error = e_3;
777
- env_3.hasError = true;
778
909
  }
779
910
  finally {
780
- const result_3 = __disposeResources(env_3);
781
- if (result_3)
782
- await result_3;
911
+ healthCtx.stopped();
783
912
  }
784
913
  }
785
914
  /**
@@ -794,7 +923,21 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
794
923
  * await agent.attachCampaign("gcmp-abc123");
795
924
  */
796
925
  async attachCampaign(campaignCode) {
797
- return this._serveCampaign(campaignCode);
926
+ const env_7 = { stack: [], error: void 0, hasError: false };
927
+ try {
928
+ const healthCtx = new health_ts_1.HealthContext();
929
+ const _server = __addDisposableResource(env_7, await (0, health_ts_1.getHealthServer)(healthCtx), true);
930
+ return this._serveCampaign(healthCtx, campaignCode);
931
+ }
932
+ catch (e_7) {
933
+ env_7.error = e_7;
934
+ env_7.hasError = true;
935
+ }
936
+ finally {
937
+ const result_7 = __disposeResources(env_7);
938
+ if (result_7)
939
+ await result_7;
940
+ }
798
941
  }
799
942
  /* ===== Aliases to be removed at some point. ===== */
800
943
  /** @deprecated Use {@link listenPhone} instead. */