@absolutejs/voice 0.0.22-beta.510 → 0.0.22-beta.511
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/dist/index.d.ts +10 -0
- package/dist/index.js +438 -0
- package/dist/liveCoach.d.ts +43 -0
- package/dist/supervisorPermissions.d.ts +33 -0
- package/dist/supervisorPresence.d.ts +49 -0
- package/dist/transcriptAnnotator.d.ts +41 -0
- package/dist/whisperChannel.d.ts +50 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -311,4 +311,14 @@ export { createVoiceRetryPolicy } from "./retryPolicy";
|
|
|
311
311
|
export type { VoiceRetryAttempt, VoiceRetryDecision, VoiceRetryDispositionAction, VoiceRetryDispositionRule, VoiceRetryPolicy, CreateVoiceRetryPolicyOptions, } from "./retryPolicy";
|
|
312
312
|
export { collectVoiceCampaignTemplateVariables, DEFAULT_VOICE_CAMPAIGN_TEMPLATE_FILTERS, resolveVoiceCampaignTemplate, } from "./campaignTemplate";
|
|
313
313
|
export type { ResolveVoiceCampaignTemplateOptions, VoiceCampaignTemplateFilter, VoiceCampaignTemplateResolveResult, VoiceCampaignTemplateScope, VoiceCampaignTemplateValue, } from "./campaignTemplate";
|
|
314
|
+
export { createVoiceWhisperChannel } from "./whisperChannel";
|
|
315
|
+
export type { CreateVoiceWhisperChannelOptions, VoiceWhisperChannel, VoiceWhisperEvent, VoiceWhisperFrame, VoiceWhisperRoute, } from "./whisperChannel";
|
|
316
|
+
export { createVoiceLiveCoach } from "./liveCoach";
|
|
317
|
+
export type { CreateVoiceLiveCoachOptions, VoiceCoachNudge, VoiceCoachNudgeInjection, VoiceCoachNudgeKind, VoiceLiveCoach, } from "./liveCoach";
|
|
318
|
+
export { createVoiceTranscriptAnnotator, DEFAULT_VOICE_ANNOTATION_KIND_SEVERITY, } from "./transcriptAnnotator";
|
|
319
|
+
export type { CreateVoiceTranscriptAnnotatorOptions, VoiceTranscriptAnnotation, VoiceTranscriptAnnotationKind, VoiceTranscriptAnnotator, } from "./transcriptAnnotator";
|
|
320
|
+
export { createVoiceSupervisorPresence } from "./supervisorPresence";
|
|
321
|
+
export type { CreateVoiceSupervisorPresenceOptions, VoiceSupervisorPresence, VoiceSupervisorPresenceEvent, VoiceSupervisorRole, VoiceSupervisorWatcher, } from "./supervisorPresence";
|
|
322
|
+
export { createVoiceSupervisorPermissions, VOICE_SUPERVISOR_TIER_CAPABILITIES, } from "./supervisorPermissions";
|
|
323
|
+
export type { CreateVoiceSupervisorPermissionsOptions, VoiceSupervisorCapability, VoiceSupervisorPermission, VoiceSupervisorPermissionCheck, VoiceSupervisorPermissions, VoiceSupervisorTier, } from "./supervisorPermissions";
|
|
314
324
|
export * from "./types";
|
package/dist/index.js
CHANGED
|
@@ -49171,6 +49171,437 @@ var collectVoiceCampaignTemplateVariables = (template) => {
|
|
|
49171
49171
|
}
|
|
49172
49172
|
return Array.from(set);
|
|
49173
49173
|
};
|
|
49174
|
+
// src/whisperChannel.ts
|
|
49175
|
+
var createVoiceWhisperChannel = (options) => {
|
|
49176
|
+
const now = options.now ?? (() => Date.now());
|
|
49177
|
+
const defaultRoute = options.defaultRoute ?? "agent-only";
|
|
49178
|
+
const duckLevel = options.duckCallerToLevel ?? 0.25;
|
|
49179
|
+
const maxConcurrent = options.maxConcurrentWhispers ?? 1;
|
|
49180
|
+
const active = new Map;
|
|
49181
|
+
const listeners = new Set;
|
|
49182
|
+
const broadcast = (event) => {
|
|
49183
|
+
for (const listener of listeners)
|
|
49184
|
+
listener(event);
|
|
49185
|
+
};
|
|
49186
|
+
const start = (supervisorId, route = defaultRoute) => {
|
|
49187
|
+
if (active.has(supervisorId))
|
|
49188
|
+
return active.get(supervisorId);
|
|
49189
|
+
if (active.size >= maxConcurrent) {
|
|
49190
|
+
throw new Error(`Whisper channel already at max concurrent (${maxConcurrent})`);
|
|
49191
|
+
}
|
|
49192
|
+
const entry = {
|
|
49193
|
+
route,
|
|
49194
|
+
startedAt: now(),
|
|
49195
|
+
supervisorId
|
|
49196
|
+
};
|
|
49197
|
+
active.set(supervisorId, entry);
|
|
49198
|
+
broadcast({ at: entry.startedAt, supervisorId, type: "started" });
|
|
49199
|
+
if (route === "agent-only") {
|
|
49200
|
+
broadcast({
|
|
49201
|
+
at: entry.startedAt,
|
|
49202
|
+
level: duckLevel,
|
|
49203
|
+
supervisorId,
|
|
49204
|
+
type: "ducked"
|
|
49205
|
+
});
|
|
49206
|
+
}
|
|
49207
|
+
return entry;
|
|
49208
|
+
};
|
|
49209
|
+
const stop = (supervisorId) => {
|
|
49210
|
+
if (!active.has(supervisorId))
|
|
49211
|
+
return false;
|
|
49212
|
+
active.delete(supervisorId);
|
|
49213
|
+
broadcast({ at: now(), supervisorId, type: "stopped" });
|
|
49214
|
+
return true;
|
|
49215
|
+
};
|
|
49216
|
+
const pushFrame = (frame) => {
|
|
49217
|
+
const entry = active.get(frame.supervisorId);
|
|
49218
|
+
if (!entry)
|
|
49219
|
+
return "drop";
|
|
49220
|
+
if (entry.route === "drop")
|
|
49221
|
+
return "drop";
|
|
49222
|
+
broadcast({ frame, type: "frame" });
|
|
49223
|
+
return entry.route;
|
|
49224
|
+
};
|
|
49225
|
+
return {
|
|
49226
|
+
activeSupervisors: () => Array.from(active.keys()),
|
|
49227
|
+
isWhispering: (supervisorId) => active.has(supervisorId),
|
|
49228
|
+
pushFrame,
|
|
49229
|
+
routeFor: (supervisorId) => active.get(supervisorId)?.route ?? null,
|
|
49230
|
+
sessionId: options.sessionId,
|
|
49231
|
+
setRoute(supervisorId, route) {
|
|
49232
|
+
const entry = active.get(supervisorId);
|
|
49233
|
+
if (!entry)
|
|
49234
|
+
return false;
|
|
49235
|
+
entry.route = route;
|
|
49236
|
+
return true;
|
|
49237
|
+
},
|
|
49238
|
+
start,
|
|
49239
|
+
stop,
|
|
49240
|
+
subscribe(listener) {
|
|
49241
|
+
listeners.add(listener);
|
|
49242
|
+
return () => {
|
|
49243
|
+
listeners.delete(listener);
|
|
49244
|
+
};
|
|
49245
|
+
}
|
|
49246
|
+
};
|
|
49247
|
+
};
|
|
49248
|
+
// src/liveCoach.ts
|
|
49249
|
+
var DEFAULT_TEMPLATES = {
|
|
49250
|
+
correction: "Supervisor correction (do not repeat this verbatim; weave it into your next response): {{text}}",
|
|
49251
|
+
hint: "Supervisor hint: {{text}}",
|
|
49252
|
+
knowledge: "Reference information from supervisor: {{text}}",
|
|
49253
|
+
"script-line": "Supervisor-approved phrasing to use next: {{text}}",
|
|
49254
|
+
warning: "Supervisor warning: {{text}}. Adjust your approach."
|
|
49255
|
+
};
|
|
49256
|
+
var createVoiceLiveCoach = (options) => {
|
|
49257
|
+
const now = options.now ?? (() => Date.now());
|
|
49258
|
+
const generateId = options.generateId ?? (() => `nudge_${Math.random().toString(36).slice(2, 10)}`);
|
|
49259
|
+
const role = options.injectionRole ?? "system";
|
|
49260
|
+
const templates = { ...DEFAULT_TEMPLATES, ...options.templateForKind ?? {} };
|
|
49261
|
+
const nudges = [];
|
|
49262
|
+
const listeners = new Set;
|
|
49263
|
+
const push = (input) => {
|
|
49264
|
+
const nudge = {
|
|
49265
|
+
acknowledged: false,
|
|
49266
|
+
createdAt: now(),
|
|
49267
|
+
id: input.id ?? generateId(),
|
|
49268
|
+
injected: false,
|
|
49269
|
+
kind: input.kind,
|
|
49270
|
+
sessionId: options.sessionId,
|
|
49271
|
+
supervisorId: input.supervisorId,
|
|
49272
|
+
text: input.text,
|
|
49273
|
+
...input.expiresAt !== undefined ? { expiresAt: input.expiresAt } : options.defaultExpiryMs !== undefined ? { expiresAt: now() + options.defaultExpiryMs } : {}
|
|
49274
|
+
};
|
|
49275
|
+
nudges.push(nudge);
|
|
49276
|
+
for (const listener of listeners)
|
|
49277
|
+
listener(nudge);
|
|
49278
|
+
return nudge;
|
|
49279
|
+
};
|
|
49280
|
+
const pending = () => {
|
|
49281
|
+
const at = now();
|
|
49282
|
+
return nudges.filter((n) => !n.injected && !n.acknowledged && (n.expiresAt === undefined || n.expiresAt > at));
|
|
49283
|
+
};
|
|
49284
|
+
const consumeForInjection = () => {
|
|
49285
|
+
const at = now();
|
|
49286
|
+
const ready = pending();
|
|
49287
|
+
const result = [];
|
|
49288
|
+
for (const nudge of ready) {
|
|
49289
|
+
const template = templates[nudge.kind] ?? DEFAULT_TEMPLATES[nudge.kind];
|
|
49290
|
+
const content = template.replace(/\{\{text\}\}/gu, nudge.text);
|
|
49291
|
+
nudge.injected = true;
|
|
49292
|
+
nudge.injectedAt = at;
|
|
49293
|
+
result.push({
|
|
49294
|
+
content,
|
|
49295
|
+
metadata: {
|
|
49296
|
+
kind: nudge.kind,
|
|
49297
|
+
nudgeId: nudge.id,
|
|
49298
|
+
supervisorId: nudge.supervisorId
|
|
49299
|
+
},
|
|
49300
|
+
role
|
|
49301
|
+
});
|
|
49302
|
+
}
|
|
49303
|
+
return result;
|
|
49304
|
+
};
|
|
49305
|
+
const acknowledge = (id) => {
|
|
49306
|
+
const nudge = nudges.find((n) => n.id === id);
|
|
49307
|
+
if (!nudge)
|
|
49308
|
+
return false;
|
|
49309
|
+
nudge.acknowledged = true;
|
|
49310
|
+
nudge.acknowledgedAt = now();
|
|
49311
|
+
return true;
|
|
49312
|
+
};
|
|
49313
|
+
return {
|
|
49314
|
+
acknowledge,
|
|
49315
|
+
consumeForInjection,
|
|
49316
|
+
history: () => nudges.slice(),
|
|
49317
|
+
pending,
|
|
49318
|
+
push,
|
|
49319
|
+
sessionId: options.sessionId,
|
|
49320
|
+
subscribe(listener) {
|
|
49321
|
+
listeners.add(listener);
|
|
49322
|
+
return () => {
|
|
49323
|
+
listeners.delete(listener);
|
|
49324
|
+
};
|
|
49325
|
+
}
|
|
49326
|
+
};
|
|
49327
|
+
};
|
|
49328
|
+
// src/transcriptAnnotator.ts
|
|
49329
|
+
var DEFAULT_VOICE_ANNOTATION_KIND_SEVERITY = {
|
|
49330
|
+
"compliance-concern": "major",
|
|
49331
|
+
custom: "info",
|
|
49332
|
+
"follow-up-needed": "minor",
|
|
49333
|
+
"great-recovery": "info",
|
|
49334
|
+
"knowledge-gap": "minor",
|
|
49335
|
+
"missed-objection": "minor",
|
|
49336
|
+
"tone-issue": "minor"
|
|
49337
|
+
};
|
|
49338
|
+
var createVoiceTranscriptAnnotator = (options) => {
|
|
49339
|
+
const now = options.now ?? (() => Date.now());
|
|
49340
|
+
const generateId = options.generateId ?? (() => `ann_${Math.random().toString(36).slice(2, 10)}`);
|
|
49341
|
+
const annotations = [];
|
|
49342
|
+
const add = (input) => {
|
|
49343
|
+
if (input.kind === "custom" && !input.customLabel) {
|
|
49344
|
+
throw new Error("customLabel is required for kind=custom");
|
|
49345
|
+
}
|
|
49346
|
+
const annotation = {
|
|
49347
|
+
createdAt: now(),
|
|
49348
|
+
id: input.id ?? generateId(),
|
|
49349
|
+
kind: input.kind,
|
|
49350
|
+
rangeStartMs: input.rangeStartMs,
|
|
49351
|
+
sessionId: options.sessionId,
|
|
49352
|
+
severity: input.severity ?? DEFAULT_VOICE_ANNOTATION_KIND_SEVERITY[input.kind],
|
|
49353
|
+
supervisorId: input.supervisorId,
|
|
49354
|
+
...input.customLabel !== undefined ? { customLabel: input.customLabel } : {},
|
|
49355
|
+
...input.rangeEndMs !== undefined ? { rangeEndMs: input.rangeEndMs } : {},
|
|
49356
|
+
...input.text !== undefined ? { text: input.text } : {},
|
|
49357
|
+
...input.turnId !== undefined ? { turnId: input.turnId } : {}
|
|
49358
|
+
};
|
|
49359
|
+
annotations.push(annotation);
|
|
49360
|
+
return annotation;
|
|
49361
|
+
};
|
|
49362
|
+
const remove = (id) => {
|
|
49363
|
+
const idx = annotations.findIndex((a) => a.id === id);
|
|
49364
|
+
if (idx === -1)
|
|
49365
|
+
return false;
|
|
49366
|
+
annotations.splice(idx, 1);
|
|
49367
|
+
return true;
|
|
49368
|
+
};
|
|
49369
|
+
const list = (filter) => annotations.filter((a) => {
|
|
49370
|
+
if (filter?.kind && a.kind !== filter.kind)
|
|
49371
|
+
return false;
|
|
49372
|
+
if (filter?.supervisorId && a.supervisorId !== filter.supervisorId) {
|
|
49373
|
+
return false;
|
|
49374
|
+
}
|
|
49375
|
+
if (filter?.severity && a.severity !== filter.severity)
|
|
49376
|
+
return false;
|
|
49377
|
+
if (filter?.fromMs !== undefined && a.rangeStartMs < filter.fromMs) {
|
|
49378
|
+
return false;
|
|
49379
|
+
}
|
|
49380
|
+
if (filter?.toMs !== undefined && a.rangeStartMs > filter.toMs) {
|
|
49381
|
+
return false;
|
|
49382
|
+
}
|
|
49383
|
+
return true;
|
|
49384
|
+
});
|
|
49385
|
+
const summarize = () => {
|
|
49386
|
+
const byKind = {};
|
|
49387
|
+
const bySeverity = {
|
|
49388
|
+
info: 0,
|
|
49389
|
+
major: 0,
|
|
49390
|
+
minor: 0
|
|
49391
|
+
};
|
|
49392
|
+
for (const a of annotations) {
|
|
49393
|
+
byKind[a.kind] = (byKind[a.kind] ?? 0) + 1;
|
|
49394
|
+
bySeverity[a.severity] += 1;
|
|
49395
|
+
}
|
|
49396
|
+
return { byKind, bySeverity, total: annotations.length };
|
|
49397
|
+
};
|
|
49398
|
+
return {
|
|
49399
|
+
add,
|
|
49400
|
+
list,
|
|
49401
|
+
remove,
|
|
49402
|
+
sessionId: options.sessionId,
|
|
49403
|
+
summarize
|
|
49404
|
+
};
|
|
49405
|
+
};
|
|
49406
|
+
// src/supervisorPresence.ts
|
|
49407
|
+
var createVoiceSupervisorPresence = (options = {}) => {
|
|
49408
|
+
const now = options.now ?? (() => Date.now());
|
|
49409
|
+
const staleAfter = options.staleAfterMs ?? 30000;
|
|
49410
|
+
const bySession = new Map;
|
|
49411
|
+
const listeners = new Set;
|
|
49412
|
+
const emit2 = (event) => {
|
|
49413
|
+
for (const listener of listeners)
|
|
49414
|
+
listener(event);
|
|
49415
|
+
};
|
|
49416
|
+
const ensureSession = (sessionId) => {
|
|
49417
|
+
let map = bySession.get(sessionId);
|
|
49418
|
+
if (!map) {
|
|
49419
|
+
map = new Map;
|
|
49420
|
+
bySession.set(sessionId, map);
|
|
49421
|
+
}
|
|
49422
|
+
return map;
|
|
49423
|
+
};
|
|
49424
|
+
const pruneStaleFromSession = (sessionId, sessionWatchers) => {
|
|
49425
|
+
const at = now();
|
|
49426
|
+
for (const [id, w] of sessionWatchers) {
|
|
49427
|
+
if (at - w.lastSeenAt > staleAfter) {
|
|
49428
|
+
sessionWatchers.delete(id);
|
|
49429
|
+
emit2({ at, sessionId, supervisorId: id, type: "leave" });
|
|
49430
|
+
}
|
|
49431
|
+
}
|
|
49432
|
+
};
|
|
49433
|
+
const join5 = (input) => {
|
|
49434
|
+
const sessionWatchers = ensureSession(input.sessionId);
|
|
49435
|
+
pruneStaleFromSession(input.sessionId, sessionWatchers);
|
|
49436
|
+
const at = now();
|
|
49437
|
+
const watcher = {
|
|
49438
|
+
joinedAt: at,
|
|
49439
|
+
lastSeenAt: at,
|
|
49440
|
+
role: input.role ?? "viewer",
|
|
49441
|
+
sessionId: input.sessionId,
|
|
49442
|
+
supervisorId: input.supervisorId,
|
|
49443
|
+
...input.displayName !== undefined ? { displayName: input.displayName } : {}
|
|
49444
|
+
};
|
|
49445
|
+
sessionWatchers.set(input.supervisorId, watcher);
|
|
49446
|
+
emit2({ type: "join", watcher });
|
|
49447
|
+
return watcher;
|
|
49448
|
+
};
|
|
49449
|
+
const leave = (sessionId, supervisorId) => {
|
|
49450
|
+
const sessionWatchers = bySession.get(sessionId);
|
|
49451
|
+
if (!sessionWatchers?.delete(supervisorId))
|
|
49452
|
+
return false;
|
|
49453
|
+
if (sessionWatchers.size === 0)
|
|
49454
|
+
bySession.delete(sessionId);
|
|
49455
|
+
emit2({ at: now(), sessionId, supervisorId, type: "leave" });
|
|
49456
|
+
return true;
|
|
49457
|
+
};
|
|
49458
|
+
const heartbeat = (sessionId, supervisorId) => {
|
|
49459
|
+
const watcher = bySession.get(sessionId)?.get(supervisorId);
|
|
49460
|
+
if (!watcher)
|
|
49461
|
+
return false;
|
|
49462
|
+
const at = now();
|
|
49463
|
+
watcher.lastSeenAt = at;
|
|
49464
|
+
emit2({ at, sessionId, supervisorId, type: "heartbeat" });
|
|
49465
|
+
return true;
|
|
49466
|
+
};
|
|
49467
|
+
const setRole = (sessionId, supervisorId, role) => {
|
|
49468
|
+
const watcher = bySession.get(sessionId)?.get(supervisorId);
|
|
49469
|
+
if (!watcher)
|
|
49470
|
+
return false;
|
|
49471
|
+
if (watcher.role === role)
|
|
49472
|
+
return true;
|
|
49473
|
+
const from = watcher.role;
|
|
49474
|
+
watcher.role = role;
|
|
49475
|
+
emit2({
|
|
49476
|
+
at: now(),
|
|
49477
|
+
from,
|
|
49478
|
+
sessionId,
|
|
49479
|
+
supervisorId,
|
|
49480
|
+
to: role,
|
|
49481
|
+
type: "role-change"
|
|
49482
|
+
});
|
|
49483
|
+
return true;
|
|
49484
|
+
};
|
|
49485
|
+
const list = (sessionId) => {
|
|
49486
|
+
const sessionWatchers = bySession.get(sessionId);
|
|
49487
|
+
if (!sessionWatchers)
|
|
49488
|
+
return [];
|
|
49489
|
+
pruneStaleFromSession(sessionId, sessionWatchers);
|
|
49490
|
+
return Array.from(sessionWatchers.values());
|
|
49491
|
+
};
|
|
49492
|
+
return {
|
|
49493
|
+
heartbeat,
|
|
49494
|
+
join: join5,
|
|
49495
|
+
leave,
|
|
49496
|
+
list,
|
|
49497
|
+
setRole,
|
|
49498
|
+
sessionsWatchedBy(supervisorId) {
|
|
49499
|
+
const out = [];
|
|
49500
|
+
for (const [sessionId, map] of bySession) {
|
|
49501
|
+
if (map.has(supervisorId))
|
|
49502
|
+
out.push(sessionId);
|
|
49503
|
+
}
|
|
49504
|
+
return out;
|
|
49505
|
+
},
|
|
49506
|
+
subscribe(listener) {
|
|
49507
|
+
listeners.add(listener);
|
|
49508
|
+
return () => {
|
|
49509
|
+
listeners.delete(listener);
|
|
49510
|
+
};
|
|
49511
|
+
}
|
|
49512
|
+
};
|
|
49513
|
+
};
|
|
49514
|
+
// src/supervisorPermissions.ts
|
|
49515
|
+
var TIER_CAPABILITIES = {
|
|
49516
|
+
annotate: ["monitor", "annotate"],
|
|
49517
|
+
coach: ["monitor", "annotate", "coach"],
|
|
49518
|
+
"full-control": [
|
|
49519
|
+
"monitor",
|
|
49520
|
+
"annotate",
|
|
49521
|
+
"coach",
|
|
49522
|
+
"whisper",
|
|
49523
|
+
"barge",
|
|
49524
|
+
"takeover",
|
|
49525
|
+
"release",
|
|
49526
|
+
"end-call",
|
|
49527
|
+
"view-pii",
|
|
49528
|
+
"export-recording"
|
|
49529
|
+
],
|
|
49530
|
+
"monitor-only": ["monitor"],
|
|
49531
|
+
whisper: ["monitor", "annotate", "coach", "whisper"]
|
|
49532
|
+
};
|
|
49533
|
+
var createVoiceSupervisorPermissions = (options = {}) => {
|
|
49534
|
+
const now = options.now ?? (() => Date.now());
|
|
49535
|
+
const store = new Map;
|
|
49536
|
+
for (const permission of options.permissions ?? []) {
|
|
49537
|
+
store.set(permission.supervisorId, permission);
|
|
49538
|
+
}
|
|
49539
|
+
const defaultTier = options.defaultTier ?? null;
|
|
49540
|
+
const get = (supervisorId) => {
|
|
49541
|
+
const permission = store.get(supervisorId);
|
|
49542
|
+
if (!permission) {
|
|
49543
|
+
return defaultTier ? { supervisorId, tier: defaultTier } : null;
|
|
49544
|
+
}
|
|
49545
|
+
if (permission.expiresAt !== undefined && permission.expiresAt <= now()) {
|
|
49546
|
+
return null;
|
|
49547
|
+
}
|
|
49548
|
+
return permission;
|
|
49549
|
+
};
|
|
49550
|
+
const capabilitiesFor = (supervisorId) => {
|
|
49551
|
+
const permission = get(supervisorId);
|
|
49552
|
+
if (!permission)
|
|
49553
|
+
return [];
|
|
49554
|
+
const base = new Set(TIER_CAPABILITIES[permission.tier]);
|
|
49555
|
+
for (const extra of permission.extraCapabilities ?? [])
|
|
49556
|
+
base.add(extra);
|
|
49557
|
+
for (const denied of permission.deniedCapabilities ?? [])
|
|
49558
|
+
base.delete(denied);
|
|
49559
|
+
return Array.from(base);
|
|
49560
|
+
};
|
|
49561
|
+
const can = (supervisorId, capability) => {
|
|
49562
|
+
const permission = store.get(supervisorId);
|
|
49563
|
+
if (!permission) {
|
|
49564
|
+
if (!defaultTier)
|
|
49565
|
+
return { allowed: false, reason: "no-permission" };
|
|
49566
|
+
} else if (permission.expiresAt !== undefined && permission.expiresAt <= now()) {
|
|
49567
|
+
return { allowed: false, reason: "expired" };
|
|
49568
|
+
} else if (permission.deniedCapabilities?.includes(capability)) {
|
|
49569
|
+
return { allowed: false, reason: "denied" };
|
|
49570
|
+
}
|
|
49571
|
+
if (capabilitiesFor(supervisorId).includes(capability)) {
|
|
49572
|
+
return { allowed: true };
|
|
49573
|
+
}
|
|
49574
|
+
return { allowed: false, reason: "tier-too-low" };
|
|
49575
|
+
};
|
|
49576
|
+
const grant = (supervisorId, tier, options2 = {}) => {
|
|
49577
|
+
const permission = {
|
|
49578
|
+
supervisorId,
|
|
49579
|
+
tier,
|
|
49580
|
+
...options2.extraCapabilities !== undefined ? { extraCapabilities: options2.extraCapabilities } : {},
|
|
49581
|
+
...options2.deniedCapabilities !== undefined ? { deniedCapabilities: options2.deniedCapabilities } : {},
|
|
49582
|
+
...options2.expiresAt !== undefined ? { expiresAt: options2.expiresAt } : {}
|
|
49583
|
+
};
|
|
49584
|
+
store.set(supervisorId, permission);
|
|
49585
|
+
return permission;
|
|
49586
|
+
};
|
|
49587
|
+
const revoke = (supervisorId) => store.delete(supervisorId);
|
|
49588
|
+
const enforce = (supervisorId, capability) => {
|
|
49589
|
+
const verdict = can(supervisorId, capability);
|
|
49590
|
+
if (!verdict.allowed) {
|
|
49591
|
+
throw new Error(`Supervisor ${supervisorId} cannot ${capability}: ${verdict.reason ?? "denied"}`);
|
|
49592
|
+
}
|
|
49593
|
+
};
|
|
49594
|
+
return {
|
|
49595
|
+
can,
|
|
49596
|
+
capabilitiesFor,
|
|
49597
|
+
enforce,
|
|
49598
|
+
get,
|
|
49599
|
+
grant,
|
|
49600
|
+
revoke,
|
|
49601
|
+
tiers: () => Object.keys(TIER_CAPABILITIES)
|
|
49602
|
+
};
|
|
49603
|
+
};
|
|
49604
|
+
var VOICE_SUPERVISOR_TIER_CAPABILITIES = TIER_CAPABILITIES;
|
|
49174
49605
|
export {
|
|
49175
49606
|
writeVoiceProofPack,
|
|
49176
49607
|
writeVoiceMediaPipelineArtifacts,
|
|
@@ -49521,6 +49952,7 @@ export {
|
|
|
49521
49952
|
createVoiceWorkflowContractPreset,
|
|
49522
49953
|
createVoiceWorkflowContractHandler,
|
|
49523
49954
|
createVoiceWorkflowContract,
|
|
49955
|
+
createVoiceWhisperChannel,
|
|
49524
49956
|
createVoiceWebhookHandoffAdapter,
|
|
49525
49957
|
createVoiceWebhookFanout,
|
|
49526
49958
|
createVoiceWebhookDeliveryWorkerLoop,
|
|
@@ -49538,6 +49970,7 @@ export {
|
|
|
49538
49970
|
createVoiceTurnLatencyHTMLHandler,
|
|
49539
49971
|
createVoiceTransferCallTool,
|
|
49540
49972
|
createVoiceTranscriptRedactor,
|
|
49973
|
+
createVoiceTranscriptAnnotator,
|
|
49541
49974
|
createVoiceTraceTimelineRoutes,
|
|
49542
49975
|
createVoiceTraceSinkStore,
|
|
49543
49976
|
createVoiceTraceSinkDeliveryWorkerLoop,
|
|
@@ -49572,6 +50005,8 @@ export {
|
|
|
49572
50005
|
createVoiceTaskSLABreachedEvent,
|
|
49573
50006
|
createVoiceTaskCreatedEvent,
|
|
49574
50007
|
createVoiceTTSProviderRouter,
|
|
50008
|
+
createVoiceSupervisorPresence,
|
|
50009
|
+
createVoiceSupervisorPermissions,
|
|
49575
50010
|
createVoiceSloThresholdProfile,
|
|
49576
50011
|
createVoiceSloReadinessThresholdRoutes,
|
|
49577
50012
|
createVoiceSloReadinessThresholdOptions,
|
|
@@ -49744,6 +50179,7 @@ export {
|
|
|
49744
50179
|
createVoiceLiveOpsController,
|
|
49745
50180
|
createVoiceLiveMonitorRoutes,
|
|
49746
50181
|
createVoiceLiveLatencyRoutes,
|
|
50182
|
+
createVoiceLiveCoach,
|
|
49747
50183
|
createVoiceLiveCallViewerHTMXRoute,
|
|
49748
50184
|
createVoiceLinearIssueUpdateSink,
|
|
49749
50185
|
createVoiceLinearIssueSyncSinks,
|
|
@@ -50053,6 +50489,7 @@ export {
|
|
|
50053
50489
|
VOICE_WEBHOOK_TIMESTAMP_HEADER,
|
|
50054
50490
|
VOICE_WEBHOOK_SIGNATURE_HEADER,
|
|
50055
50491
|
VOICE_TCPA_DEFAULT_WINDOW,
|
|
50492
|
+
VOICE_SUPERVISOR_TIER_CAPABILITIES,
|
|
50056
50493
|
VOICE_LIVE_OPS_ACTIONS,
|
|
50057
50494
|
VOICE_DTMF_DIGITS,
|
|
50058
50495
|
VOICE_CALLER_MEMORY_KEY,
|
|
@@ -50065,5 +50502,6 @@ export {
|
|
|
50065
50502
|
DEFAULT_VOICE_POST_CALL_SURVEY_QUESTIONS,
|
|
50066
50503
|
DEFAULT_VOICE_CAMPAIGN_TEMPLATE_FILTERS,
|
|
50067
50504
|
DEFAULT_VOICE_CALL_DISPOSITIONS,
|
|
50505
|
+
DEFAULT_VOICE_ANNOTATION_KIND_SEVERITY,
|
|
50068
50506
|
BROWSER_NOISE_SUPPRESSOR_PRESETS
|
|
50069
50507
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type VoiceCoachNudgeKind = "hint" | "correction" | "warning" | "script-line" | "knowledge";
|
|
2
|
+
export type VoiceCoachNudge = {
|
|
3
|
+
id: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
supervisorId: string;
|
|
6
|
+
kind: VoiceCoachNudgeKind;
|
|
7
|
+
text: string;
|
|
8
|
+
createdAt: number;
|
|
9
|
+
injected: boolean;
|
|
10
|
+
injectedAt?: number;
|
|
11
|
+
acknowledged: boolean;
|
|
12
|
+
acknowledgedAt?: number;
|
|
13
|
+
expiresAt?: number;
|
|
14
|
+
};
|
|
15
|
+
export type VoiceCoachNudgeInjection = {
|
|
16
|
+
role: "system" | "developer";
|
|
17
|
+
content: string;
|
|
18
|
+
metadata: {
|
|
19
|
+
nudgeId: string;
|
|
20
|
+
supervisorId: string;
|
|
21
|
+
kind: VoiceCoachNudgeKind;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export type CreateVoiceLiveCoachOptions = {
|
|
25
|
+
sessionId: string;
|
|
26
|
+
injectionRole?: "system" | "developer";
|
|
27
|
+
templateForKind?: Partial<Record<VoiceCoachNudgeKind, string>>;
|
|
28
|
+
defaultExpiryMs?: number;
|
|
29
|
+
generateId?: () => string;
|
|
30
|
+
now?: () => number;
|
|
31
|
+
};
|
|
32
|
+
export declare const createVoiceLiveCoach: (options: CreateVoiceLiveCoachOptions) => {
|
|
33
|
+
acknowledge: (id: string) => boolean;
|
|
34
|
+
consumeForInjection: () => VoiceCoachNudgeInjection[];
|
|
35
|
+
history: () => VoiceCoachNudge[];
|
|
36
|
+
pending: () => VoiceCoachNudge[];
|
|
37
|
+
push: (input: Omit<VoiceCoachNudge, "id" | "createdAt" | "injected" | "acknowledged" | "sessionId"> & {
|
|
38
|
+
id?: string;
|
|
39
|
+
}) => VoiceCoachNudge;
|
|
40
|
+
sessionId: string;
|
|
41
|
+
subscribe(listener: (nudge: VoiceCoachNudge) => void): () => void;
|
|
42
|
+
};
|
|
43
|
+
export type VoiceLiveCoach = ReturnType<typeof createVoiceLiveCoach>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type VoiceSupervisorCapability = "monitor" | "annotate" | "coach" | "whisper" | "barge" | "takeover" | "release" | "end-call" | "view-pii" | "export-recording";
|
|
2
|
+
export type VoiceSupervisorTier = "monitor-only" | "annotate" | "coach" | "whisper" | "full-control";
|
|
3
|
+
export type VoiceSupervisorPermission = {
|
|
4
|
+
supervisorId: string;
|
|
5
|
+
tier: VoiceSupervisorTier;
|
|
6
|
+
extraCapabilities?: VoiceSupervisorCapability[];
|
|
7
|
+
deniedCapabilities?: VoiceSupervisorCapability[];
|
|
8
|
+
expiresAt?: number;
|
|
9
|
+
};
|
|
10
|
+
export type VoiceSupervisorPermissionCheck = {
|
|
11
|
+
allowed: boolean;
|
|
12
|
+
reason?: "no-permission" | "expired" | "denied" | "tier-too-low";
|
|
13
|
+
};
|
|
14
|
+
export type CreateVoiceSupervisorPermissionsOptions = {
|
|
15
|
+
defaultTier?: VoiceSupervisorTier;
|
|
16
|
+
permissions?: VoiceSupervisorPermission[];
|
|
17
|
+
now?: () => number;
|
|
18
|
+
};
|
|
19
|
+
export declare const createVoiceSupervisorPermissions: (options?: CreateVoiceSupervisorPermissionsOptions) => {
|
|
20
|
+
can: (supervisorId: string, capability: VoiceSupervisorCapability) => VoiceSupervisorPermissionCheck;
|
|
21
|
+
capabilitiesFor: (supervisorId: string) => VoiceSupervisorCapability[];
|
|
22
|
+
enforce: (supervisorId: string, capability: VoiceSupervisorCapability) => void;
|
|
23
|
+
get: (supervisorId: string) => VoiceSupervisorPermission | null;
|
|
24
|
+
grant: (supervisorId: string, tier: VoiceSupervisorTier, options?: {
|
|
25
|
+
extraCapabilities?: VoiceSupervisorCapability[];
|
|
26
|
+
deniedCapabilities?: VoiceSupervisorCapability[];
|
|
27
|
+
expiresAt?: number;
|
|
28
|
+
}) => VoiceSupervisorPermission;
|
|
29
|
+
revoke: (supervisorId: string) => boolean;
|
|
30
|
+
tiers: () => VoiceSupervisorTier[];
|
|
31
|
+
};
|
|
32
|
+
export type VoiceSupervisorPermissions = ReturnType<typeof createVoiceSupervisorPermissions>;
|
|
33
|
+
export declare const VOICE_SUPERVISOR_TIER_CAPABILITIES: Record<VoiceSupervisorTier, VoiceSupervisorCapability[]>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type VoiceSupervisorRole = "viewer" | "coach" | "whisperer" | "owner";
|
|
2
|
+
export type VoiceSupervisorWatcher = {
|
|
3
|
+
supervisorId: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
joinedAt: number;
|
|
6
|
+
lastSeenAt: number;
|
|
7
|
+
role: VoiceSupervisorRole;
|
|
8
|
+
displayName?: string;
|
|
9
|
+
};
|
|
10
|
+
export type VoiceSupervisorPresenceEvent = {
|
|
11
|
+
type: "join";
|
|
12
|
+
watcher: VoiceSupervisorWatcher;
|
|
13
|
+
} | {
|
|
14
|
+
type: "leave";
|
|
15
|
+
supervisorId: string;
|
|
16
|
+
sessionId: string;
|
|
17
|
+
at: number;
|
|
18
|
+
} | {
|
|
19
|
+
type: "role-change";
|
|
20
|
+
supervisorId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
from: VoiceSupervisorRole;
|
|
23
|
+
to: VoiceSupervisorRole;
|
|
24
|
+
at: number;
|
|
25
|
+
} | {
|
|
26
|
+
type: "heartbeat";
|
|
27
|
+
supervisorId: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
at: number;
|
|
30
|
+
};
|
|
31
|
+
export type CreateVoiceSupervisorPresenceOptions = {
|
|
32
|
+
staleAfterMs?: number;
|
|
33
|
+
now?: () => number;
|
|
34
|
+
};
|
|
35
|
+
export declare const createVoiceSupervisorPresence: (options?: CreateVoiceSupervisorPresenceOptions) => {
|
|
36
|
+
heartbeat: (sessionId: string, supervisorId: string) => boolean;
|
|
37
|
+
join: (input: {
|
|
38
|
+
supervisorId: string;
|
|
39
|
+
sessionId: string;
|
|
40
|
+
role?: VoiceSupervisorRole;
|
|
41
|
+
displayName?: string;
|
|
42
|
+
}) => VoiceSupervisorWatcher;
|
|
43
|
+
leave: (sessionId: string, supervisorId: string) => boolean;
|
|
44
|
+
list: (sessionId: string) => VoiceSupervisorWatcher[];
|
|
45
|
+
setRole: (sessionId: string, supervisorId: string, role: VoiceSupervisorRole) => boolean;
|
|
46
|
+
sessionsWatchedBy(supervisorId: string): string[];
|
|
47
|
+
subscribe(listener: (event: VoiceSupervisorPresenceEvent) => void): () => void;
|
|
48
|
+
};
|
|
49
|
+
export type VoiceSupervisorPresence = ReturnType<typeof createVoiceSupervisorPresence>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type VoiceTranscriptAnnotationKind = "great-recovery" | "missed-objection" | "compliance-concern" | "tone-issue" | "knowledge-gap" | "follow-up-needed" | "custom";
|
|
2
|
+
export type VoiceTranscriptAnnotation = {
|
|
3
|
+
id: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
supervisorId: string;
|
|
6
|
+
kind: VoiceTranscriptAnnotationKind;
|
|
7
|
+
customLabel?: string;
|
|
8
|
+
text?: string;
|
|
9
|
+
turnId?: string;
|
|
10
|
+
rangeStartMs: number;
|
|
11
|
+
rangeEndMs?: number;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
severity: "info" | "minor" | "major";
|
|
14
|
+
};
|
|
15
|
+
export type CreateVoiceTranscriptAnnotatorOptions = {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
generateId?: () => string;
|
|
18
|
+
now?: () => number;
|
|
19
|
+
};
|
|
20
|
+
export declare const DEFAULT_VOICE_ANNOTATION_KIND_SEVERITY: Record<VoiceTranscriptAnnotationKind, VoiceTranscriptAnnotation["severity"]>;
|
|
21
|
+
export declare const createVoiceTranscriptAnnotator: (options: CreateVoiceTranscriptAnnotatorOptions) => {
|
|
22
|
+
add: (input: Omit<VoiceTranscriptAnnotation, "id" | "createdAt" | "sessionId" | "severity"> & {
|
|
23
|
+
severity?: VoiceTranscriptAnnotation["severity"];
|
|
24
|
+
id?: string;
|
|
25
|
+
}) => VoiceTranscriptAnnotation;
|
|
26
|
+
list: (filter?: {
|
|
27
|
+
kind?: VoiceTranscriptAnnotationKind;
|
|
28
|
+
supervisorId?: string;
|
|
29
|
+
severity?: VoiceTranscriptAnnotation["severity"];
|
|
30
|
+
fromMs?: number;
|
|
31
|
+
toMs?: number;
|
|
32
|
+
}) => VoiceTranscriptAnnotation[];
|
|
33
|
+
remove: (id: string) => boolean;
|
|
34
|
+
sessionId: string;
|
|
35
|
+
summarize: () => {
|
|
36
|
+
byKind: Partial<Record<VoiceTranscriptAnnotationKind, number>>;
|
|
37
|
+
bySeverity: Record<"info" | "minor" | "major", number>;
|
|
38
|
+
total: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export type VoiceTranscriptAnnotator = ReturnType<typeof createVoiceTranscriptAnnotator>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type VoiceWhisperFrame = {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
supervisorId: string;
|
|
4
|
+
pcm: ArrayBuffer | Uint8Array;
|
|
5
|
+
sampleRate: number;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
};
|
|
8
|
+
export type VoiceWhisperRoute = "agent-only" | "agent-and-caller" | "drop";
|
|
9
|
+
export type VoiceWhisperEvent = {
|
|
10
|
+
type: "started";
|
|
11
|
+
supervisorId: string;
|
|
12
|
+
at: number;
|
|
13
|
+
} | {
|
|
14
|
+
type: "stopped";
|
|
15
|
+
supervisorId: string;
|
|
16
|
+
at: number;
|
|
17
|
+
} | {
|
|
18
|
+
type: "frame";
|
|
19
|
+
frame: VoiceWhisperFrame;
|
|
20
|
+
} | {
|
|
21
|
+
type: "ducked";
|
|
22
|
+
supervisorId: string;
|
|
23
|
+
level: number;
|
|
24
|
+
at: number;
|
|
25
|
+
};
|
|
26
|
+
export type CreateVoiceWhisperChannelOptions = {
|
|
27
|
+
sessionId: string;
|
|
28
|
+
defaultRoute?: VoiceWhisperRoute;
|
|
29
|
+
duckCallerToLevel?: number;
|
|
30
|
+
maxConcurrentWhispers?: number;
|
|
31
|
+
now?: () => number;
|
|
32
|
+
};
|
|
33
|
+
type ActiveWhisper = {
|
|
34
|
+
supervisorId: string;
|
|
35
|
+
route: VoiceWhisperRoute;
|
|
36
|
+
startedAt: number;
|
|
37
|
+
};
|
|
38
|
+
export declare const createVoiceWhisperChannel: (options: CreateVoiceWhisperChannelOptions) => {
|
|
39
|
+
activeSupervisors: () => string[];
|
|
40
|
+
isWhispering: (supervisorId: string) => boolean;
|
|
41
|
+
pushFrame: (frame: VoiceWhisperFrame) => VoiceWhisperRoute;
|
|
42
|
+
routeFor: (supervisorId: string) => VoiceWhisperRoute | null;
|
|
43
|
+
sessionId: string;
|
|
44
|
+
setRoute(supervisorId: string, route: VoiceWhisperRoute): boolean;
|
|
45
|
+
start: (supervisorId: string, route?: VoiceWhisperRoute) => ActiveWhisper | undefined;
|
|
46
|
+
stop: (supervisorId: string) => boolean;
|
|
47
|
+
subscribe(listener: (event: VoiceWhisperEvent) => void): () => void;
|
|
48
|
+
};
|
|
49
|
+
export type VoiceWhisperChannel = ReturnType<typeof createVoiceWhisperChannel>;
|
|
50
|
+
export {};
|