@absolutejs/voice 0.0.22-beta.64 → 0.0.22-beta.65

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/appKit.d.ts CHANGED
@@ -5,11 +5,12 @@ import { type VoiceEvalRoutesOptions, type VoiceEvalLink } from './evalRoutes';
5
5
  import { type VoiceHandoffHealthRoutesOptions } from './handoffHealth';
6
6
  import { type VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
7
7
  import { type VoiceProviderHealthRoutesOptions } from './providerHealth';
8
+ import { type VoiceProviderCapabilityRoutesOptions } from './providerCapabilities';
8
9
  import { type VoiceQualityRoutesOptions } from './qualityRoutes';
9
10
  import { type VoiceResilienceRoutesOptions } from './resilienceRoutes';
10
11
  import { type VoiceSessionListRoutesOptions, type VoiceSessionReplayRoutesOptions } from './sessionReplay';
11
12
  import { type VoiceTraceEventStore } from './trace';
12
- export type VoiceAppKitSurface = 'assistantHealth' | 'diagnostics' | 'evals' | 'handoffs' | 'opsConsole' | 'providerHealth' | 'quality' | 'resilience' | 'sessionReplay' | 'sessions';
13
+ export type VoiceAppKitSurface = 'assistantHealth' | 'diagnostics' | 'evals' | 'handoffs' | 'opsConsole' | 'providerCapabilities' | 'providerHealth' | 'quality' | 'resilience' | 'sessionReplay' | 'sessions';
13
14
  export type VoiceAppKitLink = VoiceEvalLink & {
14
15
  description?: string;
15
16
  statusHref?: string;
@@ -25,6 +26,7 @@ export type VoiceAppKitRoutesOptions<TProvider extends string = string> = {
25
26
  llmProviders?: readonly TProvider[];
26
27
  name?: string;
27
28
  opsConsole?: false | Partial<VoiceOpsConsoleRoutesOptions>;
29
+ providerCapabilities?: false | Partial<VoiceProviderCapabilityRoutesOptions<TProvider>>;
28
30
  providerHealth?: false | Partial<VoiceProviderHealthRoutesOptions<TProvider>>;
29
31
  quality?: false | Partial<VoiceQualityRoutesOptions>;
30
32
  resilience?: false | Partial<VoiceResilienceRoutesOptions>;
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap
13
13
  export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
14
14
  export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, resolveVoiceProviderRoutingPolicyPreset, createVoiceProviderRouter } from './modelAdapters';
15
15
  export { createVoiceProviderHealthHTMLHandler, createVoiceProviderHealthJSONHandler, createVoiceProviderHealthRoutes, renderVoiceProviderHealthHTML, summarizeVoiceProviderHealth } from './providerHealth';
16
+ export { createVoiceProviderCapabilityHTMLHandler, createVoiceProviderCapabilityJSONHandler, createVoiceProviderCapabilityRoutes, renderVoiceProviderCapabilityHTML, summarizeVoiceProviderCapabilities } from './providerCapabilities';
16
17
  export { buildVoiceOpsConsoleReport, createVoiceOpsConsoleRoutes, renderVoiceOpsConsoleHTML } from './opsConsoleRoutes';
17
18
  export { createVoiceQualityRoutes, evaluateVoiceQuality, renderVoiceQualityHTML } from './qualityRoutes';
18
19
  export { createVoiceResilienceRoutes, createVoiceRoutingDecisionSummary, listVoiceRoutingEvents, renderVoiceResilienceHTML, summarizeVoiceRoutingDecision } from './resilienceRoutes';
@@ -50,6 +51,7 @@ export type { VoiceWorkflowContract, VoiceWorkflowContractDefinition, VoiceWorkf
50
51
  export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
51
52
  export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterPolicyPreset, VoiceProviderRouterPolicyWeights, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceProviderRouterStrategy, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
52
53
  export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
54
+ export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary } from './providerCapabilities';
53
55
  export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
54
56
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
55
57
  export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind } from './resilienceRoutes';
package/dist/index.js CHANGED
@@ -5474,7 +5474,7 @@ var voice = (config) => {
5474
5474
  }).use(htmxRoutes());
