@guava-ai/guava-sdk 0.24.0 → 0.26.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 (58) 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/multiple-agents.d.ts +1 -0
  9. package/dist/examples/multiple-agents.js +52 -0
  10. package/dist/examples/multiple-agents.js.map +1 -0
  11. package/dist/examples/scheduling-outbound.js +3 -2
  12. package/dist/examples/scheduling-outbound.js.map +1 -1
  13. package/dist/src/agent.d.ts +17 -6
  14. package/dist/src/agent.js +106 -41
  15. package/dist/src/agent.js.map +1 -1
  16. package/dist/src/call.d.ts +3 -0
  17. package/dist/src/call.js +63 -23
  18. package/dist/src/call.js.map +1 -1
  19. package/dist/src/client.d.ts +3 -0
  20. package/dist/src/client.js +6 -0
  21. package/dist/src/client.js.map +1 -1
  22. package/dist/src/commands.d.ts +11 -0
  23. package/dist/src/commands.js +6 -1
  24. package/dist/src/commands.js.map +1 -1
  25. package/dist/src/example-utils.d.ts +2 -0
  26. package/dist/src/example-utils.js +115 -0
  27. package/dist/src/example-utils.js.map +1 -0
  28. package/dist/src/helpers/llm.d.ts +10 -0
  29. package/dist/src/helpers/llm.js +54 -27
  30. package/dist/src/helpers/llm.js.map +1 -1
  31. package/dist/src/index.d.ts +3 -1
  32. package/dist/src/index.js +4 -1
  33. package/dist/src/index.js.map +1 -1
  34. package/dist/src/runner.d.ts +10 -0
  35. package/dist/src/runner.js +101 -0
  36. package/dist/src/runner.js.map +1 -0
  37. package/dist/src/testing/session.d.ts +7 -0
  38. package/dist/src/testing/session.js +3 -22
  39. package/dist/src/testing/session.js.map +1 -1
  40. package/dist/src/version.d.ts +1 -1
  41. package/dist/src/version.js +1 -1
  42. package/examples/README.md +7 -6
  43. package/examples/{example.test.ts → agent-testing.test.ts} +1 -1
  44. package/examples/multiple-agents.ts +18 -0
  45. package/examples/scheduling-outbound.ts +3 -2
  46. package/package.json +5 -1
  47. package/src/agent.ts +130 -47
  48. package/src/call.ts +78 -23
  49. package/src/client.ts +7 -0
  50. package/src/commands.ts +7 -0
  51. package/src/example-utils.ts +87 -0
  52. package/src/helpers/llm.ts +23 -31
  53. package/src/index.ts +3 -1
  54. package/src/runner.ts +39 -0
  55. package/src/testing/session.ts +2 -22
  56. package/src/version.ts +1 -1
  57. package/dist/examples/example.test.js.map +0 -1
  58. /package/dist/examples/{example.test.d.ts → agent-testing.test.d.ts} +0 -0
@@ -33,12 +33,12 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.TestSession = void 0;
36
+ exports.TestSession = exports._evalResponseSchema = void 0;
37
37
  const node_crypto_1 = require("node:crypto");
38
38
  const z = __importStar(require("zod"));
39
39
  const protocol_ts_1 = require("./protocol.js");
40
40
  const llm_ts_1 = require("../helpers/llm.js");
