@absolutejs/voice 0.0.22-beta.120 → 0.0.22-beta.122
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 +42 -3
- package/dist/index.js +15 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,9 +224,9 @@ app.use(
|
|
|
224
224
|
|
|
225
225
|
The suite rolls up session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts into one pass/fail report. It is the code-owned equivalent of "test this voice flow before production" without requiring a hosted voice-agent dashboard.
|
|
226
226
|
|
|
227
|
-
## Phone Voice Agent
|
|
227
|
+
## Phone Voice Agent In 20 Minutes
|
|
228
228
|
|
|
229
|
-
Use
|
|
229
|
+
Use `createVoicePhoneAgent(...)` when the agent needs to answer or place calls through your own Twilio, Telnyx, or Plivo account. This is the self-hosted alternative to a hosted phone-agent dashboard: your app owns the carrier routes, stream URLs, webhooks, traces, readiness checks, and lifecycle outcomes.
|
|
230
230
|
|
|
231
231
|
```ts
|
|
232
232
|
import {
|
|
@@ -242,6 +242,10 @@ const outcomePolicy = createVoiceTelephonyOutcomePolicy({
|
|
|
242
242
|
app
|
|
243
243
|
.use(
|
|
244
244
|
createVoicePhoneAgent({
|
|
245
|
+
setup: {
|
|
246
|
+
path: '/api/voice/phone/setup',
|
|
247
|
+
title: 'Support Phone Agent'
|
|
248
|
+
},
|
|
245
249
|
matrix: {
|
|
246
250
|
path: '/api/carriers',
|
|
247
251
|
title: 'AbsoluteJS Voice Carrier Matrix'
|
|
@@ -274,7 +278,42 @@ app
|
|
|
274
278
|
);
|
|
275
279
|
```
|
|
276
280
|
|
|
277
|
-
The wrapper mounts selected carrier routes
|
|
281
|
+
The wrapper mounts selected carrier routes plus two proof surfaces:
|
|
282
|
+
|
|
283
|
+
- `/api/voice/phone/setup`: one setup report with carrier URLs, smoke links, lifecycle stages, and readiness.
|
|
284
|
+
- `/api/voice/phone/setup?format=html`: copy/paste setup page for carrier dashboards.
|
|
285
|
+
- `/api/carriers`: carrier matrix JSON for Twilio, Telnyx, and Plivo.
|
|
286
|
+
- `/api/carriers?format=html`: side-by-side carrier readiness matrix.
|
|
287
|
+
|
|
288
|
+
The setup page tells you exactly what to copy into the carrier dashboard:
|
|
289
|
+
|
|
290
|
+
- Twilio: set the phone number voice webhook/TwiML URL to the reported TwiML URL, set the status callback to the reported webhook URL, and allow the reported `wss://` media stream.
|
|
291
|
+
- Telnyx: set the connection TeXML URL to the reported TeXML URL, set the status webhook to the reported webhook URL, and allow the reported `wss://` media stream.
|
|
292
|
+
- Plivo: set the answer URL to the reported answer URL, set the status callback to the reported webhook URL, and allow the reported `wss://` media stream.
|
|
293
|
+
|
|
294
|
+
Each configured carrier can also expose its own setup and smoke pages, for example:
|
|
295
|
+
|
|
296
|
+
- `/api/voice/twilio/setup?format=html`
|
|
297
|
+
- `/api/voice/twilio/smoke?format=html`
|
|
298
|
+
- `/api/voice/telnyx/setup?format=html`
|
|
299
|
+
- `/api/voice/telnyx/smoke?format=html`
|
|
300
|
+
- `/api/voice/plivo/setup?format=html`
|
|
301
|
+
- `/api/voice/plivo/smoke?format=html`
|
|
302
|
+
|
|
303
|
+
The phone-agent report normalizes the lifecycle schema across carriers:
|
|
304
|
+
|
|
305
|
+
- `ringing`
|
|
306
|
+
- `answered`
|
|
307
|
+
- `media-started`
|
|
308
|
+
- `transcript`
|
|
309
|
+
- `assistant-response`
|
|
310
|
+
- `transfer`
|
|
311
|
+
- `voicemail`
|
|
312
|
+
- `no-answer`
|
|
313
|
+
- `completed`
|
|
314
|
+
- `failed`
|
|
315
|
+
|
|
316
|
+
That is the important Vapi/Retell/Bland gap this primitive closes: a team can mount one phone-agent entrypoint, bring its own carrier account, verify readiness before live calls, and keep call traces and lifecycle outcomes inside its own AbsoluteJS app. Telnyx and Plivo use the same wrapper with `{ provider: 'telnyx', options: ... }` or `{ provider: 'plivo', options: ... }`. The lower-level `createTwilioVoiceRoutes(...)`, `createTelnyxVoiceRoutes(...)`, and `createPlivoVoiceRoutes(...)` helpers remain available when you need carrier-specific control.
|
|
278
317
|
|
|
279
318
|
## App Kit And Status Widgets
|
|
280
319
|
|
package/dist/index.js
CHANGED
|
@@ -15338,9 +15338,22 @@ var loadCarrierMatrixInputs = async (input) => {
|
|
|
15338
15338
|
return entries;
|
|
15339
15339
|
};
|
|
15340
15340
|
var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
15341
|
-
const carrierRows = report.carriers.map((carrier) =>
|
|
15341
|
+
const carrierRows = report.carriers.map((carrier) => {
|
|
15342
|
+
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
15343
|
+
const urls = entry?.setup.urls;
|
|
15344
|
+
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
15345
|
+
return `<tr><td>${escapeHtml28(carrier.name ?? carrier.provider)}</td><td>${escapeHtml28(carrier.provider)}</td><td><code>${escapeHtml28(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml28(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml28(entry.status)}">${escapeHtml28(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml28(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml28(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml28(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
15346
|
+
}).join("");
|
|
15342
15347
|
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml28(stage)}</code></li>`).join("");
|
|
15343
|
-
|
|
15348
|
+
const checklist = report.carriers.map((carrier) => {
|
|
15349
|
+
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
15350
|
+
const urls = entry?.setup.urls;
|
|
15351
|
+
const answerLabel = carrier.provider === "telnyx" ? "TeXML URL" : carrier.provider === "plivo" ? "Answer URL" : "TwiML URL";
|
|
15352
|
+
const answerUrl = urls?.twiml;
|
|
15353
|
+
const issueList = entry?.issues.map((issue) => `<li>${escapeHtml28(issue.severity)}: ${escapeHtml28(issue.message)}</li>`).join("") ?? "";
|
|
15354
|
+
return `<article><h3>${escapeHtml28(carrier.name ?? carrier.provider)}</h3><ol><li>Set ${escapeHtml28(answerLabel)} to <code>${escapeHtml28(answerUrl ?? "missing")}</code>.</li><li>Set status webhook to <code>${escapeHtml28(urls?.webhook ?? "missing")}</code>.</li><li>Allow media stream URL <code>${escapeHtml28(urls?.stream ?? "missing")}</code>.</li><li>Open setup: ${carrier.setupPath ? `<a href="${escapeHtml28(carrier.setupPath)}?format=html">${escapeHtml28(carrier.setupPath)}</a>` : '<span class="muted">disabled</span>'}.</li><li>Run smoke: ${carrier.smokePath ? `<a href="${escapeHtml28(carrier.smokePath)}?format=html">${escapeHtml28(carrier.smokePath)}</a>` : '<span class="muted">disabled</span>'}.</li></ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
15355
|
+
}).join("");
|
|
15356
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml28(report.title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.warn{color:#fde68a}.muted{color:#aab5c0}table{background:#151d27;border:1px solid #283544;border-collapse:collapse;border-radius:18px;display:block;overflow:auto;width:100%}td,th{border-bottom:1px solid #283544;padding:12px;text-align:left;vertical-align:top}code{color:#fde68a;overflow-wrap:anywhere}.checklist{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));margin:18px 0}.checklist article{background:#151d27;border:1px solid #283544;border-radius:18px;padding:18px}.checklist ol{padding-left:20px}.issues{color:#fca5a5}.stages{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));padding-left:18px}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent setup</p><h1>${escapeHtml28(report.title)}</h1><p>One self-hosted entrypoint for carrier routes, setup reports, smoke checks, and normalized call lifecycle stages.</p><p class="badge ${report.ready ? "pass" : "fail"}">Ready: ${String(report.ready)}</p>${report.matrixPath ? `<p><a href="${escapeHtml28(report.matrixPath)}?format=html">Open carrier matrix</a></p>` : ""}</section><h2>Carrier Setup Checklist</h2><section class="checklist">${checklist}</section><h2>Carrier URLs</h2><table><thead><tr><th>Name</th><th>Provider</th><th>Setup</th><th>Smoke</th><th>Status</th><th>Answer/TwiML/TeXML</th><th>Webhook</th><th>Stream</th></tr></thead><tbody>${carrierRows}</tbody></table><h2>Lifecycle Schema</h2><ul class="stages">${stageList}</ul></main></body></html>`;
|
|
15344
15357
|
};
|
|
15345
15358
|
var createVoicePhoneAgent = (options) => {
|
|
15346
15359
|
const carrierSummaries = options.carriers.map((carrier) => ({
|