5475
5475
  };
5476
5476
  // src/appKit.ts
5477
- import { Elysia as Elysia11 } from "elysia";
5477
+ import { Elysia as Elysia12 } from "elysia";
5478
5478
 
5479
5479
  // src/assistantHealth.ts
5480
5480
  import { Elysia as Elysia3 } from "elysia";
@@ -9186,6 +9186,117 @@ var createVoiceOpsConsoleRoutes = (options) => {
9186
9186
  return routes;
9187
9187
  };
9188
9188
 
9189
+ // src/providerCapabilities.ts
9190
+ import { Elysia as Elysia11 } from "elysia";
9191
+ var escapeHtml13 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
9192
+ var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
9193
+ configured: true,
9194
+ features: options.features?.[provider],
9195
+ kind,
9196
+ model: options.models?.[provider],
9197
+ provider,
9198
+ selected: options.selected?.[kind] === provider
9199
+ }));
9200
+ var resolveCapabilityDefinitions = (options) => {
9201
+ const definitions = [
9202
+ ...fromProviderList("llm", options.llmProviders, options),
9203
+ ...fromProviderList("stt", options.sttProviders, options),
9204
+ ...fromProviderList("tts", options.ttsProviders, options),
9205
+ ...options.providers ?? []
9206
+ ];
9207
+ const seen = new Set;
9208
+ return definitions.filter((definition) => {
9209
+ const key = `${definition.kind}:${definition.provider}`;
9210
+ if (seen.has(key)) {
9211
+ return false;
9212
+ }
9213
+ seen.add(key);
9214
+ return true;
9215
+ });
9216
+ };
9217
+ var summarizeVoiceProviderCapabilities = async (options) => {
9218
+ const definitions = resolveCapabilityDefinitions(options);
9219
+ const providerNames = [...new Set(definitions.map((entry) => entry.provider))];
9220
+ const health = await summarizeVoiceProviderHealth({
9221
+ events: options.events,
9222
+ now: options.now,
9223
+ providers: providerNames,
9224
+ store: options.store
9225
+ });
9226
+ const healthByProvider = new Map(health.map((entry) => [entry.provider, entry]));
9227
+ const capabilities = definitions.map((definition) => {
9228
+ const configured = definition.configured !== false;
9229
+ const providerHealth = healthByProvider.get(definition.provider);
9230
+ const selected = definition.selected === true || options.selected?.[definition.kind] === definition.provider;
9231
+ const status = !configured ? "unconfigured" : selected ? "selected" : providerHealth?.status ?? "idle";
9232
+ return {
9233
+ ...definition,
9234
+ configured,
9235
+ features: definition.features ?? options.features?.[definition.provider],
9236
+ health: providerHealth,
9237
+ model: definition.model ?? options.models?.[definition.provider],
9238
+ selected,
9239
+ status
9240
+ };
9241
+ });
9242
+ return {
9243
+ capabilities,
9244
+ checkedAt: Date.now(),
9245
+ configured: capabilities.filter((entry) => entry.configured).length,
9246
+ selected: capabilities.filter((entry) => entry.selected).length,
9247
+ total: capabilities.length,
9248
+ unconfigured: capabilities.filter((entry) => !entry.configured).length
9249
+ };
9250
+ };
9251
+ var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
9252
+ const title = options.title ?? "Voice Provider Capabilities";
9253
+ const cards = report.capabilities.map((capability) => {
9254
+ const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml13(feature)}</span>`).join("");
9255
+ return `<article class="card ${escapeHtml13(capability.status)}">
9256
+ <div class="card-header">
9257
+ <div>
9258
+ <p class="eyebrow">${escapeHtml13(capability.kind)}</p>
9259
+ <h2>${escapeHtml13(capability.label ?? capability.provider)}</h2>
9260
+ </div>
9261
+ <strong>${escapeHtml13(capability.status)}</strong>
9262
+ </div>
9263
+ ${capability.description ? `<p>${escapeHtml13(capability.description)}</p>` : ""}
9264
+ <dl>
9265
+ <div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
9266
+ <div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
9267
+ <div><dt>Model</dt><dd>${escapeHtml13(capability.model ?? "default")}</dd></div>
9268
+ <div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
9269
+ <div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
9270
+ </dl>
9271
+ ${features ? `<div class="features">${features}</div>` : ""}
9272
+ </article>`;
9273
+ }).join("");
9274
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml13(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.card{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.16),rgba(34,197,94,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary,.features{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.card-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.selected,.healthy{color:#86efac}.unconfigured,.degraded,.rate-limited,.suppressed{color:#fca5a5}.idle,.recoverable{color:#fde68a}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.card-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider Discovery</p><h1>${escapeHtml13(title)}</h1><div class="summary"><span class="pill">${String(report.configured)} configured</span><span class="pill">${String(report.selected)} selected</span><span class="pill">${String(report.unconfigured)} missing</span><span class="pill">${String(report.total)} total</span></div></section><section class="grid">${cards || '<article class="card"><p>No provider capabilities configured.</p></article>'}</section></main></body></html>`;
9275
+ };
9276
+ var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
9277
+ var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
9278
+ const report = await summarizeVoiceProviderCapabilities(options);
9279
+ const render = options.render ?? ((input) => renderVoiceProviderCapabilityHTML(input, options));
9280
+ const body = await render(report);
9281
+ return new Response(body, {
9282
+ headers: {
9283
+ "Content-Type": "text/html; charset=utf-8",
9284
+ ...options.headers
9285
+ }
9286
+ });
9287
+ };
9288
+ var createVoiceProviderCapabilityRoutes = (options) => {
9289
+ const path = options.path ?? "/api/provider-capabilities";
9290
+ const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
9291
+ const routes = new Elysia11({
9292
+ name: options.name ?? "absolutejs-voice-provider-capabilities"
9293
+ }).get(path, createVoiceProviderCapabilityJSONHandler(options));
9294
+ if (htmlPath) {
9295
+ routes.get(htmlPath, createVoiceProviderCapabilityHTMLHandler(options));
9296
+ }
9297
+ return routes;
9298
+ };
9299
+
9189
9300
  // src/appKit.ts
