@absolutejs/voice 0.0.22-beta.40 → 0.0.22-beta.41

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
@@ -8,6 +8,7 @@ export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap
8
8
  export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
9
9
  export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, createVoiceProviderRouter } from './modelAdapters';
10
10
  export { createVoiceProviderHealthHTMLHandler, createVoiceProviderHealthJSONHandler, createVoiceProviderHealthRoutes, renderVoiceProviderHealthHTML, summarizeVoiceProviderHealth } from './providerHealth';
11
+ export { buildVoiceOpsConsoleReport, createVoiceOpsConsoleRoutes, renderVoiceOpsConsoleHTML } from './opsConsoleRoutes';
11
12
  export { createVoiceQualityRoutes, evaluateVoiceQuality, renderVoiceQualityHTML } from './qualityRoutes';
12
13
  export { createVoiceResilienceRoutes, listVoiceRoutingEvents, renderVoiceResilienceHTML } from './resilienceRoutes';
13
14
  export { createVoiceSTTProviderRouter, createVoiceTTSProviderRouter } from './providerAdapters';
@@ -41,6 +42,7 @@ export type { VoiceDiagnosticsRoutesOptions } from './diagnosticsRoutes';
41
42
  export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
42
43
  export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
43
44
  export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
45
+ export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
44
46
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
45
47
  export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingEvent, VoiceRoutingEventKind } from './resilienceRoutes';
46
48
  export type { VoiceIOProviderRouterEvent, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
package/dist/index.js CHANGED
@@ -8993,8 +8993,8 @@ var createGeminiVoiceAssistantModel = (options) => {
8993
8993
  }
8994
8994
  };
8995
8995
  };
8996
- // src/qualityRoutes.ts
8997
- import { Elysia as Elysia7 } from "elysia";
8996
+ // src/opsConsoleRoutes.ts
8997
+ import { Elysia as Elysia9 } from "elysia";
8998
8998
 
8999
8999
  // src/handoffHealth.ts
9000
9000
  import { Elysia as Elysia6 } from "elysia";
@@ -9190,6 +9190,7 @@ var createVoiceHandoffHealthRoutes = (options = {}) => {
9190
9190
  };
9191
9191
 
9192
9192
  // src/qualityRoutes.ts
9193
+ import { Elysia as Elysia7 } from "elysia";
9193
9194
  var DEFAULT_THRESHOLDS = {
9194
9195
  maxDuplicateTurnRate: 0,
9195
9196
  maxEmptyTurnRate: 0.02,
@@ -9338,6 +9339,7 @@ var createVoiceQualityRoutes = (options) => {
9338
9339
  });
9339
9340
  return routes;
9340
9341
  };
9342
+
9341
9343
  // src/resilienceRoutes.ts
9342
9344
  import { Elysia as Elysia8 } from "elysia";
9343
9345
  var escapeHtml10 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
@@ -9651,6 +9653,121 @@ var createVoiceResilienceRoutes = (options) => {
9651
9653
  registerSimulationRoutes(routes, options.ttsSimulation, "/api/tts-simulate");
9652
9654
  return routes;
9653
9655
  };
