@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 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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.510",
3
+ "version": "0.0.22-beta.511",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",