@guava-ai/guava-sdk 0.33.0 → 0.35.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/src/agent.ts CHANGED
@@ -6,8 +6,11 @@ import {
6
6
  type Command,
7
7
  ExpertErrorCommand,
8
8
  RegisteredHooksCommand,
9
+ type SpeedPreset,
10
+ validateSpeedPreset,
9
11
  } from "./commands.ts";
10
12
  import {
13
+ type AgentDTMFSentEvent,
11
14
  type AgentSpeechEvent,
12
15
  type BotSessionEnded,
13
16
  type CallerSpeechEvent,
@@ -63,6 +66,7 @@ export class Agent {
63
66
  private _purpose?: string;
64
67
  private _voice?: string;
65
68
  private _ttsReplacements?: Record<string, string>;
69
+ private _speechSpeed?: SpeedPreset;
66
70
  private _acceptDtmfForNumbers: boolean;
67
71
  private _logger: Logger;
68
72
 
@@ -93,7 +97,8 @@ export class Agent {
93
97
  > = {};
94
98
  private _onSessionEnd?: (call: Call, event: BotSessionEnded) => Promise<void>;
95
99
  private _onEscalate?: (call: Call, event: EscalateEvent) => Promise<void>;
96
- private _onDtmf?: (call: Call, event: DTMFPressedEvent) => Promise<void>;
100
+ private _onCallerDtmf?: (call: Call, event: DTMFPressedEvent) => Promise<void>;
101
+ private _onAgentDtmf?: (call: Call, event: AgentDTMFSentEvent) => Promise<void>;
97
102
  private _onOutboundFailed?: (call: Call, event: OutboundCallFailed) => Promise<void>;
98
103
 
99
104
  constructor(args?: {
@@ -103,6 +108,7 @@ export class Agent {
103
108
  voice?: string;
104
109
  pronunciations?: Record<string, string>;
105
110
  acceptDtmf?: boolean;
111
+ speechSpeed?: SpeedPreset;
106
112
  }) {
107
113
  this._name = args?.name;
108
114
  this._organization = args?.organization;
@@ -110,6 +116,8 @@ export class Agent {
110
116
  this._voice = args?.voice;
111
117
  this._ttsReplacements = args?.pronunciations;
112
118
  this._acceptDtmfForNumbers = args?.acceptDtmf ?? true;
119
+ validateSpeedPreset(args?.speechSpeed); // Validate the preset name early, at construction.
120
+ this._speechSpeed = args?.speechSpeed;
113
121
  this._logger = getDefaultLogger();
114
122
  }
115
123
 
@@ -196,8 +204,17 @@ export class Agent {
196
204
  this._onEscalate = callback;
197
205
  }
198
206
 
207
+ onCallerDtmf(callback: (call: Call, event: DTMFPressedEvent) => Promise<void>): void {
208
+ this._onCallerDtmf = callback;
209
+ }
210
+
211
+ /** Alias for {@link onCallerDtmf}; will be marked deprecated in a future release. */
199
212
  onDtmf(callback: (call: Call, event: DTMFPressedEvent) => Promise<void>): void {
200
- this._onDtmf = callback;
213
+ this._onCallerDtmf = callback;
214
+ }
215
+
216
+ onAgentDtmf(callback: (call: Call, event: AgentDTMFSentEvent) => Promise<void>): void {
217
+ this._onAgentDtmf = callback;
201
218
  }
202
219
 
203
220
  onOutboundFailed(callback: (call: Call, event: OutboundCallFailed) => Promise<void>): void {
@@ -260,7 +277,7 @@ export class Agent {
260
277
  async listenPhone(phoneNumber: string): Promise<void> {
261
278
  const healthCtx = new HealthContext();
262
279
  await using _server = await getHealthServer(healthCtx);
263
- return this._listenInbound(healthCtx, { agent_number: phoneNumber });
280
+ return await this._listenInbound(healthCtx, { agent_number: phoneNumber });
264
281
  }
265
282
 
266
283
  async listenWebrtc(webrtcCode?: string): Promise<void> {
@@ -270,13 +287,13 @@ export class Agent {
270
287
  }
271
288
  const healthCtx = new HealthContext();
272
289
  await using _server = await getHealthServer(healthCtx);
273
- return this._listenInbound(healthCtx, { webrtc_code: webrtcCode });
290
+ return await this._listenInbound(healthCtx, { webrtc_code: webrtcCode });
274
291
  }
275
292
 
276
293
  async listenSip(sipCode: string): Promise<void> {
277
294
  const healthCtx = new HealthContext();
278
295
  await using _server = await getHealthServer(healthCtx);
279
- return this._listenInbound(healthCtx, { sip_code: sipCode });
296
+ return await this._listenInbound(healthCtx, { sip_code: sipCode });
280
297
  }
281
298
 
282
299
  async callLocal(variables: Record<string, any> = {}): Promise<void> {
@@ -483,8 +500,16 @@ export class Agent {
483
500
  );
484
501
  }
485
502
  } else if (event.event_type === "dtmf") {
486
- if (this._onDtmf !== undefined) {
487
- await this._invokeHandler(call, "onDtmf", false, () => this._onDtmf!(call, event));
503
+ if (this._onCallerDtmf !== undefined) {
504
+ await this._invokeHandler(call, "onCallerDtmf", false, () =>
505
+ this._onCallerDtmf!(call, event),
506
+ );
507
+ }
508
+ } else if (event.event_type === "agent-dtmf") {
509
+ if (this._onAgentDtmf !== undefined) {
510
+ await this._invokeHandler(call, "onAgentDtmf", false, () =>
511
+ this._onAgentDtmf!(call, event),
512
+ );
488
513
  }
489
514
  } else if (event.event_type === "escalate") {
490
515
  if (this._onEscalate !== undefined) {
@@ -523,6 +548,7 @@ export class Agent {
523
548
  agentPurpose: this._purpose,
524
549
  organizationName: this._organization,
525
550
  voice: this._voice,
551
+ speechSpeed: this._speechSpeed,
526
552
  pronunciations: this._ttsReplacements,
527
553
  });
528
554
  await call.sendCommand(RegisteredHooksCommand, {
@@ -532,6 +558,7 @@ export class Agent {
532
558
  has_on_action_requested: this._onActionRequested !== undefined,
533
559
  has_on_escalate: this._onEscalate !== undefined,
534
560
  accept_dtmf_for_numbers: this._acceptDtmfForNumbers,
561
+ has_on_agent_dtmf: this._onAgentDtmf !== undefined,
535
562
  });
536
563
  if (this._onCallStart !== undefined) {
537
564
  await this._onCallStart(call);
@@ -897,6 +924,7 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
897
924
  voice: this._voice,
898
925
  pronunciations: this._ttsReplacements,
899
926
  acceptDtmf: this._acceptDtmfForNumbers,
927
+ speechSpeed: this._speechSpeed,
900
928
  });
901
929
  cloned._onCallReceived = this._onCallReceived;
902
930
  cloned._onCallStart = this._onCallStart;
@@ -912,7 +940,8 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
912
940
  cloned._onActionHandlers = { ...this._onActionHandlers };
913
941
  cloned._onSessionEnd = this._onSessionEnd;
914
942
  cloned._onEscalate = this._onEscalate;
915
- cloned._onDtmf = this._onDtmf;
943
+ cloned._onCallerDtmf = this._onCallerDtmf;
944
+ cloned._onAgentDtmf = this._onAgentDtmf;
916
945
  cloned._onOutboundFailed = this._onOutboundFailed;
917
946
  return cloned;
918
947
  }
@@ -1009,7 +1038,7 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
1009
1038
  async attachCampaign(campaignCode: string): Promise<void> {
1010
1039
  const healthCtx = new HealthContext();
1011
1040
  await using _server = await getHealthServer(healthCtx);
1012
- return this._serveCampaign(healthCtx, campaignCode);
1041
+ return await this._serveCampaign(healthCtx, campaignCode);
1013
1042
  }
1014
1043
 
1015
1044
  /* ===== Aliases to be removed at some point. ===== */
package/src/call.ts CHANGED
@@ -2,6 +2,7 @@ import { type Logger, getDefaultLogger } from "./logging.ts";
2
2
  import {
3
3
  type Command,
4
4
  SetPersona,
5
+ type SpeedPreset,
5
6
  SetLanguageModeCommand,
6
7
  type Language,
7
8
  SetTaskCommand,
@@ -180,6 +181,7 @@ export class Call {
180
181
  agentName?: string;
181
182
  agentPurpose?: string;
182
183
  voice?: string;
184
+ speechSpeed?: SpeedPreset;
183
185
  pronunciations?: Record<string, string>;
184
186
  }) {
185
187
  await this.sendCommand(SetPersona, {
@@ -188,6 +190,7 @@ export class Call {
188
190
  agent_name: args.agentName,
189
191
  agent_purpose: args.agentPurpose,
190
192
  voice: args.voice,
193
+ tts_speed: args.speechSpeed,
191
194
  tts_replacements: args.pronunciations,
192
195
  });
193
196
  }
package/src/commands.ts CHANGED
@@ -57,12 +57,27 @@ export const AnswerQuestionCommand = z.strictObject({
57
57
  });
58
58
  export type AnswerQuestionCommand = z.input<typeof AnswerQuestionCommand>;
59
59
 
60
+ export const SpeedPreset = z.enum(["x-slow", "slow", "medium", "fast", "x-fast"]);
61
+ export type SpeedPreset = z.infer<typeof SpeedPreset>;
62
+
63
+ /**
64
+ * @description throw on an unrecognized speech speed preset, with the valid presets listed
65
+ */
66
+ export function validateSpeedPreset(speed: unknown): void {
67
+ if (speed !== undefined && !SpeedPreset.safeParse(speed).success) {
68
+ throw new Error(
69
+ `speechSpeed must be one of ${JSON.stringify(SpeedPreset.options)} (got ${JSON.stringify(speed)}).`,
70
+ );
71
+ }
72
+ }
73
+
60
74
  export const SetPersona = z.strictObject({
61
75
  command_type: z.literal("set-persona"),
62
76
  agent_name: z.string().optional(),
63
77
  organization_name: z.string().optional(),
64
78
  agent_purpose: z.string().optional(),
65
79
  voice: z.string().optional(),
80
+ tts_speed: SpeedPreset.optional(),
66
81
  tts_replacements: z.record(z.string().min(1), z.string()).optional(),
67
82
  });
68
83
  export type SetPersona = z.input<typeof SetPersona>;
@@ -98,6 +113,7 @@ export const RegisteredHooksCommand = z.strictObject({
98
113
  has_on_action_requested: z.boolean().optional().default(false),
99
114
  has_on_escalate: z.boolean().optional().default(false),
100
115
  accept_dtmf_for_numbers: z.boolean().default(true),
116
+ has_on_agent_dtmf: z.boolean().optional().default(false),
101
117
  });
102
118
  export type RegisteredHooksCommand = z.input<typeof RegisteredHooksCommand>;
103
119
 
package/src/events.ts CHANGED
@@ -99,6 +99,7 @@ export const BotSessionEnded = z.object({
99
99
  "voicemail",
100
100
  ]),
101
101
  dnc: z.boolean().default(false),
102
+ pickup: z.boolean().default(true),
102
103
  });
103
104
  export type BotSessionEnded = z.infer<typeof BotSessionEnded>;
104
105
 
@@ -153,6 +154,17 @@ export const DTMFPressedEvent = z.object({
153
154
  });
154
155
  export type DTMFPressedEvent = z.infer<typeof DTMFPressedEvent>;
155
156
 
157
+ /**
158
+ * The agent pressed DTMF digits, either on its own after `call.setAgentDtmf(true)`
159
+ * or because the developer called `call.sendDtmf(...)`; `requested_by` says which.
160
+ */
161
+ export const AgentDTMFSentEvent = z.object({
162
+ event_type: z.literal("agent-dtmf"),
163
+ digits: z.array(z.enum(DTMF_DIGITS)),
164
+ requested_by: z.enum(["agent", "developer"]).default("agent"),
165
+ });
166
+ export type AgentDTMFSentEvent = z.infer<typeof AgentDTMFSentEvent>;
167
+
156
168
  export const EscalateEvent = z.object({
157
169
  event_type: z.literal("escalate"),
158
170
  requested_by: z.enum(["human", "agent"]).default("human"),
@@ -178,6 +190,7 @@ export const GuavaEvent = z.discriminatedUnion("event_type", [
178
190
  ExecuteActionEvent,
179
191
  EscalateEvent,
180
192
  DTMFPressedEvent,
193
+ AgentDTMFSentEvent,
181
194
  ]);
182
195
  export type GuavaEvent = z.infer<typeof GuavaEvent>;
183
196
 
package/src/index.ts CHANGED
@@ -14,10 +14,12 @@ export { Logger, getConsoleLogger, getDefaultLogger } from "./logging.ts";
14
14
  export { Agent, CallInfo } from "./agent.ts";
15
15
  export { Call, DEFAULT_REACH_PERSON_OUTCOMES } from "./call.ts";
16
16
  export type { ReachPersonOutcome } from "./call.ts";
17
+ export type { SpeedPreset, Language } from "./commands.ts";
17
18
  export type {
18
19
  BotSessionEnded,
19
20
  TerminationReason,
20
21
  DTMFPressedEvent,
22
+ AgentDTMFSentEvent,
21
23
  DTMFDigit,
22
24
  OutboundCallFailed,
23
25
  } from "./events.ts";
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = "0.33.0";
1
+ export const SDK_VERSION = "0.35.0";