9656
+
9657
+ // src/opsConsoleRoutes.ts
9658
+ var DEFAULT_LINKS = [
9659
+ {
9660
+ description: "Quality gates for CI, deploy checks, and production readiness.",
9661
+ href: "/quality",
9662
+ label: "Quality",
9663
+ statusHref: "/quality/status"
9664
+ },
9665
+ {
9666
+ description: "Provider health, fallback paths, and failure simulation.",
9667
+ href: "/resilience",
9668
+ label: "Resilience"
9669
+ },
9670
+ {
9671
+ description: "Redacted trace exports for debugging and support handoffs.",
9672
+ href: "/diagnostics",
9673
+ label: "Diagnostics"
9674
+ },
9675
+ {
9676
+ description: "Recent sessions with replay links.",
9677
+ href: "/sessions",
9678
+ label: "Sessions"
9679
+ },
9680
+ {
9681
+ description: "Transfer and webhook delivery health.",
9682
+ href: "/handoffs",
9683
+ label: "Handoffs"
9684
+ }
9685
+ ];
9686
+ var escapeHtml11 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
9687
+ var countProviderStatuses = (providers) => {
9688
+ const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
9689
+ const healthy = providers.filter((provider) => provider.status === "healthy").length;
9690
+ const degraded = providers.filter((provider) => degradedStatuses.has(provider.status)).length;
9691
+ return {
9692
+ degraded,
9693
+ healthy,
9694
+ total: providers.length
9695
+ };
9696
+ };
9697
+ var buildVoiceOpsConsoleReport = async (options) => {
9698
+ const events = await options.store.list();
9699
+ const providers = [
9700
+ ...await summarizeVoiceProviderHealth({
9701
+ events,
9702
+ providers: options.llmProviders
9703
+ }),
9704
+ ...await summarizeVoiceProviderHealth({
9705
+ events,
9706
+ providers: options.sttProviders
9707
+ }),
9708
+ ...await summarizeVoiceProviderHealth({
9709
+ events,
9710
+ providers: options.ttsProviders
9711
+ })
9712
+ ];
9713
+ const handoffs = await summarizeVoiceHandoffHealth({ events });
9714
+ const sessions = await summarizeVoiceSessions({
9715
+ events,
9716
+ limit: 8,
9717
+ status: "all"
9718
+ });
9719
+ const quality = await evaluateVoiceQuality({ events });
9720
+ const routingEvents = listVoiceRoutingEvents(events).slice(0, 10);
9721
+ const trace = summarizeVoiceTrace(events);
9722
+ return {
9723
+ checkedAt: Date.now(),
9724
+ eventCount: events.length,
9725
+ handoffs: {
9726
+ failed: handoffs.failed,
9727
+ total: handoffs.total
9728
+ },
9729
+ links: options.links ?? DEFAULT_LINKS,
9730
+ providers: countProviderStatuses(providers),
9731
+ quality,
9732
+ recentRoutingEvents: routingEvents,
9733
+ recentSessions: sessions,
9734
+ sessions: {
9735
+ failed: sessions.filter((session) => session.status === "failed").length,
9736
+ healthy: sessions.filter((session) => session.status === "healthy").length,
9737
+ total: sessions.length
9738
+ },
9739
+ trace
9740
+ };
9741
+ };
9742
+ var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml11(input.label)}</span><strong>${escapeHtml11(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml11(input.status)}">${escapeHtml11(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml11(input.href)}">Open</a>` : ""}</article>`;
9743
+ var renderVoiceOpsConsoleHTML = (report, options = {}) => {
9744
+ const links = report.links.map((link) => `<article class="surface">
9745
+ <div><h2>${escapeHtml11(link.label)}</h2>${link.description ? `<p>${escapeHtml11(link.description)}</p>` : ""}</div>
9746
+ <p><a href="${escapeHtml11(link.href)}">Open ${escapeHtml11(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml11(link.statusHref)}">Status</a>` : ""}</p>
9747
+ </article>`).join("");
9748
+ const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml11(session.sessionId)}</td><td>${escapeHtml11(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml11(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
9749
+ const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml11(event.kind)}</td><td>${escapeHtml11(event.provider ?? "unknown")}</td><td>${escapeHtml11(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml11(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
9750
+ const title = options.title ?? "AbsoluteJS Voice Ops Console";
9751
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml11(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#101316;color:#f6f2e8;margin:0}main{max-width:1180px;margin:auto;padding:32px}a{color:#fbbf24}header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:24px}.eyebrow{color:#fbbf24;font-weight:800;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.5rem);line-height:.95;margin:.2rem 0 1rem}.muted{color:#a8b0b8}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.metric,.surface{background:#181d22;border:1px solid #2a323a;border-radius:20px;padding:18px}.metric strong{display:block;font-size:2.2rem;margin:.25rem 0}.pass,.healthy{color:#86efac}.fail,.failed,.degraded{color:#fca5a5}.surfaces{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:24px 0}table{width:100%;border-collapse:collapse;background:#181d22;border-radius:16px;overflow:hidden;margin:12px 0 28px}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left}section{margin-top:30px}@media(max-width:700px){main{padding:20px}header{display:block}}</style></head><body><main><header><div><p class="eyebrow">Self-hosted voice operations</p><h1>${escapeHtml11(title)}</h1><p class="muted">One deployable control plane for quality gates, failover, traces, sessions, handoffs, and provider health.</p></div><p class="muted">Checked ${escapeHtml11(new Date(report.checkedAt).toLocaleString())}</p></header><div class="grid">${renderMetricCard({ label: "Quality", value: report.quality.status, status: report.quality.status, href: "/quality" })}${renderMetricCard({ label: "Events", value: report.eventCount, href: "/diagnostics" })}${renderMetricCard({ label: "Sessions", value: report.sessions.total, status: report.sessions.failed > 0 ? "failed" : "healthy", href: "/sessions" })}${renderMetricCard({ label: "Handoffs failed", value: report.handoffs.failed, status: report.handoffs.failed > 0 ? "failed" : "healthy", href: "/handoffs" })}${renderMetricCard({ label: "Providers degraded", value: report.providers.degraded, status: report.providers.degraded > 0 ? "degraded" : "healthy", href: "/resilience" })}</div><section><h2>Operational Surfaces</h2><div class="surfaces">${links}</div></section><section><h2>Recent Sessions</h2><table><thead><tr><th>Session</th><th>Status</th><th>Turns</th><th>Errors</th><th>Replay</th></tr></thead><tbody>${sessions}</tbody></table></section><section><h2>Recent Provider Routing</h2><table><thead><tr><th>Kind</th><th>Provider</th><th>Status</th><th>Elapsed</th><th>Session</th></tr></thead><tbody>${routing}</tbody></table></section></main></body></html>`;
9752
+ };
9753
+ var createVoiceOpsConsoleRoutes = (options) => {
9754
+ const path = options.path ?? "/ops-console";
9755
+ const routes = new Elysia9({
9756
+ name: options.name ?? "absolutejs-voice-ops-console"
9757
+ });
9758
+ const getReport = () => buildVoiceOpsConsoleReport(options);
9759
+ routes.get(path, async () => {
9760
+ const report = await getReport();
9761
+ return new Response(renderVoiceOpsConsoleHTML(report, { title: options.title }), {
9762
+ headers: {
9763
+ "Content-Type": "text/html; charset=utf-8",
9764
+ ...options.headers
9765
+ }
9766
+ });
9767
+ });
9768
+ routes.get(`${path}/json`, async () => getReport());
9769
+ return routes;
9770
+ };
9654
9771
  // src/providerAdapters.ts
9655
9772
  class VoiceIOProviderTimeoutError extends Error {
9656
9773
  provider;
@@ -10495,7 +10612,7 @@ var createVoiceMemoryStore = () => {
10495
10612
  return { get, getOrCreate, list, remove, set };
10496
10613
  };
10497
10614
  // src/opsWebhook.ts
10498
- import { Elysia as Elysia9 } from "elysia";
10615
+ import { Elysia as Elysia10 } from "elysia";
10499
10616
  var toHex5 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
10500
10617
  var signVoiceOpsWebhookBody = async (input) => {
10501
10618
  const encoder = new TextEncoder;
@@ -10625,7 +10742,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
10625
10742
  };
10626
10743
  var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
10627
10744
  const path = options.path ?? "/api/voice-ops/webhook";
10628
- return new Elysia9().post(path, async ({ body, request, set }) => {
10745
+ return new Elysia10().post(path, async ({ body, request, set }) => {
10629
10746
  const bodyText = typeof body === "string" ? body : JSON.stringify(body);
10630
10747
  if (options.signingSecret) {
10631
10748
  const verification = await verifyVoiceOpsWebhookSignature({
@@ -12807,6 +12924,7 @@ export {
12807
12924
  renderVoiceResilienceHTML,
12808
12925
  renderVoiceQualityHTML,
12809
12926
  renderVoiceProviderHealthHTML,
12927
+ renderVoiceOpsConsoleHTML,
12810
12928
  renderVoiceHandoffHealthHTML,
12811
12929
  renderVoiceCallReviewMarkdown,
12812
12930
  renderVoiceCallReviewHTML,
@@ -12898,6 +13016,7 @@ export {
12898
13016
  createVoiceOpsTaskProcessorWorkerLoop,
12899
13017
  createVoiceOpsTaskProcessorWorker,
12900
13018
  createVoiceOpsRuntime,
13019
+ createVoiceOpsConsoleRoutes,
12901
13020
  createVoiceMemoryTraceSinkDeliveryStore,
12902
13021
  createVoiceMemoryTraceEventStore,
12903
13022
  createVoiceMemoryStore,
@@ -12968,6 +13087,7 @@ export {
12968
13087
  buildVoiceTraceReplay,
12969
13088
  buildVoiceOpsTaskFromSLABreach,
12970
13089
  buildVoiceOpsTaskFromReview,
13090
+ buildVoiceOpsConsoleReport,
12971
13091
  buildVoiceDiagnosticsMarkdown,
12972
13092
  assignVoiceOpsTask,
12973
13093
  applyVoiceOpsTaskPolicy,
@@ -0,0 +1,77 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type VoiceQualityReport } from './qualityRoutes';
3
+ import { type VoiceRoutingEvent } from './resilienceRoutes';
4
+ import { type VoiceSessionListItem } from './sessionReplay';
5
+ import { summarizeVoiceTrace, type VoiceTraceEventStore } from './trace';
6
+ export type VoiceOpsConsoleLink = {
7
+ description?: string;
8
+ href: string;
9
+ label: string;
10
+ statusHref?: string;
11
+ };
12
+ export type VoiceOpsConsoleReport = {
13
+ checkedAt: number;
14
+ eventCount: number;
15
+ handoffs: {
16
+ failed: number;
17
+ total: number;
18
+ };
19
+ links: VoiceOpsConsoleLink[];
20
+ providers: {
21
+ degraded: number;
22
+ healthy: number;
23
+ total: number;
24
+ };
25
+ quality: VoiceQualityReport;
26
+ recentRoutingEvents: VoiceRoutingEvent[];
27
+ recentSessions: VoiceSessionListItem[];
28
+ sessions: {
29
+ failed: number;
30
+ healthy: number;
31
+ total: number;
32
+ };
33
+ trace: ReturnType<typeof summarizeVoiceTrace>;
34
+ };
35
+ export type VoiceOpsConsoleRoutesOptions = {
36
+ headers?: HeadersInit;
37
+ links?: VoiceOpsConsoleLink[];
38
+ llmProviders?: readonly string[];
39
+ name?: string;
40
+ path?: string;
41
+ store: VoiceTraceEventStore;
42
+ sttProviders?: readonly string[];
43
+ title?: string;
44
+ ttsProviders?: readonly string[];
45
+ };
46
+ export declare const buildVoiceOpsConsoleReport: (options: VoiceOpsConsoleRoutesOptions) => Promise<VoiceOpsConsoleReport>;
47
+ export declare const renderVoiceOpsConsoleHTML: (report: VoiceOpsConsoleReport, options?: {
48
+ title?: string;
49
+ }) => string;
50
+ export declare const createVoiceOpsConsoleRoutes: (options: VoiceOpsConsoleRoutesOptions) => Elysia<"", {
51
+ decorator: {};
52
+ store: {};
53
+ derive: {};
54
+ resolve: {};
55
+ }, {
56
+ typebox: {};
57
+ error: {};
58
+ }, {
59
+ schema: {};
60
+ standaloneSchema: {};
61
+ macro: {};
62
+ macroFn: {};
63
+ parser: {};
64
+ response: {};
65
+ }, {}, {
66
+ derive: {};
67
+ resolve: {};
68
+ schema: {};
69
+ standaloneSchema: {};
70
+ response: {};
71
+ }, {
72
+ derive: {};
73
+ resolve: {};
74
+ schema: {};
75
+ standaloneSchema: {};
76
+ response: {};
77
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.40",
3
+ "version": "0.0.22-beta.41",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",