9190
9301
  var DEFAULT_LINKS2 = [
9191
9302
  {
@@ -9342,7 +9453,7 @@ var summarizeVoiceAppKitStatus = async (options) => {
9342
9453
  };
9343
9454
  };
9344
9455
  var createVoiceAppKitRoutes = (options) => {
9345
- const routes = new Elysia11({
9456
+ const routes = new Elysia12({
9346
9457
  name: options.name ?? "absolutejs-voice-app-kit"
9347
9458
  });
9348
9459
  const links = resolveLinks(options.links);
@@ -9362,6 +9473,19 @@ var createVoiceAppKitRoutes = (options) => {
9362
9473
  ...options.providerHealth
9363
9474
  }));
9364
9475
  }
9476
+ if (options.providerCapabilities !== false) {
9477
+ surfaces.push("providerCapabilities");
9478
+ routes.use(createVoiceProviderCapabilityRoutes({
9479
+ ...common,
9480
+ htmlPath: "/provider-capabilities",
9481
+ llmProviders: options.llmProviders,
9482
+ path: "/api/provider-capabilities",
9483
+ sttProviders: options.sttProviders,
9484
+ title: options.title ? `${options.title} Provider Capabilities` : undefined,
9485
+ ttsProviders: options.ttsProviders,
9486
+ ...options.providerCapabilities
9487
+ }));
9488
+ }
9365
9489
  if (options.assistantHealth !== false) {
9366
9490
  surfaces.push("assistantHealth");
9367
9491
  routes.use(createVoiceAssistantHealthRoutes({
@@ -9947,7 +10071,7 @@ var createVoiceToolIdempotencyKey = (input) => {
9947
10071
  ].join(":");
9948
10072
  };
9949
10073
  // src/toolContract.ts
9950
- import { Elysia as Elysia12 } from "elysia";
10074
+ import { Elysia as Elysia13 } from "elysia";
9951
10075
  var createDefaultSession = (contractId, caseId) => createVoiceSessionRecord(`tool-contract-${contractId}-${caseId}`);
9952
10076
  var createDefaultTurn = (caseId) => ({
9953
10077
  committedAt: Date.now(),
@@ -9957,7 +10081,7 @@ var createDefaultTurn = (caseId) => ({
9957
10081
  });
9958
10082
  var defaultApi = {};
9959
10083
  var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
9960
- var escapeHtml13 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
10084
+ var escapeHtml14 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
9961
10085
  var evaluateExpectation = (input) => {
9962
10086
  const issues = [];
9963
10087
  const expect = input.expect;
@@ -10123,19 +10247,19 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
10123
10247
  const title = options.title ?? "Voice Tool Contracts";
10124
10248
  const contracts = report.contracts.map((contract) => {
10125
10249
  const cases = contract.cases.map((testCase) => `<tr>
10126
- <td>${escapeHtml13(testCase.label ?? testCase.caseId)}</td>
10250
+ <td>${escapeHtml14(testCase.label ?? testCase.caseId)}</td>
10127
10251
  <td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
10128
- <td>${escapeHtml13(testCase.status)}</td>
10252
+ <td>${escapeHtml14(testCase.status)}</td>
10129
10253
  <td>${String(testCase.attempts)}</td>
10130
10254
  <td>${String(testCase.elapsedMs)}ms</td>
10131
10255
  <td>${testCase.timedOut ? "yes" : "no"}</td>
10132
- <td>${escapeHtml13(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
10256
+ <td>${escapeHtml14(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
10133
10257
  </tr>`).join("");
10134
10258
  return `<section class="contract ${contract.pass ? "pass" : "fail"}">
10135
10259
  <div class="contract-header">
10136
10260
  <div>
10137
- <p class="eyebrow">${escapeHtml13(contract.toolName)}</p>
10138
- <h2>${escapeHtml13(contract.label ?? contract.contractId)}</h2>
10261
+ <p class="eyebrow">${escapeHtml14(contract.toolName)}</p>
10262
+ <h2>${escapeHtml14(contract.label ?? contract.contractId)}</h2>
10139
10263
  </div>
10140
10264
  <strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
10141
10265
  </div>
@@ -10145,7 +10269,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
10145
10269
  </table>
10146
10270
  </section>`;
10147
10271
  }).join("");
10148
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml13(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(245,158,11,.12))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}h2{margin:.2rem 0 1rem}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left;vertical-align:top}th{color:#a8b0b8;font-size:.82rem}@media(max-width:800px){main{padding:18px}table{display:block;overflow:auto}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Tool Reliability</p><h1>${escapeHtml13(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml13(report.status)}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No tool contracts configured.</p></section>'}</main></body></html>`;
10272
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml14(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(245,158,11,.12))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}h2{margin:.2rem 0 1rem}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left;vertical-align:top}th{color:#a8b0b8;font-size:.82rem}@media(max-width:800px){main{padding:18px}table{display:block;overflow:auto}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Tool Reliability</p><h1>${escapeHtml14(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml14(report.status)}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No tool contracts configured.</p></section>'}</main></body></html>`;
10149
10273
  };
10150
10274
  var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
10151
10275
  var createVoiceToolContractHTMLHandler = (options) => async () => {
@@ -10162,7 +10286,7 @@ var createVoiceToolContractHTMLHandler = (options) => async () => {
10162
10286
  var createVoiceToolContractRoutes = (options) => {
10163
10287
  const path = options.path ?? "/api/tool-contracts";
10164
10288
  const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
10165
- const routes = new Elysia12({
10289
+ const routes = new Elysia13({
10166
10290
  name: options.name ?? "absolutejs-voice-tool-contracts"
10167
10291
  }).get(path, createVoiceToolContractJSONHandler(options));
10168
10292
  if (htmlPath) {
@@ -12263,7 +12387,7 @@ var createVoiceMemoryStore = () => {
12263
12387
  return { get, getOrCreate, list, remove, set };
12264
12388
  };
12265
12389
  // src/opsWebhook.ts
12266
- import { Elysia as Elysia13 } from "elysia";
12390
+ import { Elysia as Elysia14 } from "elysia";
12267
12391
  var toHex5 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
12268
12392
  var signVoiceOpsWebhookBody = async (input) => {
12269
12393
  const encoder = new TextEncoder;
@@ -12393,7 +12517,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
12393
12517
  };
12394
12518
  var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
12395
12519
  const path = options.path ?? "/api/voice-ops/webhook";
12396
- return new Elysia13().post(path, async ({ body, request, set }) => {
12520
+ return new Elysia14().post(path, async ({ body, request, set }) => {
12397
12521
  const bodyText = typeof body === "string" ? body : JSON.stringify(body);
12398
12522
  if (options.signingSecret) {
12399
12523
  const verification = await verifyVoiceOpsWebhookSignature({
@@ -14546,6 +14670,7 @@ export {
14546
14670
  summarizeVoiceSessionReplay,
14547
14671
  summarizeVoiceRoutingDecision,
14548
14672
  summarizeVoiceProviderHealth,
14673
+ summarizeVoiceProviderCapabilities,
14549
14674
  summarizeVoiceOpsTasks,
14550
14675
  summarizeVoiceOpsTaskQueue,
14551
14676
  summarizeVoiceOpsTaskAnalytics,
@@ -14587,6 +14712,7 @@ export {
14587
14712
  renderVoiceResilienceHTML,
14588
14713
  renderVoiceQualityHTML,
14589
14714
  renderVoiceProviderHealthHTML,
14715
+ renderVoiceProviderCapabilityHTML,
14590
14716
  renderVoiceOpsConsoleHTML,
14591
14717
  renderVoiceHandoffHealthHTML,
14592
14718
  renderVoiceEvalHTML,
@@ -14679,6 +14805,9 @@ export {
14679
14805
  createVoiceProviderHealthRoutes,
14680
14806
  createVoiceProviderHealthJSONHandler,
14681
14807
  createVoiceProviderHealthHTMLHandler,
14808
+ createVoiceProviderCapabilityRoutes,
14809
+ createVoiceProviderCapabilityJSONHandler,
14810
+ createVoiceProviderCapabilityHTMLHandler,
14682
14811
  createVoicePostgresTraceSinkDeliveryStore,
14683
14812
  createVoicePostgresTraceEventStore,
14684
14813
  createVoicePostgresTaskStore,
@@ -0,0 +1,92 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type VoiceProviderHealthStatus, type VoiceProviderHealthSummary, type VoiceProviderHealthSummaryOptions } from './providerHealth';
3
+ export type VoiceProviderCapabilityKind = 'llm' | 'stt' | 'tts' | 'custom';
4
+ export type VoiceProviderCapabilityDefinition<TProvider extends string = string> = {
5
+ configured?: boolean;
6
+ description?: string;
7
+ features?: string[];
8
+ kind: VoiceProviderCapabilityKind;
9
+ label?: string;
10
+ model?: string;
11
+ provider: TProvider;
12
+ selected?: boolean;
13
+ };
14
+ export type VoiceProviderCapabilitySummary<TProvider extends string = string> = VoiceProviderCapabilityDefinition<TProvider> & {
15
+ configured: boolean;
16
+ health?: VoiceProviderHealthSummary<TProvider>;
17
+ status: VoiceProviderHealthStatus | 'selected' | 'unconfigured';
18
+ };
19
+ export type VoiceProviderCapabilityReport<TProvider extends string = string> = {
20
+ capabilities: VoiceProviderCapabilitySummary<TProvider>[];
21
+ checkedAt: number;
22
+ configured: number;
23
+ selected: number;
24
+ total: number;
25
+ unconfigured: number;
26
+ };
27
+ export type VoiceProviderCapabilityOptions<TProvider extends string = string> = VoiceProviderHealthSummaryOptions<TProvider> & {
28
+ features?: Partial<Record<TProvider, string[]>>;
29
+ llmProviders?: readonly TProvider[];
30
+ models?: Partial<Record<TProvider, string>>;
31
+ providers?: readonly VoiceProviderCapabilityDefinition<TProvider>[];
32
+ selected?: Partial<Record<VoiceProviderCapabilityKind, TProvider>>;
33
+ sttProviders?: readonly TProvider[];
34
+ ttsProviders?: readonly TProvider[];
35
+ };
36
+ export type VoiceProviderCapabilityHandlerOptions<TProvider extends string = string> = VoiceProviderCapabilityOptions<TProvider>;
37
+ export type VoiceProviderCapabilityHTMLHandlerOptions<TProvider extends string = string> = VoiceProviderCapabilityHandlerOptions<TProvider> & {
38
+ headers?: HeadersInit;
39
+ render?: (report: VoiceProviderCapabilityReport<TProvider>) => string | Promise<string>;
40
+ title?: string;
41
+ };
42
+ export type VoiceProviderCapabilityRoutesOptions<TProvider extends string = string> = VoiceProviderCapabilityHTMLHandlerOptions<TProvider> & {
43
+ htmlPath?: false | string;
44
+ name?: string;
45
+ path?: string;
46
+ };
47
+ export declare const summarizeVoiceProviderCapabilities: <TProvider extends string = string>(options: VoiceProviderCapabilityOptions<TProvider>) => Promise<VoiceProviderCapabilityReport<TProvider>>;
48
+ export declare const renderVoiceProviderCapabilityHTML: <TProvider extends string = string>(report: VoiceProviderCapabilityReport<TProvider>, options?: {
49
+ title?: string;
50
+ }) => string;
51
+ export declare const createVoiceProviderCapabilityJSONHandler: <TProvider extends string = string>(options: VoiceProviderCapabilityHandlerOptions<TProvider>) => () => Promise<VoiceProviderCapabilityReport<TProvider>>;
52
+ export declare const createVoiceProviderCapabilityHTMLHandler: <TProvider extends string = string>(options: VoiceProviderCapabilityHTMLHandlerOptions<TProvider>) => () => Promise<Response>;
53
+ export declare const createVoiceProviderCapabilityRoutes: <TProvider extends string = string>(options: VoiceProviderCapabilityRoutesOptions<TProvider>) => Elysia<"", {
54
+ decorator: {};
55
+ store: {};
56
+ derive: {};
57
+ resolve: {};
58
+ }, {
59
+ typebox: {};
60
+ error: {};
61
+ }, {
62
+ schema: {};
63
+ standaloneSchema: {};
64
+ macro: {};
65
+ macroFn: {};
66
+ parser: {};
67
+ response: {};
68
+ }, {
69
+ [x: string]: {
70
+ get: {
71
+ body: unknown;
72
+ params: {};
73
+ query: unknown;
74
+ headers: unknown;
75
+ response: {
76
+ 200: VoiceProviderCapabilityReport<TProvider>;
77
+ };
78
+ };
79
+ };
80
+ }, {
81
+ derive: {};
82
+ resolve: {};
83
+ schema: {};
84
+ standaloneSchema: {};
85
+ response: {};
86
+ }, {
87
+ derive: {};
88
+ resolve: {};
89
+ schema: {};
90
+ standaloneSchema: {};
91
+ response: {};
92
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.64",
3
+ "version": "0.0.22-beta.65",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",