@absolutejs/voice 0.0.22-beta.210 → 0.0.22-beta.212
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 +238 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -479,6 +479,244 @@ The UI should keep the scheduling flow simple: microphone, transcript, selected
|
|
|
479
479
|
|
|
480
480
|
This recipe covers the hosted-platform expectations that matter for appointment scheduling: scheduling tools, deterministic tool proof, post-call confirmation work, outcome validation, simulation proof, production readiness, and one call-log replacement for debugging.
|
|
481
481
|
|
|
482
|
+
## Use-Case Recipe: Campaign Outreach
|
|
483
|
+
|
|
484
|
+
Use this path when you need Retell/Bland-style outbound outreach without handing recipients, consent proof, attempt policy, carrier outcomes, or debugging records to a hosted campaign dashboard. The package gives you campaign primitives; your app decides who can upload recipients, when workers run, which carrier dials, and how campaign results sync back to your product.
|
|
485
|
+
|
|
486
|
+
The production shape is:
|
|
487
|
+
|
|
488
|
+
1. Store campaigns in app-owned storage.
|
|
489
|
+
2. Import recipients with consent checks, phone validation, dedupe, variables, and rejected-row evidence.
|
|
490
|
+
3. Configure campaign policy for max attempts, concurrency, attempt windows, quiet hours, rate limits, and retry backoff.
|
|
491
|
+
4. Use a carrier dialer or your own dialer function, then apply Twilio/Telnyx/Plivo webhook outcomes back to attempts.
|
|
492
|
+
5. Expose campaign routes, observability, readiness proof, production readiness, and operations-record links for every attempted call.
|
|
493
|
+
|
|
494
|
+
```ts
|
|
495
|
+
import { Elysia } from 'elysia';
|
|
496
|
+
import {
|
|
497
|
+
createVoiceCampaignRoutes,
|
|
498
|
+
createVoiceFileRuntimeStorage,
|
|
499
|
+
createVoiceOperationsRecordRoutes,
|
|
500
|
+
createVoiceProductionReadinessRoutes,
|
|
501
|
+
createVoiceReadinessProfile,
|
|
502
|
+
createVoiceSQLiteCampaignStore,
|
|
503
|
+
runVoiceCampaignReadinessProof
|
|
504
|
+
} from '@absolutejs/voice';
|
|
505
|
+
|
|
506
|
+
const runtime = createVoiceFileRuntimeStorage({
|
|
507
|
+
directory: '.voice-runtime/campaign-outreach'
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const campaigns = createVoiceSQLiteCampaignStore({
|
|
511
|
+
path: '.voice-runtime/campaigns.sqlite'
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const app = new Elysia()
|
|
515
|
+
.use(
|
|
516
|
+
createVoiceCampaignRoutes({
|
|
517
|
+
htmlPath: '/voice/campaigns',
|
|
518
|
+
operationsRecordHref: '/voice-operations/:sessionId',
|
|
519
|
+
path: '/api/voice/campaigns',
|
|
520
|
+
store: campaigns,
|
|
521
|
+
title: 'Renewal Outreach'
|
|
522
|
+
})
|
|
523
|
+
)
|
|
524
|
+
.use(
|
|
525
|
+
createVoiceOperationsRecordRoutes({
|
|
526
|
+
htmlPath: '/voice-operations/:sessionId',
|
|
527
|
+
path: '/api/voice-operations/:sessionId',
|
|
528
|
+
store: runtime.traces
|
|
529
|
+
})
|
|
530
|
+
)
|
|
531
|
+
.use(
|
|
532
|
+
createVoiceProductionReadinessRoutes({
|
|
533
|
+
...createVoiceReadinessProfile('phone-agent', {
|
|
534
|
+
campaignReadiness: () =>
|
|
535
|
+
runVoiceCampaignReadinessProof({
|
|
536
|
+
store: campaigns
|
|
537
|
+
}),
|
|
538
|
+
explain: true
|
|
539
|
+
}),
|
|
540
|
+
links: {
|
|
541
|
+
campaigns: '/voice/campaigns',
|
|
542
|
+
operationsRecords: '/voice-operations/:sessionId'
|
|
543
|
+
},
|
|
544
|
+
store: runtime.traces
|
|
545
|
+
})
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
await fetch('/api/voice/campaigns', {
|
|
549
|
+
body: JSON.stringify({
|
|
550
|
+
maxAttempts: 3,
|
|
551
|
+
maxConcurrentAttempts: 10,
|
|
552
|
+
name: 'Renewal outreach',
|
|
553
|
+
schedule: {
|
|
554
|
+
attemptWindow: { startHour: 9, endHour: 17 },
|
|
555
|
+
quietHours: { startHour: 12, endHour: 13 },
|
|
556
|
+
rateLimit: { maxAttempts: 60, windowMs: 60_000 },
|
|
557
|
+
retryPolicy: { backoffMs: [5 * 60_000, 30 * 60_000] }
|
|
558
|
+
}
|
|
559
|
+
}),
|
|
560
|
+
headers: { 'content-type': 'application/json' },
|
|
561
|
+
method: 'POST'
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
await fetch('/api/voice/campaigns/campaign-1/recipients/import', {
|
|
565
|
+
body: JSON.stringify({
|
|
566
|
+
csv: `id,name,phone,consent,segment
|
|
567
|
+
recipient-1,Ada,+15550001001,yes,trial
|
|
568
|
+
recipient-2,Grace,+15550001002,true,enterprise
|
|
569
|
+
recipient-3,Linus,not-a-phone,yes,partner
|
|
570
|
+
recipient-4,Barbara,+15550001004,no,trial`,
|
|
571
|
+
metadataColumns: ['segment'],
|
|
572
|
+
requireConsent: true,
|
|
573
|
+
variableColumns: ['segment']
|
|
574
|
+
}),
|
|
575
|
+
headers: { 'content-type': 'application/json' },
|
|
576
|
+
method: 'POST'
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
await fetch('/api/voice/campaigns/campaign-1/enqueue', {
|
|
580
|
+
method: 'POST'
|
|
581
|
+
});
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Use `/api/voice/campaigns/campaign-1/tick` for manual workers or `createVoiceCampaignWorkerLoop(...)` when the app should continuously drain eligible recipients. The runtime enforces the campaign policy on each tick, so parallel workers do not double-start recipients and attempts respect quiet hours, rate limits, retry backoff, and max attempts.
|
|
585
|
+
|
|
586
|
+
For production carrier dialing, pass a `dialer` to `createVoiceCampaignRoutes(...)`: `createVoiceTwilioCampaignDialer(...)`, `createVoiceTelnyxCampaignDialer(...)`, `createVoicePlivoCampaignDialer(...)`, or a custom dialer that starts the call and returns an external call id. Run `runVoiceCampaignDialerProof(...)` before live traffic to dry-run carrier request metadata and webhook outcome application.
|
|
587
|
+
|
|
588
|
+
The UI should show `/voice/campaigns`, `/voice/campaigns/observability`, `/api/voice/campaigns/readiness-proof`, `/production-readiness`, and operations-record links for recent attempts. If a recipient failed, the operator should open the campaign attempt, follow `/voice-operations/:sessionId`, and see the same trace/review/task/audit context used by support and phone-agent flows.
|
|
589
|
+
|
|
590
|
+
This recipe covers the hosted-platform expectations that matter for campaign outreach: recipient import evidence, consent/dedupe checks, scheduling policy, worker-safe attempts, carrier dry-run proof, webhook outcome mapping, queue observability, readiness gating, and call-log replacement links.
|
|
591
|
+
|
|
592
|
+
## Use-Case Recipe: Meeting Recorder
|
|
593
|
+
|
|
594
|
+
Use this path when the product needs a browser recorder for meetings, interviews, demos, or internal calls: capture microphone audio, persist transcripts and traces, generate a post-call review, expose a replayable operations record, and keep retention/export controls inside the app. This is not a hosted meeting bot; it is a set of recorder primitives your AbsoluteJS UI can own.
|
|
595
|
+
|
|
596
|
+
The production shape is:
|
|
597
|
+
|
|
598
|
+
1. Mount a browser voice route with persistent session, trace, review, task, and audit-capable storage.
|
|
599
|
+
2. Use framework stream/controller helpers for the microphone, transcript, reconnect state, and recording status.
|
|
600
|
+
3. Persist a review artifact on completion with transcript, summary, latency, outcome, and recommended follow-up.
|
|
601
|
+
4. Mount trace timelines, operations records, production readiness, and data-control routes.
|
|
602
|
+
5. Gate release with the `meeting-recorder` readiness profile so reconnect, barge-in/interruption, provider routing, latency, and session-health proof stay visible.
|
|
603
|
+
|
|
604
|
+
```ts
|
|
605
|
+
import { Elysia } from 'elysia';
|
|
606
|
+
import {
|
|
607
|
+
createVoiceDataControlRoutes,
|
|
608
|
+
createVoiceFileRuntimeStorage,
|
|
609
|
+
createVoiceOperationsRecordRoutes,
|
|
610
|
+
createVoiceProductionReadinessRoutes,
|
|
611
|
+
createVoiceReadinessProfile,
|
|
612
|
+
createVoiceTraceTimelineRoutes,
|
|
613
|
+
voice,
|
|
614
|
+
voiceComplianceRedactionDefaults
|
|
615
|
+
} from '@absolutejs/voice';
|
|
616
|
+
import { deepgram } from '@absolutejs/voice-deepgram';
|
|
617
|
+
|
|
618
|
+
const runtime = createVoiceFileRuntimeStorage({
|
|
619
|
+
directory: '.voice-runtime/meeting-recorder'
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
const app = new Elysia()
|
|
623
|
+
.use(
|
|
624
|
+
voice({
|
|
625
|
+
path: '/voice/meeting-recorder',
|
|
626
|
+
preset: 'reliability',
|
|
627
|
+
session: runtime.session,
|
|
628
|
+
stt: deepgram({
|
|
629
|
+
apiKey: process.env.DEEPGRAM_API_KEY!,
|
|
630
|
+
model: 'flux-general-en'
|
|
631
|
+
}),
|
|
632
|
+
trace: runtime.traces,
|
|
633
|
+
async onTurn({ turn }) {
|
|
634
|
+
return {
|
|
635
|
+
assistantText: '',
|
|
636
|
+
metadata: {
|
|
637
|
+
recorder: true,
|
|
638
|
+
transcript: turn.text
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
},
|
|
642
|
+
onComplete: async () => {},
|
|
643
|
+
ops: {
|
|
644
|
+
events: runtime.events,
|
|
645
|
+
reviews: runtime.reviews,
|
|
646
|
+
tasks: runtime.tasks,
|
|
647
|
+
buildReview: ({ session }) => ({
|
|
648
|
+
errors: [],
|
|
649
|
+
latencyBreakdown: [],
|
|
650
|
+
notes: ['Generated by the self-hosted meeting recorder path.'],
|
|
651
|
+
postCall: {
|
|
652
|
+
label: 'Meeting summary',
|
|
653
|
+
recommendedAction: 'Review the transcript and share action items.',
|
|
654
|
+
summary: 'Review transcript, decisions, and follow-up owners.'
|
|
655
|
+
},
|
|
656
|
+
summary: {
|
|
657
|
+
outcome: 'completed',
|
|
658
|
+
pass: true,
|
|
659
|
+
turnCount: session.turns.length
|
|
660
|
+
},
|
|
661
|
+
title: `Meeting recorder review for ${session.id}`,
|
|
662
|
+
timeline: [],
|
|
663
|
+
transcript: {
|
|
664
|
+
actual: session.turns
|
|
665
|
+
.map((turn) => turn.text)
|
|
666
|
+
.filter(Boolean)
|
|
667
|
+
.join('\n')
|
|
668
|
+
}
|
|
669
|
+
})
|
|
670
|
+
}
|
|
671
|
+
})
|
|
672
|
+
)
|
|
673
|
+
.use(
|
|
674
|
+
createVoiceTraceTimelineRoutes({
|
|
675
|
+
htmlPath: '/traces',
|
|
676
|
+
path: '/api/voice-traces',
|
|
677
|
+
store: runtime.traces
|
|
678
|
+
})
|
|
679
|
+
)
|
|
680
|
+
.use(
|
|
681
|
+
createVoiceOperationsRecordRoutes({
|
|
682
|
+
htmlPath: '/voice-operations/:sessionId',
|
|
683
|
+
integrationEvents: runtime.events,
|
|
684
|
+
path: '/api/voice-operations/:sessionId',
|
|
685
|
+
reviews: runtime.reviews,
|
|
686
|
+
store: runtime.traces,
|
|
687
|
+
tasks: runtime.tasks
|
|
688
|
+
})
|
|
689
|
+
)
|
|
690
|
+
.use(
|
|
691
|
+
createVoiceDataControlRoutes({
|
|
692
|
+
...runtime,
|
|
693
|
+
audit: runtime.audit,
|
|
694
|
+
auditDeliveries: runtime.auditDeliveries,
|
|
695
|
+
redact: voiceComplianceRedactionDefaults,
|
|
696
|
+
traceDeliveries: runtime.traceDeliveries
|
|
697
|
+
})
|
|
698
|
+
)
|
|
699
|
+
.use(
|
|
700
|
+
createVoiceProductionReadinessRoutes({
|
|
701
|
+
...createVoiceReadinessProfile('meeting-recorder', {
|
|
702
|
+
explain: true
|
|
703
|
+
}),
|
|
704
|
+
links: {
|
|
705
|
+
dataControl: '/data-control',
|
|
706
|
+
operationsRecords: '/voice-operations/:sessionId',
|
|
707
|
+
traces: '/traces'
|
|
708
|
+
},
|
|
709
|
+
store: runtime.traces
|
|
710
|
+
})
|
|
711
|
+
);
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
The UI should show a clear recording button, elapsed time, live transcript, reconnect state, recording status, a stop/finalize action, and links to `/voice-operations/:sessionId`, `/traces`, `/production-readiness`, and `/data-control`. Use `useVoiceStream(...)`, `useVoiceController(...)`, `createVoiceStream(...)`, `VoiceStreamService`, or the HTML/HTMX client helpers depending on the framework; the route and trace/review stores stay the same.
|
|
715
|
+
|
|
716
|
+
For customer-facing exports, use the same redaction/export primitives as support workflows: render trace Markdown or audit Markdown with `voiceComplianceRedactionDefaults`, then deliver it through file, webhook, or S3 delivery runtimes. For sensitive recordings, start with a retention dry run through `/data-control/retention/plan` before applying deletion.
|
|
717
|
+
|
|
718
|
+
This recipe covers the hosted-platform expectations that matter for meeting recorders: browser mic capture, live transcript UI, reconnect visibility, post-call review, transcript/debug record, readiness proof, customer-owned storage, retention controls, and redacted export paths.
|
|
719
|
+
|
|
482
720
|
## How This Differs From Hosted Voice Platforms
|
|
483
721
|
|
|
484
722
|
Hosted voice-agent platforms are strongest when you want a managed dashboard, phone-number provisioning, hosted orchestration, and campaign tooling out of the box.
|