@absolutejs/voice 0.0.22-beta.1 → 0.0.22-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/dist/assistant.d.ts +99 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +377 -224
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,88 @@ const app = new Elysia()
|
|
|
73
73
|
|
|
74
74
|
`createVoiceMemoryStore()` is dev-only. Real deployments should provide a shared store backed by Redis, Postgres, or equivalent.
|
|
75
75
|
|
|
76
|
+
## Voice Assistants
|
|
77
|
+
|
|
78
|
+
Use `createVoiceAssistant(...)` when you want one product-level surface for a voice agent instead of wiring tools, guardrails, experiments, traces, and ops recipes separately. It returns a standard `onTurn` handler, plus an `ops` object you can pass to `voice(...)`.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import {
|
|
82
|
+
createVoiceAssistant,
|
|
83
|
+
createVoiceExperiment,
|
|
84
|
+
createVoiceFileRuntimeStorage,
|
|
85
|
+
createVoiceMemoryStore,
|
|
86
|
+
createVoiceAgentTool,
|
|
87
|
+
voice
|
|
88
|
+
} from '@absolutejs/voice';
|
|
89
|
+
import { deepgram } from '@absolutejs/voice-deepgram';
|
|
90
|
+
|
|
91
|
+
const runtimeStorage = createVoiceFileRuntimeStorage({
|
|
92
|
+
directory: '.voice-runtime/support'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const lookupOrder = createVoiceAgentTool({
|
|
96
|
+
name: 'lookup_order',
|
|
97
|
+
description: 'Look up an order by id.',
|
|
98
|
+
execute: async ({ args }) => ({ orderId: args.orderId, status: 'shipped' })
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const assistant = createVoiceAssistant({
|
|
102
|
+
id: 'support',
|
|
103
|
+
artifactPlan: {
|
|
104
|
+
ops: {
|
|
105
|
+
events: runtimeStorage.events,
|
|
106
|
+
reviews: runtimeStorage.reviews,
|
|
107
|
+
tasks: runtimeStorage.tasks
|
|
108
|
+
},
|
|
109
|
+
preset: {
|
|
110
|
+
name: 'support-triage',
|
|
111
|
+
options: {
|
|
112
|
+
queue: 'support-triage'
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
experiment: createVoiceExperiment({
|
|
117
|
+
id: 'support-prompt',
|
|
118
|
+
variants: [
|
|
119
|
+
{ id: 'baseline', weight: 1 },
|
|
120
|
+
{
|
|
121
|
+
id: 'direct',
|
|
122
|
+
weight: 1,
|
|
123
|
+
system: 'You are concise, practical, and resolve the caller quickly.'
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}),
|
|
127
|
+
guardrails: {
|
|
128
|
+
beforeTurn: ({ turn }) =>
|
|
129
|
+
turn.text.toLowerCase().includes('human')
|
|
130
|
+
? { escalate: { reason: 'caller requested a human' } }
|
|
131
|
+
: undefined
|
|
132
|
+
},
|
|
133
|
+
model: {
|
|
134
|
+
async generate({ messages, tools }) {
|
|
135
|
+
return {
|
|
136
|
+
assistantText: `I can help. Available tools: ${tools.map((tool) => tool.name).join(', ')}`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
system: 'You are a support voice assistant.',
|
|
141
|
+
tools: [lookupOrder],
|
|
142
|
+
trace: runtimeStorage.traces
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
voice({
|
|
146
|
+
path: '/voice',
|
|
147
|
+
session: createVoiceMemoryStore(),
|
|
148
|
+
stt: deepgram({ apiKey: process.env.DEEPGRAM_API_KEY! }),
|
|
149
|
+
trace: runtimeStorage.traces,
|
|
150
|
+
ops: assistant.ops,
|
|
151
|
+
onTurn: assistant.onTurn,
|
|
152
|
+
onComplete: async () => {}
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Assistant experiments are deterministic by session id, so a caller stays on the same variant for a call. Variants can change the model, system prompt, tools, and tool-round budget; guardrails can block a turn before model execution or rewrite the returned `VoiceRouteResult`.
|
|
157
|
+
|
|
76
158
|
## Agent Tools And Squads
|
|
77
159
|
|
|
78
160
|
For assistant-style products, use `createVoiceAgent(...)` as the `onTurn` handler. The agent layer is provider-neutral: plug in any model adapter, register server-side tools, and return normal voice route results like `assistantText`, `transfer`, `escalate`, or `complete`.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type VoiceAgent, type VoiceAgentModel, type VoiceAgentOptions, type VoiceAgentSquadOptions, type VoiceAgentTool } from './agent';
|
|
2
|
+
import { type VoiceOutcomeRecipeName, type VoiceOutcomeRecipeOptions } from './outcomeRecipes';
|
|
3
|
+
import type { VoiceNormalizedRouteConfig, VoiceOnTurnObjectHandler, VoiceRouteConfig, VoiceRouteResult, VoiceRuntimeOpsConfig, VoiceSessionRecord } from './types';
|
|
4
|
+
export type VoiceAssistantPreset = VoiceOutcomeRecipeName;
|
|
5
|
+
export type VoiceAssistantArtifactPlan<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
6
|
+
ops?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
|
|
7
|
+
preset?: VoiceAssistantPreset | {
|
|
8
|
+
name: VoiceAssistantPreset;
|
|
9
|
+
options?: VoiceOutcomeRecipeOptions;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export type VoiceAssistantGuardrailInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Parameters<VoiceOnTurnObjectHandler<TContext, TSession, TResult>>[0] & {
|
|
13
|
+
assistantId: string;
|
|
14
|
+
};
|
|
15
|
+
export type VoiceAssistantOutputGuardrailInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = VoiceAssistantGuardrailInput<TContext, TSession, TResult> & {
|
|
16
|
+
result: VoiceRouteResult<TResult>;
|
|
17
|
+
};
|
|
18
|
+
export type VoiceAssistantGuardrails<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
19
|
+
beforeTurn?: (input: VoiceAssistantGuardrailInput<TContext, TSession, TResult>) => Promise<VoiceRouteResult<TResult> | void> | VoiceRouteResult<TResult> | void;
|
|
20
|
+
afterTurn?: (input: VoiceAssistantOutputGuardrailInput<TContext, TSession, TResult>) => Promise<VoiceRouteResult<TResult> | void> | VoiceRouteResult<TResult> | void;
|
|
21
|
+
};
|
|
22
|
+
export type VoiceAssistantVariant<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
23
|
+
id: string;
|
|
24
|
+
maxToolRounds?: number;
|
|
25
|
+
metadata?: Record<string, unknown>;
|
|
26
|
+
model?: VoiceAgentModel<TContext, TSession, TResult>;
|
|
27
|
+
system?: VoiceAgentOptions<TContext, TSession, TResult>['system'];
|
|
28
|
+
tools?: Array<VoiceAgentTool<TContext, TSession, Record<string, unknown>, unknown, TResult>>;
|
|
29
|
+
weight?: number;
|
|
30
|
+
};
|
|
31
|
+
export type VoiceAssistantExperimentResolverInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord> = {
|
|
32
|
+
assistantId: string;
|
|
33
|
+
context: TContext;
|
|
34
|
+
session: TSession;
|
|
35
|
+
turnId?: string;
|
|
36
|
+
};
|
|
37
|
+
export type VoiceAssistantExperiment<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
38
|
+
id: string;
|
|
39
|
+
resolve: (input: VoiceAssistantExperimentResolverInput<TContext, TSession>) => VoiceAssistantVariant<TContext, TSession, TResult>;
|
|
40
|
+
variants: Array<VoiceAssistantVariant<TContext, TSession, TResult>>;
|
|
41
|
+
};
|
|
42
|
+
export type VoiceAssistantExperimentOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
43
|
+
id: string;
|
|
44
|
+
selectVariant?: (input: VoiceAssistantExperimentResolverInput<TContext, TSession> & {
|
|
45
|
+
variants: Array<VoiceAssistantVariant<TContext, TSession, TResult>>;
|
|
46
|
+
}) => VoiceAssistantVariant<TContext, TSession, TResult> | string | void;
|
|
47
|
+
variants: Array<VoiceAssistantVariant<TContext, TSession, TResult>>;
|
|
48
|
+
};
|
|
49
|
+
type VoiceAssistantAgentSource<TContext, TSession extends VoiceSessionRecord, TResult> = {
|
|
50
|
+
agent: VoiceAgent<TContext, TSession, TResult>;
|
|
51
|
+
agents?: never;
|
|
52
|
+
defaultAgentId?: never;
|
|
53
|
+
maxHandoffsPerTurn?: never;
|
|
54
|
+
maxToolRounds?: never;
|
|
55
|
+
model?: never;
|
|
56
|
+
selectAgent?: never;
|
|
57
|
+
system?: never;
|
|
58
|
+
tools?: never;
|
|
59
|
+
} | {
|
|
60
|
+
agent?: never;
|
|
61
|
+
agents: Array<VoiceAgent<TContext, TSession, TResult>>;
|
|
62
|
+
defaultAgentId: string;
|
|
63
|
+
maxHandoffsPerTurn?: number;
|
|
64
|
+
maxToolRounds?: never;
|
|
65
|
+
model?: never;
|
|
66
|
+
selectAgent?: VoiceAgentSquadOptions<TContext, TSession, TResult>['selectAgent'];
|
|
67
|
+
system?: never;
|
|
68
|
+
tools?: never;
|
|
69
|
+
} | {
|
|
70
|
+
agent?: never;
|
|
71
|
+
agents?: never;
|
|
72
|
+
defaultAgentId?: never;
|
|
73
|
+
maxHandoffsPerTurn?: never;
|
|
74
|
+
maxToolRounds?: number;
|
|
75
|
+
model: VoiceAgentModel<TContext, TSession, TResult>;
|
|
76
|
+
selectAgent?: never;
|
|
77
|
+
system?: VoiceAgentOptions<TContext, TSession, TResult>['system'];
|
|
78
|
+
tools?: Array<VoiceAgentTool<TContext, TSession, Record<string, unknown>, unknown, TResult>>;
|
|
79
|
+
};
|
|
80
|
+
export type VoiceAssistantOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = VoiceAssistantAgentSource<TContext, TSession, TResult> & {
|
|
81
|
+
artifactPlan?: VoiceAssistantArtifactPlan<TContext, TSession, TResult>;
|
|
82
|
+
experiment?: VoiceAssistantExperiment<TContext, TSession, TResult>;
|
|
83
|
+
guardrails?: VoiceAssistantGuardrails<TContext, TSession, TResult>;
|
|
84
|
+
id: string;
|
|
85
|
+
ops?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
|
|
86
|
+
trace?: VoiceAgentOptions<TContext, TSession, TResult>['trace'];
|
|
87
|
+
};
|
|
88
|
+
export type VoiceAssistant<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
89
|
+
agent: VoiceAgent<TContext, TSession, TResult>;
|
|
90
|
+
id: string;
|
|
91
|
+
onTurn: VoiceOnTurnObjectHandler<TContext, TSession, TResult>;
|
|
92
|
+
ops?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
|
|
93
|
+
route: (overrides: Omit<VoiceRouteConfig<TContext, TSession, TResult>, 'onComplete' | 'onTurn'> & {
|
|
94
|
+
onComplete?: VoiceRouteConfig<TContext, TSession, TResult>['onComplete'];
|
|
95
|
+
}) => VoiceNormalizedRouteConfig<TContext, TSession, TResult>;
|
|
96
|
+
};
|
|
97
|
+
export declare const createVoiceExperiment: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceAssistantExperimentOptions<TContext, TSession, TResult>) => VoiceAssistantExperiment<TContext, TSession, TResult>;
|
|
98
|
+
export declare const createVoiceAssistant: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoiceAssistantOptions<TContext, TSession, TResult>) => VoiceAssistant<TContext, TSession, TResult>;
|
|
99
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { voice } from './plugin';
|
|
2
|
+
export { createVoiceAssistant, createVoiceExperiment } from './assistant';
|
|
2
3
|
export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
|
|
3
4
|
export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
|
|
4
5
|
export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceTraceHTTPSink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
|
|
@@ -21,6 +22,7 @@ export { conditionAudioChunk, resolveAudioConditioningConfig } from './audioCond
|
|
|
21
22
|
export { resolveVoiceRuntimePreset } from './presets';
|
|
22
23
|
export { resolveTurnDetectionConfig, TURN_PROFILE_DEFAULTS } from './turnProfiles';
|
|
23
24
|
export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewRecorder, renderVoiceCallReviewHTML, renderVoiceCallReviewMarkdown } from './testing/review';
|
|
25
|
+
export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantVariant } from './assistant';
|
|
24
26
|
export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
|
|
25
27
|
export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
|
|
26
28
|
export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
|
package/dist/index.js
CHANGED
|
@@ -5368,6 +5368,381 @@ var createVoiceAgentSquad = (options) => {
|
|
|
5368
5368
|
run
|
|
5369
5369
|
};
|
|
5370
5370
|
};
|
|
5371
|
+
|
|
5372
|
+
// src/outcomeRecipes.ts
|
|
5373
|
+
var RECIPE_DEFAULTS = {
|
|
5374
|
+
"appointment-booking": {
|
|
5375
|
+
completedAction: "Verify appointment details, confirm calendar state, and send any required confirmation.",
|
|
5376
|
+
completedDescription: "The call completed an appointment-booking flow and should be checked against the scheduling system.",
|
|
5377
|
+
completedKind: "appointment-booking",
|
|
5378
|
+
completedTitle: "Confirm booked appointment",
|
|
5379
|
+
defaultCompletedCreatesTask: true,
|
|
5380
|
+
defaultDueInMs: 30 * 60000,
|
|
5381
|
+
defaultPriority: "normal",
|
|
5382
|
+
defaultQueue: "appointments",
|
|
5383
|
+
description: "Creates appointment confirmation work for completed calls and callback/retry work for missed booking attempts.",
|
|
5384
|
+
escalationQueue: "appointments-escalations"
|
|
5385
|
+
},
|
|
5386
|
+
"lead-qualification": {
|
|
5387
|
+
completedAction: "Review qualification signals, update CRM fields, and route the lead to the right owner.",
|
|
5388
|
+
completedDescription: "The call completed a lead-qualification flow and should be reviewed for sales follow-up.",
|
|
5389
|
+
completedKind: "lead-qualification",
|
|
5390
|
+
completedTitle: "Review qualified lead",
|
|
5391
|
+
defaultCompletedCreatesTask: true,
|
|
5392
|
+
defaultDueInMs: 15 * 60000,
|
|
5393
|
+
defaultPriority: "high",
|
|
5394
|
+
defaultQueue: "sales-leads",
|
|
5395
|
+
description: "Creates sales follow-up work for completed qualification calls and fast callbacks for missed leads.",
|
|
5396
|
+
escalationQueue: "sales-escalations"
|
|
5397
|
+
},
|
|
5398
|
+
"support-triage": {
|
|
5399
|
+
completedAction: "Review the triage result, confirm the support category, and route any unresolved issue.",
|
|
5400
|
+
completedDescription: "The call completed support triage and may need queue routing or human follow-up.",
|
|
5401
|
+
completedKind: "support-triage",
|
|
5402
|
+
completedTitle: "Review support triage",
|
|
5403
|
+
defaultCompletedCreatesTask: true,
|
|
5404
|
+
defaultDueInMs: 20 * 60000,
|
|
5405
|
+
defaultPriority: "normal",
|
|
5406
|
+
defaultQueue: "support-triage",
|
|
5407
|
+
description: "Creates support triage work for completed calls and urgent escalation/callback work for unresolved callers.",
|
|
5408
|
+
escalationQueue: "support-escalations"
|
|
5409
|
+
},
|
|
5410
|
+
"voicemail-callback": {
|
|
5411
|
+
completedAction: "No callback is required for completed calls.",
|
|
5412
|
+
completedDescription: "The call completed without requiring voicemail follow-up.",
|
|
5413
|
+
completedKind: "callback",
|
|
5414
|
+
completedTitle: "Completed call",
|
|
5415
|
+
defaultCompletedCreatesTask: false,
|
|
5416
|
+
defaultDueInMs: 15 * 60000,
|
|
5417
|
+
defaultPriority: "high",
|
|
5418
|
+
defaultQueue: "callbacks",
|
|
5419
|
+
description: "Creates callback work for voicemail, no-answer, failed, or escalated calls while ignoring completed calls.",
|
|
5420
|
+
escalationQueue: "callback-escalations"
|
|
5421
|
+
},
|
|
5422
|
+
"warm-transfer": {
|
|
5423
|
+
completedAction: "Confirm the handoff target received the caller context and close the transfer loop.",
|
|
5424
|
+
completedDescription: "The call is part of a warm-transfer flow and should be verified downstream.",
|
|
5425
|
+
completedKind: "transfer-check",
|
|
5426
|
+
completedTitle: "Verify warm transfer",
|
|
5427
|
+
defaultCompletedCreatesTask: false,
|
|
5428
|
+
defaultDueInMs: 10 * 60000,
|
|
5429
|
+
defaultPriority: "normal",
|
|
5430
|
+
defaultQueue: "transfer-verification",
|
|
5431
|
+
description: "Creates transfer verification work for transferred calls and escalation work when the handoff fails.",
|
|
5432
|
+
escalationQueue: "transfer-escalations"
|
|
5433
|
+
}
|
|
5434
|
+
};
|
|
5435
|
+
var buildRecipeTask = (input) => {
|
|
5436
|
+
const createdAt = input.review.generatedAt ?? Date.now();
|
|
5437
|
+
const queue = input.options.queue ?? input.defaults.defaultQueue;
|
|
5438
|
+
const target = input.options.target ?? input.review.postCall?.target;
|
|
5439
|
+
const common = {
|
|
5440
|
+
assignee: input.options.assignee,
|
|
5441
|
+
createdAt,
|
|
5442
|
+
history: [
|
|
5443
|
+
{
|
|
5444
|
+
actor: "system",
|
|
5445
|
+
at: createdAt,
|
|
5446
|
+
detail: input.review.postCall?.summary,
|
|
5447
|
+
type: "created"
|
|
5448
|
+
}
|
|
5449
|
+
],
|
|
5450
|
+
id: `${input.review.id}:${input.defaults.completedKind}`,
|
|
5451
|
+
intakeId: input.review.id,
|
|
5452
|
+
outcome: input.review.summary.outcome,
|
|
5453
|
+
priority: input.options.priority ?? input.defaults.defaultPriority,
|
|
5454
|
+
queue,
|
|
5455
|
+
reviewId: input.review.id,
|
|
5456
|
+
status: "open",
|
|
5457
|
+
target,
|
|
5458
|
+
updatedAt: createdAt
|
|
5459
|
+
};
|
|
5460
|
+
switch (input.disposition) {
|
|
5461
|
+
case "completed":
|
|
5462
|
+
if (!(input.options.completedCreatesTask ?? input.defaults.defaultCompletedCreatesTask)) {
|
|
5463
|
+
return null;
|
|
5464
|
+
}
|
|
5465
|
+
return {
|
|
5466
|
+
...common,
|
|
5467
|
+
description: input.defaults.completedDescription,
|
|
5468
|
+
kind: input.defaults.completedKind,
|
|
5469
|
+
recommendedAction: input.defaults.completedAction,
|
|
5470
|
+
title: target ? `${input.defaults.completedTitle}: ${target}` : input.defaults.completedTitle
|
|
5471
|
+
};
|
|
5472
|
+
case "voicemail":
|
|
5473
|
+
return {
|
|
5474
|
+
...common,
|
|
5475
|
+
description: input.review.postCall?.summary ?? "The caller reached voicemail and needs a callback.",
|
|
5476
|
+
id: `${input.review.id}:callback`,
|
|
5477
|
+
kind: "callback",
|
|
5478
|
+
recommendedAction: input.review.postCall?.recommendedAction ?? "Call the customer back and continue the original flow.",
|
|
5479
|
+
title: target ? `Call back ${target}` : "Call back voicemail lead"
|
|
5480
|
+
};
|
|
5481
|
+
case "no-answer":
|
|
5482
|
+
return {
|
|
5483
|
+
...common,
|
|
5484
|
+
description: input.review.postCall?.summary ?? "The call did not reach a live respondent and should be retried.",
|
|
5485
|
+
id: `${input.review.id}:retry`,
|
|
5486
|
+
kind: "callback",
|
|
5487
|
+
recommendedAction: input.review.postCall?.recommendedAction ?? "Retry the call or schedule a callback.",
|
|
5488
|
+
title: "Retry no-answer call"
|
|
5489
|
+
};
|
|
5490
|
+
case "transferred":
|
|
5491
|
+
return {
|
|
5492
|
+
...common,
|
|
5493
|
+
description: input.review.postCall?.summary ?? "The call was transferred and should be verified downstream.",
|
|
5494
|
+
id: `${input.review.id}:transfer-check`,
|
|
5495
|
+
kind: "transfer-check",
|
|
5496
|
+
recommendedAction: input.review.postCall?.recommendedAction ?? "Confirm the receiving team got the caller context.",
|
|
5497
|
+
title: target ? `Verify transfer to ${target}` : "Verify call transfer"
|
|
5498
|
+
};
|
|
5499
|
+
case "escalated":
|
|
5500
|
+
return {
|
|
5501
|
+
...common,
|
|
5502
|
+
description: input.review.postCall?.summary ?? "The call escalated and needs human review.",
|
|
5503
|
+
id: `${input.review.id}:escalation`,
|
|
5504
|
+
kind: "escalation",
|
|
5505
|
+
priority: "urgent",
|
|
5506
|
+
queue: input.options.escalationQueue ?? input.defaults.escalationQueue,
|
|
5507
|
+
assignee: input.options.escalationAssignee ?? input.options.assignee,
|
|
5508
|
+
recommendedAction: input.review.postCall?.recommendedAction ?? "Review the escalated call and respond immediately.",
|
|
5509
|
+
title: "Review escalated call"
|
|
5510
|
+
};
|
|
5511
|
+
case "failed":
|
|
5512
|
+
case "closed":
|
|
5513
|
+
return {
|
|
5514
|
+
...common,
|
|
5515
|
+
description: input.review.postCall?.summary ?? "The call ended before successful completion and needs review.",
|
|
5516
|
+
id: `${input.review.id}:retry-review`,
|
|
5517
|
+
kind: "retry-review",
|
|
5518
|
+
priority: "high",
|
|
5519
|
+
recommendedAction: input.review.postCall?.recommendedAction ?? "Inspect the call and decide whether to retry, escalate, or close.",
|
|
5520
|
+
title: "Inspect incomplete call"
|
|
5521
|
+
};
|
|
5522
|
+
default:
|
|
5523
|
+
return null;
|
|
5524
|
+
}
|
|
5525
|
+
};
|
|
5526
|
+
var resolveVoiceOutcomeRecipe = (name, options = {}) => {
|
|
5527
|
+
const defaults = RECIPE_DEFAULTS[name];
|
|
5528
|
+
const taskPolicies = {
|
|
5529
|
+
completed: {
|
|
5530
|
+
assignee: options.assignee,
|
|
5531
|
+
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
5532
|
+
name: `${name}-completed`,
|
|
5533
|
+
priority: options.priority ?? defaults.defaultPriority,
|
|
5534
|
+
queue: options.queue ?? defaults.defaultQueue
|
|
5535
|
+
},
|
|
5536
|
+
escalated: {
|
|
5537
|
+
assignee: options.escalationAssignee ?? options.assignee,
|
|
5538
|
+
dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 10 * 60000),
|
|
5539
|
+
name: `${name}-escalation`,
|
|
5540
|
+
priority: "urgent",
|
|
5541
|
+
queue: options.escalationQueue ?? defaults.escalationQueue
|
|
5542
|
+
},
|
|
5543
|
+
failed: {
|
|
5544
|
+
assignee: options.assignee,
|
|
5545
|
+
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
5546
|
+
name: `${name}-failed-review`,
|
|
5547
|
+
priority: "high",
|
|
5548
|
+
queue: options.queue ?? defaults.defaultQueue
|
|
5549
|
+
},
|
|
5550
|
+
"no-answer": {
|
|
5551
|
+
assignee: options.assignee,
|
|
5552
|
+
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
5553
|
+
name: `${name}-no-answer`,
|
|
5554
|
+
priority: options.priority ?? defaults.defaultPriority,
|
|
5555
|
+
queue: options.queue ?? defaults.defaultQueue
|
|
5556
|
+
},
|
|
5557
|
+
transferred: {
|
|
5558
|
+
assignee: options.assignee,
|
|
5559
|
+
dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 20 * 60000),
|
|
5560
|
+
name: `${name}-transfer-check`,
|
|
5561
|
+
priority: options.priority ?? defaults.defaultPriority,
|
|
5562
|
+
queue: name === "warm-transfer" ? options.queue ?? defaults.defaultQueue : "transfer-verification"
|
|
5563
|
+
},
|
|
5564
|
+
voicemail: {
|
|
5565
|
+
assignee: options.assignee,
|
|
5566
|
+
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
5567
|
+
name: `${name}-voicemail`,
|
|
5568
|
+
priority: options.priority ?? defaults.defaultPriority,
|
|
5569
|
+
queue: options.queue ?? defaults.defaultQueue
|
|
5570
|
+
}
|
|
5571
|
+
};
|
|
5572
|
+
const taskAssignmentRules = [
|
|
5573
|
+
{
|
|
5574
|
+
assign: options.escalationAssignee ?? options.assignee,
|
|
5575
|
+
description: `Route urgent ${name} work to the escalation lane.`,
|
|
5576
|
+
name: `${name}-urgent-routing`,
|
|
5577
|
+
queue: options.escalationQueue ?? defaults.escalationQueue,
|
|
5578
|
+
when: {
|
|
5579
|
+
priority: "urgent"
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
].filter((rule) => rule.assign || rule.queue);
|
|
5583
|
+
return {
|
|
5584
|
+
createTaskFromReview: ({ disposition, review }) => buildRecipeTask({
|
|
5585
|
+
defaults,
|
|
5586
|
+
disposition,
|
|
5587
|
+
options,
|
|
5588
|
+
review
|
|
5589
|
+
}),
|
|
5590
|
+
description: defaults.description,
|
|
5591
|
+
name,
|
|
5592
|
+
taskAssignmentRules,
|
|
5593
|
+
taskPolicies
|
|
5594
|
+
};
|
|
5595
|
+
};
|
|
5596
|
+
|
|
5597
|
+
// src/assistant.ts
|
|
5598
|
+
var hashString = (value) => {
|
|
5599
|
+
let hash = 2166136261;
|
|
5600
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
5601
|
+
hash ^= value.charCodeAt(index);
|
|
5602
|
+
hash = Math.imul(hash, 16777619);
|
|
5603
|
+
}
|
|
5604
|
+
return hash >>> 0;
|
|
5605
|
+
};
|
|
5606
|
+
var resolvePresetOps = (artifactPlan) => {
|
|
5607
|
+
const preset = artifactPlan?.preset;
|
|
5608
|
+
if (!preset) {
|
|
5609
|
+
return artifactPlan?.ops;
|
|
5610
|
+
}
|
|
5611
|
+
const recipe = typeof preset === "string" ? resolveVoiceOutcomeRecipe(preset) : resolveVoiceOutcomeRecipe(preset.name, preset.options);
|
|
5612
|
+
return {
|
|
5613
|
+
...recipe,
|
|
5614
|
+
...artifactPlan?.ops
|
|
5615
|
+
};
|
|
5616
|
+
};
|
|
5617
|
+
var mergeOps = (base, override) => {
|
|
5618
|
+
if (!base && !override) {
|
|
5619
|
+
return;
|
|
5620
|
+
}
|
|
5621
|
+
return {
|
|
5622
|
+
...base,
|
|
5623
|
+
...override,
|
|
5624
|
+
taskAssignmentRules: base?.taskAssignmentRules || override?.taskAssignmentRules ? [
|
|
5625
|
+
...base?.taskAssignmentRules ?? [],
|
|
5626
|
+
...override?.taskAssignmentRules ?? []
|
|
5627
|
+
] : undefined,
|
|
5628
|
+
taskPolicies: base?.taskPolicies || override?.taskPolicies ? {
|
|
5629
|
+
...base?.taskPolicies ?? {},
|
|
5630
|
+
...override?.taskPolicies ?? {}
|
|
5631
|
+
} : undefined
|
|
5632
|
+
};
|
|
5633
|
+
};
|
|
5634
|
+
var createVoiceExperiment = (options) => {
|
|
5635
|
+
if (!options.variants.length) {
|
|
5636
|
+
throw new Error("createVoiceExperiment requires at least one variant.");
|
|
5637
|
+
}
|
|
5638
|
+
const firstVariant = options.variants[0];
|
|
5639
|
+
return {
|
|
5640
|
+
id: options.id,
|
|
5641
|
+
resolve: (input) => {
|
|
5642
|
+
const selected = options.selectVariant?.({
|
|
5643
|
+
...input,
|
|
5644
|
+
variants: options.variants
|
|
5645
|
+
});
|
|
5646
|
+
if (selected && typeof selected !== "object") {
|
|
5647
|
+
const variant = options.variants.find((item) => item.id === selected);
|
|
5648
|
+
if (variant) {
|
|
5649
|
+
return variant;
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
if (selected && typeof selected === "object" && "id" in selected) {
|
|
5653
|
+
return selected;
|
|
5654
|
+
}
|
|
5655
|
+
const totalWeight = options.variants.reduce((total, variant) => total + Math.max(0, variant.weight ?? 1), 0);
|
|
5656
|
+
if (totalWeight <= 0) {
|
|
5657
|
+
return firstVariant;
|
|
5658
|
+
}
|
|
5659
|
+
const bucket = hashString(`${options.id}:${input.assistantId}:${input.session.id}`) % totalWeight;
|
|
5660
|
+
let cursor = 0;
|
|
5661
|
+
for (const variant of options.variants) {
|
|
5662
|
+
cursor += Math.max(0, variant.weight ?? 1);
|
|
5663
|
+
if (bucket < cursor) {
|
|
5664
|
+
return variant;
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5667
|
+
return firstVariant;
|
|
5668
|
+
},
|
|
5669
|
+
variants: options.variants
|
|
5670
|
+
};
|
|
5671
|
+
};
|
|
5672
|
+
var createVoiceAssistant = (options) => {
|
|
5673
|
+
const ops = mergeOps(resolvePresetOps(options.artifactPlan), options.ops);
|
|
5674
|
+
let agent;
|
|
5675
|
+
const baseModelOptions = "model" in options && options.model ? {
|
|
5676
|
+
maxToolRounds: options.maxToolRounds,
|
|
5677
|
+
model: options.model,
|
|
5678
|
+
system: options.system,
|
|
5679
|
+
tools: options.tools
|
|
5680
|
+
} : undefined;
|
|
5681
|
+
if ("agent" in options && options.agent) {
|
|
5682
|
+
agent = options.agent;
|
|
5683
|
+
} else if ("agents" in options && options.agents) {
|
|
5684
|
+
agent = createVoiceAgentSquad({
|
|
5685
|
+
agents: options.agents,
|
|
5686
|
+
defaultAgentId: options.defaultAgentId,
|
|
5687
|
+
id: options.id,
|
|
5688
|
+
maxHandoffsPerTurn: options.maxHandoffsPerTurn,
|
|
5689
|
+
selectAgent: options.selectAgent,
|
|
5690
|
+
trace: options.trace
|
|
5691
|
+
});
|
|
5692
|
+
} else {
|
|
5693
|
+
agent = createVoiceAgent({
|
|
5694
|
+
id: options.id,
|
|
5695
|
+
maxToolRounds: options.maxToolRounds,
|
|
5696
|
+
model: options.model,
|
|
5697
|
+
system: options.system,
|
|
5698
|
+
trace: options.trace,
|
|
5699
|
+
tools: options.tools
|
|
5700
|
+
});
|
|
5701
|
+
}
|
|
5702
|
+
const onTurn = async (input) => {
|
|
5703
|
+
const guardrailInput = {
|
|
5704
|
+
...input,
|
|
5705
|
+
assistantId: options.id
|
|
5706
|
+
};
|
|
5707
|
+
const blocked = await options.guardrails?.beforeTurn?.(guardrailInput);
|
|
5708
|
+
if (blocked) {
|
|
5709
|
+
return blocked;
|
|
5710
|
+
}
|
|
5711
|
+
const variant = options.experiment?.resolve({
|
|
5712
|
+
assistantId: options.id,
|
|
5713
|
+
context: input.context,
|
|
5714
|
+
session: input.session,
|
|
5715
|
+
turnId: input.turn.id
|
|
5716
|
+
});
|
|
5717
|
+
const runner = variant && baseModelOptions ? createVoiceAgent({
|
|
5718
|
+
id: `${options.id}:${variant.id}`,
|
|
5719
|
+
maxToolRounds: variant.maxToolRounds ?? baseModelOptions.maxToolRounds,
|
|
5720
|
+
model: variant.model ?? baseModelOptions.model,
|
|
5721
|
+
system: variant.system ?? baseModelOptions.system,
|
|
5722
|
+
trace: options.trace,
|
|
5723
|
+
tools: variant.tools ?? baseModelOptions.tools
|
|
5724
|
+
}) : agent;
|
|
5725
|
+
const result = await runner.run(input) ?? {};
|
|
5726
|
+
const guarded = await options.guardrails?.afterTurn?.({
|
|
5727
|
+
...guardrailInput,
|
|
5728
|
+
result
|
|
5729
|
+
});
|
|
5730
|
+
return guarded ?? result;
|
|
5731
|
+
};
|
|
5732
|
+
return {
|
|
5733
|
+
agent,
|
|
5734
|
+
id: options.id,
|
|
5735
|
+
onTurn,
|
|
5736
|
+
ops,
|
|
5737
|
+
route: (overrides) => ({
|
|
5738
|
+
...overrides,
|
|
5739
|
+
onComplete: overrides.onComplete ?? (() => {
|
|
5740
|
+
return;
|
|
5741
|
+
}),
|
|
5742
|
+
onTurn
|
|
5743
|
+
})
|
|
5744
|
+
};
|
|
5745
|
+
};
|
|
5371
5746
|
// src/fileStore.ts
|
|
5372
5747
|
import { mkdir, readFile, readdir, rename, rm, writeFile } from "fs/promises";
|
|
5373
5748
|
import { join } from "path";
|
|
@@ -7987,230 +8362,6 @@ var resolveVoiceOpsPreset = (name, overrides = {}) => {
|
|
|
7987
8362
|
taskPolicies: mergePolicies(preset.taskPolicies, overrides.taskPolicies)
|
|
7988
8363
|
};
|
|
7989
8364
|
};
|
|
7990
|
-
// src/outcomeRecipes.ts
|
|
7991
|
-
var RECIPE_DEFAULTS = {
|
|
7992
|
-
"appointment-booking": {
|
|
7993
|
-
completedAction: "Verify appointment details, confirm calendar state, and send any required confirmation.",
|
|
7994
|
-
completedDescription: "The call completed an appointment-booking flow and should be checked against the scheduling system.",
|
|
7995
|
-
completedKind: "appointment-booking",
|
|
7996
|
-
completedTitle: "Confirm booked appointment",
|
|
7997
|
-
defaultCompletedCreatesTask: true,
|
|
7998
|
-
defaultDueInMs: 30 * 60000,
|
|
7999
|
-
defaultPriority: "normal",
|
|
8000
|
-
defaultQueue: "appointments",
|
|
8001
|
-
description: "Creates appointment confirmation work for completed calls and callback/retry work for missed booking attempts.",
|
|
8002
|
-
escalationQueue: "appointments-escalations"
|
|
8003
|
-
},
|
|
8004
|
-
"lead-qualification": {
|
|
8005
|
-
completedAction: "Review qualification signals, update CRM fields, and route the lead to the right owner.",
|
|
8006
|
-
completedDescription: "The call completed a lead-qualification flow and should be reviewed for sales follow-up.",
|
|
8007
|
-
completedKind: "lead-qualification",
|
|
8008
|
-
completedTitle: "Review qualified lead",
|
|
8009
|
-
defaultCompletedCreatesTask: true,
|
|
8010
|
-
defaultDueInMs: 15 * 60000,
|
|
8011
|
-
defaultPriority: "high",
|
|
8012
|
-
defaultQueue: "sales-leads",
|
|
8013
|
-
description: "Creates sales follow-up work for completed qualification calls and fast callbacks for missed leads.",
|
|
8014
|
-
escalationQueue: "sales-escalations"
|
|
8015
|
-
},
|
|
8016
|
-
"support-triage": {
|
|
8017
|
-
completedAction: "Review the triage result, confirm the support category, and route any unresolved issue.",
|
|
8018
|
-
completedDescription: "The call completed support triage and may need queue routing or human follow-up.",
|
|
8019
|
-
completedKind: "support-triage",
|
|
8020
|
-
completedTitle: "Review support triage",
|
|
8021
|
-
defaultCompletedCreatesTask: true,
|
|
8022
|
-
defaultDueInMs: 20 * 60000,
|
|
8023
|
-
defaultPriority: "normal",
|
|
8024
|
-
defaultQueue: "support-triage",
|
|
8025
|
-
description: "Creates support triage work for completed calls and urgent escalation/callback work for unresolved callers.",
|
|
8026
|
-
escalationQueue: "support-escalations"
|
|
8027
|
-
},
|
|
8028
|
-
"voicemail-callback": {
|
|
8029
|
-
completedAction: "No callback is required for completed calls.",
|
|
8030
|
-
completedDescription: "The call completed without requiring voicemail follow-up.",
|
|
8031
|
-
completedKind: "callback",
|
|
8032
|
-
completedTitle: "Completed call",
|
|
8033
|
-
defaultCompletedCreatesTask: false,
|
|
8034
|
-
defaultDueInMs: 15 * 60000,
|
|
8035
|
-
defaultPriority: "high",
|
|
8036
|
-
defaultQueue: "callbacks",
|
|
8037
|
-
description: "Creates callback work for voicemail, no-answer, failed, or escalated calls while ignoring completed calls.",
|
|
8038
|
-
escalationQueue: "callback-escalations"
|
|
8039
|
-
},
|
|
8040
|
-
"warm-transfer": {
|
|
8041
|
-
completedAction: "Confirm the handoff target received the caller context and close the transfer loop.",
|
|
8042
|
-
completedDescription: "The call is part of a warm-transfer flow and should be verified downstream.",
|
|
8043
|
-
completedKind: "transfer-check",
|
|
8044
|
-
completedTitle: "Verify warm transfer",
|
|
8045
|
-
defaultCompletedCreatesTask: false,
|
|
8046
|
-
defaultDueInMs: 10 * 60000,
|
|
8047
|
-
defaultPriority: "normal",
|
|
8048
|
-
defaultQueue: "transfer-verification",
|
|
8049
|
-
description: "Creates transfer verification work for transferred calls and escalation work when the handoff fails.",
|
|
8050
|
-
escalationQueue: "transfer-escalations"
|
|
8051
|
-
}
|
|
8052
|
-
};
|
|
8053
|
-
var buildRecipeTask = (input) => {
|
|
8054
|
-
const createdAt = input.review.generatedAt ?? Date.now();
|
|
8055
|
-
const queue = input.options.queue ?? input.defaults.defaultQueue;
|
|
8056
|
-
const target = input.options.target ?? input.review.postCall?.target;
|
|
8057
|
-
const common = {
|
|
8058
|
-
assignee: input.options.assignee,
|
|
8059
|
-
createdAt,
|
|
8060
|
-
history: [
|
|
8061
|
-
{
|
|
8062
|
-
actor: "system",
|
|
8063
|
-
at: createdAt,
|
|
8064
|
-
detail: input.review.postCall?.summary,
|
|
8065
|
-
type: "created"
|
|
8066
|
-
}
|
|
8067
|
-
],
|
|
8068
|
-
id: `${input.review.id}:${input.defaults.completedKind}`,
|
|
8069
|
-
intakeId: input.review.id,
|
|
8070
|
-
outcome: input.review.summary.outcome,
|
|
8071
|
-
priority: input.options.priority ?? input.defaults.defaultPriority,
|
|
8072
|
-
queue,
|
|
8073
|
-
reviewId: input.review.id,
|
|
8074
|
-
status: "open",
|
|
8075
|
-
target,
|
|
8076
|
-
updatedAt: createdAt
|
|
8077
|
-
};
|
|
8078
|
-
switch (input.disposition) {
|
|
8079
|
-
case "completed":
|
|
8080
|
-
if (!(input.options.completedCreatesTask ?? input.defaults.defaultCompletedCreatesTask)) {
|
|
8081
|
-
return null;
|
|
8082
|
-
}
|
|
8083
|
-
return {
|
|
8084
|
-
...common,
|
|
8085
|
-
description: input.defaults.completedDescription,
|
|
8086
|
-
kind: input.defaults.completedKind,
|
|
8087
|
-
recommendedAction: input.defaults.completedAction,
|
|
8088
|
-
title: target ? `${input.defaults.completedTitle}: ${target}` : input.defaults.completedTitle
|
|
8089
|
-
};
|
|
8090
|
-
case "voicemail":
|
|
8091
|
-
return {
|
|
8092
|
-
...common,
|
|
8093
|
-
description: input.review.postCall?.summary ?? "The caller reached voicemail and needs a callback.",
|
|
8094
|
-
id: `${input.review.id}:callback`,
|
|
8095
|
-
kind: "callback",
|
|
8096
|
-
recommendedAction: input.review.postCall?.recommendedAction ?? "Call the customer back and continue the original flow.",
|
|
8097
|
-
title: target ? `Call back ${target}` : "Call back voicemail lead"
|
|
8098
|
-
};
|
|
8099
|
-
case "no-answer":
|
|
8100
|
-
return {
|
|
8101
|
-
...common,
|
|
8102
|
-
description: input.review.postCall?.summary ?? "The call did not reach a live respondent and should be retried.",
|
|
8103
|
-
id: `${input.review.id}:retry`,
|
|
8104
|
-
kind: "callback",
|
|
8105
|
-
recommendedAction: input.review.postCall?.recommendedAction ?? "Retry the call or schedule a callback.",
|
|
8106
|
-
title: "Retry no-answer call"
|
|
8107
|
-
};
|
|
8108
|
-
case "transferred":
|
|
8109
|
-
return {
|
|
8110
|
-
...common,
|
|
8111
|
-
description: input.review.postCall?.summary ?? "The call was transferred and should be verified downstream.",
|
|
8112
|
-
id: `${input.review.id}:transfer-check`,
|
|
8113
|
-
kind: "transfer-check",
|
|
8114
|
-
recommendedAction: input.review.postCall?.recommendedAction ?? "Confirm the receiving team got the caller context.",
|
|
8115
|
-
title: target ? `Verify transfer to ${target}` : "Verify call transfer"
|
|
8116
|
-
};
|
|
8117
|
-
case "escalated":
|
|
8118
|
-
return {
|
|
8119
|
-
...common,
|
|
8120
|
-
description: input.review.postCall?.summary ?? "The call escalated and needs human review.",
|
|
8121
|
-
id: `${input.review.id}:escalation`,
|
|
8122
|
-
kind: "escalation",
|
|
8123
|
-
priority: "urgent",
|
|
8124
|
-
queue: input.options.escalationQueue ?? input.defaults.escalationQueue,
|
|
8125
|
-
assignee: input.options.escalationAssignee ?? input.options.assignee,
|
|
8126
|
-
recommendedAction: input.review.postCall?.recommendedAction ?? "Review the escalated call and respond immediately.",
|
|
8127
|
-
title: "Review escalated call"
|
|
8128
|
-
};
|
|
8129
|
-
case "failed":
|
|
8130
|
-
case "closed":
|
|
8131
|
-
return {
|
|
8132
|
-
...common,
|
|
8133
|
-
description: input.review.postCall?.summary ?? "The call ended before successful completion and needs review.",
|
|
8134
|
-
id: `${input.review.id}:retry-review`,
|
|
8135
|
-
kind: "retry-review",
|
|
8136
|
-
priority: "high",
|
|
8137
|
-
recommendedAction: input.review.postCall?.recommendedAction ?? "Inspect the call and decide whether to retry, escalate, or close.",
|
|
8138
|
-
title: "Inspect incomplete call"
|
|
8139
|
-
};
|
|
8140
|
-
default:
|
|
8141
|
-
return null;
|
|
8142
|
-
}
|
|
8143
|
-
};
|
|
8144
|
-
var resolveVoiceOutcomeRecipe = (name, options = {}) => {
|
|
8145
|
-
const defaults = RECIPE_DEFAULTS[name];
|
|
8146
|
-
const taskPolicies = {
|
|
8147
|
-
completed: {
|
|
8148
|
-
assignee: options.assignee,
|
|
8149
|
-
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
8150
|
-
name: `${name}-completed`,
|
|
8151
|
-
priority: options.priority ?? defaults.defaultPriority,
|
|
8152
|
-
queue: options.queue ?? defaults.defaultQueue
|
|
8153
|
-
},
|
|
8154
|
-
escalated: {
|
|
8155
|
-
assignee: options.escalationAssignee ?? options.assignee,
|
|
8156
|
-
dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 10 * 60000),
|
|
8157
|
-
name: `${name}-escalation`,
|
|
8158
|
-
priority: "urgent",
|
|
8159
|
-
queue: options.escalationQueue ?? defaults.escalationQueue
|
|
8160
|
-
},
|
|
8161
|
-
failed: {
|
|
8162
|
-
assignee: options.assignee,
|
|
8163
|
-
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
8164
|
-
name: `${name}-failed-review`,
|
|
8165
|
-
priority: "high",
|
|
8166
|
-
queue: options.queue ?? defaults.defaultQueue
|
|
8167
|
-
},
|
|
8168
|
-
"no-answer": {
|
|
8169
|
-
assignee: options.assignee,
|
|
8170
|
-
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
8171
|
-
name: `${name}-no-answer`,
|
|
8172
|
-
priority: options.priority ?? defaults.defaultPriority,
|
|
8173
|
-
queue: options.queue ?? defaults.defaultQueue
|
|
8174
|
-
},
|
|
8175
|
-
transferred: {
|
|
8176
|
-
assignee: options.assignee,
|
|
8177
|
-
dueInMs: Math.min(options.dueInMs ?? defaults.defaultDueInMs, 20 * 60000),
|
|
8178
|
-
name: `${name}-transfer-check`,
|
|
8179
|
-
priority: options.priority ?? defaults.defaultPriority,
|
|
8180
|
-
queue: name === "warm-transfer" ? options.queue ?? defaults.defaultQueue : "transfer-verification"
|
|
8181
|
-
},
|
|
8182
|
-
voicemail: {
|
|
8183
|
-
assignee: options.assignee,
|
|
8184
|
-
dueInMs: options.dueInMs ?? defaults.defaultDueInMs,
|
|
8185
|
-
name: `${name}-voicemail`,
|
|
8186
|
-
priority: options.priority ?? defaults.defaultPriority,
|
|
8187
|
-
queue: options.queue ?? defaults.defaultQueue
|
|
8188
|
-
}
|
|
8189
|
-
};
|
|
8190
|
-
const taskAssignmentRules = [
|
|
8191
|
-
{
|
|
8192
|
-
assign: options.escalationAssignee ?? options.assignee,
|
|
8193
|
-
description: `Route urgent ${name} work to the escalation lane.`,
|
|
8194
|
-
name: `${name}-urgent-routing`,
|
|
8195
|
-
queue: options.escalationQueue ?? defaults.escalationQueue,
|
|
8196
|
-
when: {
|
|
8197
|
-
priority: "urgent"
|
|
8198
|
-
}
|
|
8199
|
-
}
|
|
8200
|
-
].filter((rule) => rule.assign || rule.queue);
|
|
8201
|
-
return {
|
|
8202
|
-
createTaskFromReview: ({ disposition, review }) => buildRecipeTask({
|
|
8203
|
-
defaults,
|
|
8204
|
-
disposition,
|
|
8205
|
-
options,
|
|
8206
|
-
review
|
|
8207
|
-
}),
|
|
8208
|
-
description: defaults.description,
|
|
8209
|
-
name,
|
|
8210
|
-
taskAssignmentRules,
|
|
8211
|
-
taskPolicies
|
|
8212
|
-
};
|
|
8213
|
-
};
|
|
8214
8365
|
// src/correction.ts
|
|
8215
8366
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8216
8367
|
var buildAliasMatcher = (alias) => new RegExp(`(?<![\\p{L}\\p{N}'])${escapeRegExp(alias)}(?![\\p{L}\\p{N}'])`, "giu");
|
|
@@ -9097,11 +9248,13 @@ export {
|
|
|
9097
9248
|
createVoiceFileExternalObjectMapStore,
|
|
9098
9249
|
createVoiceExternalObjectMapId,
|
|
9099
9250
|
createVoiceExternalObjectMap,
|
|
9251
|
+
createVoiceExperiment,
|
|
9100
9252
|
createVoiceCallReviewRecorder,
|
|
9101
9253
|
createVoiceCallReviewFromSession,
|
|
9102
9254
|
createVoiceCallReviewFromLiveTelephonyReport,
|
|
9103
9255
|
createVoiceCallCompletedEvent,
|
|
9104
9256
|
createVoiceCRMActivitySink,
|
|
9257
|
+
createVoiceAssistant,
|
|
9105
9258
|
createVoiceAgentTool,
|
|
9106
9259
|
createVoiceAgentSquad,
|
|
9107
9260
|
createVoiceAgent,
|