41
- const _evalResponseSchema = z.object({
41
+ exports._evalResponseSchema = z.object({
42
42
  results: z.array(z.object({
43
43
  passed: z.boolean(),
44
44
  reasoning: z.string().optional(),
@@ -170,26 +170,7 @@ ${transcript || "(empty — no conversation occurred)"}
170
170
 
171
171
  Criteria:
172
172
  ${criteriaList}`;
173
- const evalSchema = {
174
- type: "object",
175
- properties: {
176
- results: {
177
- type: "array",
178
- items: {
179
- type: "object",
180
- properties: {
181
- passed: { type: "boolean" },
182
- reasoning: { type: "string" },
183
- },
184
- required: ["passed"],
185
- additionalProperties: false,
186
- },
187
- },
188
- },
189
- required: ["results"],
190
- additionalProperties: false,
191
- };
192
- const evalResponse = _evalResponseSchema.parse(JSON.parse(await (0, llm_ts_1._generate)(this._client, prompt, evalSchema)));
173
+ const evalResponse = exports._evalResponseSchema.parse(JSON.parse(await (0, llm_ts_1._generate)(this._client, prompt, z.toJSONSchema(exports._evalResponseSchema))));
193
174
  if (evalResponse.results.length !== allCriteria.length) {
194
175
  throw new Error(`Evaluation returned ${evalResponse.results.length} results for ${allCriteria.length} criteria.`);
195
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/testing/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/testing/session.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.24.0";
1
+ export declare const SDK_VERSION = "0.26.0";
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = "0.24.0";
4
+ exports.SDK_VERSION = "0.26.0";
5
5
  //# sourceMappingURL=version.js.map
@@ -1,8 +1,9 @@
1
1
  # Examples
2
2
 
3
- - [example.test.ts](./example.test.ts) - Agent testing example demonstrating `agent.test()`, `agent.testRoleplay()`, `agent.patch()`, and `session.evaluate()` for unit testing handlers, scripted conversation flows, LLM-driven roleplay, and natural-language pass/fail evaluation.
4
- - [help-desk.ts](./help-desk.ts) - An inbound call agent for a furniture retailer that uses `DocumentQA` for RAG-based question answering and `IntentRecognizer` to classify caller intent and route to the appropriate department.
5
- - [polling-campaign.ts](./polling-campaign.ts) - Attaches a political polling agent to an existing Guava campaign via `attachCampaign()`, uses `reachPerson()` to confirm availability, then conducts a structured poll with text and multiple-choice fields.
6
- - [property-insurance.ts](./property-insurance.ts) - A simple inbound call agent that uses `DocumentQA` and RAG to answer questions about a property insurance policy.
7
- - [restaurant-waitlist.ts](./restaurant-waitlist.ts) - An inbound call agent that collects a caller's name, party size, and phone number to add them to a restaurant waitlist using a structured task checklist.
8
- - [scheduling-outbound.ts](./scheduling-outbound.ts) - An outbound call agent for a dental office that calls patients to schedule appointments, using `reachPerson()` for human detection and `DatetimeFilter` for searchable calendar slot availability.
3
+ - [agent-testing.test.ts](./agent-testing.test.ts) - Test suite for the help-desk agent demonstrating `agent.test()`, `agent.testRoleplay()`, `agent.patch()`, handler unit tests with `MockCall`, and LLM-evaluated assertions with `session.evaluate()`.
4
+ - [help-desk.ts](./help-desk.ts) - Inbound call agent for a furniture retailer that uses `DocumentQA` for RAG-backed question answering and `IntentRecognizer` to route callers to the correct department (sales, delivery, account management).
5
+ - [multiple-agents.ts](./multiple-agents.ts) - Demonstrates running two independent agents simultaneously on different connection types (phone and WebRTC) using a shared `Runner`.
6
+ - [polling-campaign.ts](./polling-campaign.ts) - Outbound campaign agent that conducts a non-partisan political opinion poll, uses `reachPerson` to confirm contact availability, and collects structured responses via a task checklist.
7
+ - [property-insurance.ts](./property-insurance.ts) - Inbound call agent for a property insurance company that uses `DocumentQA` to answer policy questions from a knowledge base.
8
+ - [restaurant-waitlist.ts](./restaurant-waitlist.ts) - Inbound call agent for a restaurant that collects caller name, party size, and phone number to add callers to a waitlist using a structured task.
9
+ - [scheduling-outbound.ts](./scheduling-outbound.ts) - Outbound call agent for a dental office that uses `reachPerson` to confirm contact availability, then schedules appointments using a searchable calendar-slot field backed by `DatetimeFilter`.
@@ -10,7 +10,7 @@ import { agent } from "@guava-ai/guava-sdk/examples/help-desk";
10
10
  describe("HelpDeskAgent", () => {
11
11
  test("handler unit test - new purchase routes to sales", async () => {
12
12
  const suggestion = await agent.handlers.onActionRequest(new MockCall(), "make a new purchase");
13
- expect(suggestion).toHaveProperty("key", "sales");
13
+ expect(suggestion).toContainEqual({ key: "sales" });
14
14
  });
15
15
 
16
16
  test("new purchase routes to sales", async () => {
@@ -0,0 +1,18 @@
1
+ import * as guava from "@guava-ai/guava-sdk";
2
+
3
+ const agentA = new guava.Agent({
4
+ name: "Grace",
5
+ purpose: "You are a helpful voice agent.",
6
+ });
7
+
8
+ const agentB = new guava.Agent({
9
+ name: "Jordan",
10
+ purpose: "You are a helpful voice agent.",
11
+ });
12
+
13
+ export async function run(_args: string[]) {
14
+ const runner = new guava.Runner();
15
+ runner.listenPhone(agentA, process.env.GUAVA_AGENT_NUMBER!);
16
+ runner.listenWebrtc(agentB);
17
+ await runner.run();
18
+ }
@@ -1,6 +1,7 @@
1
1
  import * as guava from "@guava-ai/guava-sdk";
2
2
  import { DatetimeFilter } from "@guava-ai/guava-sdk/helpers";
3
3
  import { mockAppointmentsForFuture } from "@guava-ai/guava-sdk/example-data";
4
+ import { getAgentNumber } from "@guava-ai/guava-sdk/example-utils";
4
5
 
5
6
  const agent = new guava.Agent({
6
7
  organization: "Bright Smile Dental",
@@ -33,7 +34,7 @@ agent.onReachPerson(async (call: guava.Call, outcome: string) => {
33
34
  ],
34
35
  });
35
36
  } else {
36
- await call.hangup("Apologize for your mistake and hang up the call.");
37
+ await call.hangup("Appropriately end the call.");
37
38
  }
38
39
  });
39
40
 
@@ -56,7 +57,7 @@ export async function run(args: string[]) {
56
57
  process.exit(1);
57
58
  }
58
59
 
59
- agent.callPhone(process.env.GUAVA_AGENT_NUMBER, toNumber, {
60
+ agent.callPhone(await getAgentNumber(), toNumber, {
60
61
  patientName: patientName,
61
62
  });
62
63
  }
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.24.0",
5
+ "version": "0.26.0",
6
6
  "type": "commonjs",
7
7
  "main": "dist/src/index.js",
8
8
  "types": "dist/src/index.d.ts",
@@ -23,6 +23,10 @@
23
23
  "types": "./dist/src/example-data.d.ts",
24
24
  "default": "./dist/src/example-data.js"
25
25
  },
26
+ "./example-utils": {
27
+ "types": "./dist/src/example-utils.d.ts",
28
+ "default": "./dist/src/example-utils.js"
29
+ },
26
30
  "./examples/help-desk": {
27
31
  "types": "./dist/examples/help-desk.d.ts",
28
32
  "default": "./dist/examples/help-desk.js"
package/src/agent.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  AnswerQuestionCommand,
5
5
  ChoiceResultCommand,
6
6
  type Command,
7
+ ExpertErrorCommand,
7
8
  RegisteredHooksCommand,
8
9
  } from "./commands.ts";
9
10
  import {
@@ -13,6 +14,7 @@ import {
13
14
  type DTMFPressedEvent,
14
15
  type EscalateEvent,
15
16
  type GuavaEvent,
17
+ type OutboundCallFailed,
16
18
  decodeEventDict,
17
19
  } from "./events.ts";
18
20
  import {
@@ -31,9 +33,15 @@ import { telemetryClient } from "./telemetry.ts";
31
33
  import { runChat } from "./testing/chat.ts";
32
34
  import { SessionStarted } from "./testing/protocol.ts";
33
35
  import { TestSession } from "./testing/session.ts";
36
+ import * as z from "zod";
34
37
  import { fetchOrThrow, getBaseUrl } from "./utils.ts";
35
38
  import { runWebrtcHelper } from "./webrtc-helper.ts";
36
39
 
40
+ export const _roleplayActionSchema = z.object({
41
+ action: z.enum(["speak", "hangup"]),
42
+ utterance: z.string().optional(),
43
+ });
44
+
37
45
  export type { CallInfo } from "./socket/call-info.ts";
38
46
 
39
47
  export type IncomingCallAction = { action: "accept" } | { action: "decline" };
@@ -57,7 +65,7 @@ export class Agent {
57
65
  private _acceptDtmfForNumbers: boolean;
58
66
  private _logger: Logger;
59
67
 
60
- private _client: Client = new Client();
68
+ _client: Client = new Client();
61
69
 
62
70
  private _onCallReceived: (callInfo: CallInfo) => Promise<IncomingCallAction> = async () => ({
63
71
  action: "accept",
@@ -85,6 +93,7 @@ export class Agent {
85
93
  private _onSessionEnd?: (call: Call, event: BotSessionEnded) => Promise<void>;
86
94
  private _onEscalate?: (call: Call, event: EscalateEvent) => Promise<void>;
87
95
  private _onDtmf?: (call: Call, event: DTMFPressedEvent) => Promise<void>;
96
+ private _onOutboundFailed?: (event: OutboundCallFailed) => Promise<void>;
88
97
 
89
98
  constructor(args?: {
90
99
  name?: string;
@@ -188,6 +197,10 @@ export class Agent {
188
197
  this._onDtmf = callback;
189
198
  }
190
199
 
200
+ onOutboundFailed(callback: (event: OutboundCallFailed) => Promise<void>): void {
201
+ this._onOutboundFailed = callback;
202
+ }
203
+
191
204
  get handlers() {
192
205
  return {
193
206
  onCallReceived: (callInfo: CallInfo) => this._onCallReceived(callInfo),
@@ -272,14 +285,37 @@ export class Agent {
272
285
  await runWebrtcHelper(webrtcCode, getBaseUrl());
273
286
  }
274
287
 
288
+ private async _invokeHandler(
289
+ call: Call,
290
+ name: string,
291
+ informAgent: boolean,
292
+ handler: () => Promise<void>,
293
+ ): Promise<void> {
294
+ try {
295
+ await handler();
296
+ } catch (err) {
297
+ this._logger.error(`An error occurred in the ${name} handler.`, err);
298
+ if (informAgent) {
299
+ await call.sendCommand(ExpertErrorCommand, {
300
+ command_type: "expert-error",
301
+ message: `The expert encountered an error while processing ${name}`,
302
+ });
303
+ }
304
+ }
305
+ }
306
+
275
307
  private async _dispatchEvent(call: Call, event: GuavaEvent, testSession?: TestSession) {
276
308
  if (event.event_type === "caller-speech") {
277
309
  if (this._onCallerSpeech !== undefined) {
278
- await this._onCallerSpeech(call, event);
310
+ await this._invokeHandler(call, "onCallerSpeech", false, () =>
311
+ this._onCallerSpeech!(call, event),
312
+ );
279
313
  }
280
314
  } else if (event.event_type === "agent-speech") {
281
315
  if (this._onAgentSpeech !== undefined) {
282
- await this._onAgentSpeech(call, event);
316
+ await this._invokeHandler(call, "onAgentSpeech", false, () =>
317
+ this._onAgentSpeech!(call, event),
318
+ );
283
319
  }
284
320
  } else if (event.event_type === "task-done") {
285
321
  const errors: string[] = [];
@@ -298,17 +334,28 @@ export class Agent {
298
334
  await call.retryTask(errors.join(" "));
299
335
  } else {
300
336
  this._logger.info(`Task ${event.task_id} completed.`);
301
- if (this._onTaskCompleteGeneric !== undefined) {
302
- await this._onTaskCompleteGeneric(call, event.task_id);
303
- } else if (event.task_id in this._onTaskCompleteHandlers) {
304
- await this._onTaskCompleteHandlers[event.task_id](call);
305
- } else {
306
- this._logger.warn(`No handler registered for completion of task '${event.task_id}'`);
337
+ try {
338
+ if (this._onTaskCompleteGeneric !== undefined) {
339
+ await this._onTaskCompleteGeneric(call, event.task_id);
340
+ } else if (event.task_id in this._onTaskCompleteHandlers) {
341
+ await this._onTaskCompleteHandlers[event.task_id](call);
342
+ } else {
343
+ this._logger.warn(`No handler registered for completion of task '${event.task_id}'`);
344
+ }
345
+ } catch (err) {
346
+ this._logger.error(
347
+ `An error occurred in the on_task_complete('${event.task_id}') handler.`,
348
+ err,
349
+ );
350
+ await call.sendCommand(ExpertErrorCommand, {
351
+ command_type: "expert-error",
352
+ message: `The expert encountered an error while processing on_task_complete('${event.task_id}') - the task has failed.`,
353
+ });
307
354
  }
308
355
  }
309
356
  } else if (event.event_type === "agent-question") {
357
+ this._logger.info(`Received question from bot: ${event.question}`);
310
358
  if (this._onQuestion !== undefined) {
311
- this._logger.info(`Received question from bot: ${event.question}`);
312
359
  let answer: string;
313
360
  try {
314
361
  answer = await this._onQuestion(call, event.question);
@@ -333,6 +380,9 @@ export class Agent {
333
380
  }
334
381
  } else if (event.event_type === "action-item-done") {
335
382
  call._fieldValues[event.key] = event.payload;
383
+ if (event.key && event.payload) {
384
+ this._logger.info(`Field ${event.key} updated.`);
385
+ }
336
386
  } else if (event.event_type === "choice-query") {
337
387
  this._logger.info(`Received search query for field '${event.field_key}': ${event.query}`);
338
388
  const handler = this._searchQueryHandlers[event.field_key];
@@ -340,25 +390,49 @@ export class Agent {
340
390
  this._logger.warn(
341
391
  `Search query arrived for field '${event.field_key}' with no handler attached.`,
342
392
  );
343
- } else {
344
- const [matchedChoices, otherChoices] = await handler(call, event.query);
345
- await call.sendCommand(ChoiceResultCommand, {
346
- command_type: "choice-query-result",
347
- field_key: event.field_key,
348
- query_id: event.query_id,
349
- matched_choices: matchedChoices,
350
- other_choices: otherChoices,
393
+ await call.sendCommand(ExpertErrorCommand, {
394
+ command_type: "expert-error",
395
+ message: `The expert failed to handle on_search_query('${event.field_key}'). No results will be forthcoming.`,
351
396
  });
397
+ } else {
398
+ try {
399
+ const [matchedChoices, otherChoices] = await handler(call, event.query);
400
+ await call.sendCommand(ChoiceResultCommand, {
401
+ command_type: "choice-query-result",
402
+ field_key: event.field_key,
403
+ query_id: event.query_id,
404
+ matched_choices: matchedChoices,
405
+ other_choices: otherChoices,
406
+ });
407
+ } catch (err) {
408
+ this._logger.error(
409
+ `An error occurred in the on_search_query('${event.field_key}') handler.`,
410
+ err,
411
+ );
412
+ await call.sendCommand(ExpertErrorCommand, {
413
+ command_type: "expert-error",
414
+ message: `The expert encountered an error while processing the on_search_query('${event.field_key}') handler. No results will be forthcoming.`,
415
+ });
416
+ }
352
417
  }
353
418
  } else if (event.event_type === "action-request") {
354
419
  this._logger.info(`Received action request ${event.intent_id}: ${event.intent_summary}`);
355
420
  let suggestions: SuggestedAction[] = [];
356
421
  if (this._onActionRequested !== undefined) {
357
- const result = await this._onActionRequested(call, event.intent_summary);
358
- if (Array.isArray(result)) {
359
- suggestions = result;
360
- } else if (result !== null && result !== undefined) {
361
- suggestions = [result];
422
+ try {
423
+ const result = await this._onActionRequested(call, event.intent_summary);
424
+ if (Array.isArray(result)) {
425
+ suggestions = result;
426
+ } else if (result !== null && result !== undefined) {
427
+ suggestions = [result];
428
+ }
429
+ } catch (err) {
430
+ this._logger.error("An error occurred in the onActionRequest handler.", err);
431
+ await call.sendCommand(ExpertErrorCommand, {
432
+ command_type: "expert-error",
433
+ message:
434
+ "The expert encountered an error while processing the on_action_request handler.",
435
+ });
362
436
  }
363
437
  }
364
438
  await call.sendCommand(ActionSuggestionCommand, {
@@ -378,7 +452,18 @@ export class Agent {
378
452
  onActionFunc = () => this._onActionHandlers[event.action_key](call);
379
453
  }
380
454
  if (onActionFunc !== undefined) {
381
- await onActionFunc();
455
+ try {
456
+ await onActionFunc();
457
+ } catch (err) {
458
+ this._logger.error(
459
+ `An error occurred in the on_action('${event.action_key}') handler.`,
460
+ err,
461
+ );
462
+ await call.sendCommand(ExpertErrorCommand, {
463
+ command_type: "expert-error",
464
+ message: `The expert encountered an error while processing the on_action('${event.action_key}') handler.`,
465
+ });
466
+ }
382
467
  } else {
383
468
  this._logger.warn(`No handler registered for action '${event.action_key}'`);
384
469
  }
@@ -387,14 +472,18 @@ export class Agent {
387
472
  if (testSession) {
388
473
  testSession.terminationReason = event.termination_reason;
389
474
  }
390
- await this._onSessionEnd?.(call, event);
475
+ if (this._onSessionEnd !== undefined) {
476
+ await this._invokeHandler(call, "onSessionEnd", false, () =>
477
+ this._onSessionEnd!(call, event),
478
+ );
479
+ }
391
480
  } else if (event.event_type === "dtmf") {
392
481
  if (this._onDtmf !== undefined) {
393
- await this._onDtmf(call, event);
482
+ await this._invokeHandler(call, "onDtmf", false, () => this._onDtmf!(call, event));
394
483
  }
395
484
  } else if (event.event_type === "escalate") {
396
485
  if (this._onEscalate !== undefined) {
397
- await this._onEscalate(call, event);
486
+ await this._invokeHandler(call, "onEscalate", true, () => this._onEscalate!(call, event));
398
487
  } else if (event.requested_by === "agent") {
399
488
  await call.sendInstruction(
400
489
  "No escalation target set. Apologize for not being able to help, ask them to try calling another time, and hang up the call immediately.",
@@ -404,6 +493,13 @@ export class Agent {
404
493
  "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.",
405
494
  );
406
495
  }
496
+ } else if (event.event_type === "outbound-call-failed") {
497
+ this._logger.error(`Outbound call failed: ${event.error_reason}`);
498
+ if (this._onOutboundFailed !== undefined) {
499
+ await this._invokeHandler(call, "onOutboundFailed", false, () =>
500
+ this._onOutboundFailed!(event),
501
+ );
502
+ }
407
503
  } else if (event.event_type === "error") {
408
504
  this._logger.error(`The Guava agent reported an error: ${event.content}`);
409
505
  } else if (event.event_type === "warning") {
@@ -572,12 +668,12 @@ export class Agent {
572
668
  * @description use the Guava API to call out to a number
573
669
  */
574
670
  async callPhone(
575
- fromNumber: string | undefined,
671
+ fromNumber: string,
576
672
  toNumber: string,
577
673
  variables: Record<string, any> = {},
578
674
  ): Promise<void> {
579
675
  const url = new URL("v2/create-outbound", this._client.getHttpBase());
580
- if (fromNumber) url.searchParams.set("from_number", fromNumber);
676
+ url.searchParams.set("from_number", fromNumber);
581
677
  url.searchParams.set("to_number", toNumber);
582
678
 
583
679
  const response = await fetchOrThrow(url, {
@@ -681,16 +777,6 @@ export class Agent {
681
777
  roleplayPrompt: string,
682
778
  variables: Record<string, any> = {},
683
779
  ): Promise<TestSession> {
684
- const roleplaySchema = {
685
- type: "object",
686
- properties: {
687
- action: { type: "string", enum: ["speak", "hangup"] },
688
- utterance: { type: "string" },
689
- },
690
- required: ["action"],
691
- additionalProperties: false,
692
- };
693
-
694
780
  return this.test(async (session) => {
695
781
  let snapshotLen = 0;
696
782
  try {
@@ -714,8 +800,8 @@ ${transcript || "(The agent has not spoken yet)"}
714
800
 
715
801
  Choose "speak" and provide your next utterance, or choose "hangup" if the conversation has naturally concluded.`;
716
802
 
717
- const raw = await _generate(this._client, prompt, roleplaySchema);
718
- const action = JSON.parse(raw) as { action: string; utterance?: string };
803
+ const raw = await _generate(this._client, prompt, z.toJSONSchema(_roleplayActionSchema));
804
+ const action = _roleplayActionSchema.parse(JSON.parse(raw));
719
805
 
720
806
  if (action.action === "hangup") {
721
807
  this._logger.info("(Roleplay Session) [caller hangs up]");
@@ -800,10 +886,11 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
800
886
  cloned._onSessionEnd = this._onSessionEnd;
801
887
  cloned._onEscalate = this._onEscalate;
802
888
  cloned._onDtmf = this._onDtmf;
889
+ cloned._onOutboundFailed = this._onOutboundFailed;
803
890
  return cloned;
804
891
  }
805
892
 
806
- private async _serveCampaign(healthCtx: HealthContext, campaignCode: string): Promise<void> {
893
+ async _serveCampaign(healthCtx: HealthContext, campaignCode: string): Promise<void> {
807
894
  try {
808
895
  const campaignUrl = new URL(`v1/campaigns/${campaignCode}`, this._client.getHttpBase());
809
896
  const campaignResponse = await fetchOrThrow(campaignUrl, {
@@ -898,11 +985,7 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
898
985
  }
899
986
 
900
987
  /** @deprecated Use {@link callPhone} instead. */
901
- async outboundPhone(
902
- fromNumber: string | undefined,
903
- toNumber: string,
904
- variables: Record<string, any> = {},
905
- ) {
988
+ async outboundPhone(fromNumber: string, toNumber: string, variables: Record<string, any> = {}) {
906
989
  return this.callPhone(fromNumber, toNumber, variables);
907
990
  }
908
991
  }