@absolutejs/voice 0.0.22-beta.63 → 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>;
@@ -29,7 +29,7 @@ export type VoiceOpsStatusWidgetOptions = VoiceAppKitStatusClientOptions & {
29
29
  includeLinks?: boolean;
30
30
  title?: string;
31
31
  };
32
- export declare const getVoiceOpsStatusLabel: (report?: VoiceAppKitStatusReport | null, error?: string | null) => "Unavailable" | "Checking" | "Passing" | "Needs attention";
32
+ export declare const getVoiceOpsStatusLabel: (report?: VoiceAppKitStatusReport | null, error?: string | null) => "Passing" | "Unavailable" | "Checking" | "Needs attention";
33
33
  export declare const createVoiceOpsStatusViewModel: (snapshot: VoiceAppKitStatusSnapshot, options?: VoiceOpsStatusWidgetOptions) => VoiceOpsStatusViewModel;
34
34
  export declare const renderVoiceOpsStatusHTML: (snapshot: VoiceAppKitStatusSnapshot, options?: VoiceOpsStatusWidgetOptions) => string;
35
35
  export declare const getVoiceOpsStatusCSS: () => string;
package/dist/index.d.ts CHANGED
@@ -8,11 +8,12 @@ export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, create
8
8
  export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
9
9
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
10
10
  export { createVoiceToolIdempotencyKey, createVoiceToolRuntime } from './toolRuntime';
11
- export { createVoiceToolContract, createVoiceToolRuntimeContractDefaults, runVoiceToolContract } from './toolContract';
11
+ export { createVoiceToolContract, createVoiceToolContractHTMLHandler, createVoiceToolContractJSONHandler, createVoiceToolContractRoutes, createVoiceToolRuntimeContractDefaults, renderVoiceToolContractHTML, runVoiceToolContractSuite, runVoiceToolContract } from './toolContract';
12
12
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
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,13 +51,14 @@ 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';
56
58
  export type { VoiceIOProviderRouterEvent, VoiceIOProviderRouterOptions, VoiceIOProviderRouterPolicy, VoiceIOProviderRouterPolicyConfig, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
57
59
  export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
58
60
  export type { VoiceToolRetryDelay, VoiceToolRuntime, VoiceToolRuntimeExecuteInput, VoiceToolRuntimeOptions, VoiceToolRuntimeResult } from './toolRuntime';
59
- export type { VoiceToolContractCase, VoiceToolContractCaseReport, VoiceToolContractDefinition, VoiceToolContractExpectation, VoiceToolContractIssue, VoiceToolContractReport } from './toolContract';
61
+ export type { VoiceToolContractCase, VoiceToolContractCaseReport, VoiceToolContractDefinition, VoiceToolContractExpectation, VoiceToolContractHandlerOptions, VoiceToolContractHTMLHandlerOptions, VoiceToolContractIssue, VoiceToolContractReport, VoiceToolContractRoutesOptions, VoiceToolContractSuiteReport } from './toolContract';
60
62
  export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
61
63
  export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
62
64
  export type { VoiceOutcomeRecipe, VoiceOutcomeRecipeName, VoiceOutcomeRecipeOptions } from './outcomeRecipes';
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,6 +10071,7 @@ var createVoiceToolIdempotencyKey = (input) => {
9947
10071
  ].join(":");
9948
10072
  };
9949
10073
  // src/toolContract.ts
10074
+ import { Elysia as Elysia13 } from "elysia";
9950
10075
  var createDefaultSession = (contractId, caseId) => createVoiceSessionRecord(`tool-contract-${contractId}-${caseId}`);
9951
10076
  var createDefaultTurn = (caseId) => ({
9952
10077
  committedAt: Date.now(),
@@ -9956,6 +10081,7 @@ var createDefaultTurn = (caseId) => ({
9956
10081
  });
9957
10082
  var defaultApi = {};
9958
10083
  var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
10084
+ var escapeHtml14 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
9959
10085
  var evaluateExpectation = (input) => {
9960
10086
  const issues = [];
9961
10087
  const expect = input.expect;
@@ -10075,6 +10201,7 @@ var runVoiceToolContract = async (definition) => {
10075
10201
  return {
10076
10202
  cases,
10077
10203
  contractId: definition.id,
10204
+ label: definition.label,
10078
10205
  issues,
10079
10206
  pass: issues.length === 0,
10080
10207
  toolName: definition.tool.name
@@ -10103,6 +10230,70 @@ var createVoiceToolRuntimeContractDefaults = () => ({
10103
10230
  maxRetries: 1,
10104
10231
  timeoutMs: 5000
10105
10232
  });
10233
+ var runVoiceToolContractSuite = async (options) => {
10234
+ const contracts = await Promise.all(options.contracts.map((contract) => runVoiceToolContract(contract)));
10235
+ const passed = contracts.filter((contract) => contract.pass).length;
10236
+ const failed = contracts.length - passed;
10237
+ return {
10238
+ checkedAt: Date.now(),
10239
+ contracts,
10240
+ failed,
10241
+ passed,
10242
+ status: failed > 0 ? "fail" : "pass",
10243
+ total: contracts.length
10244
+ };
10245
+ };
10246
+ var renderVoiceToolContractHTML = (report, options = {}) => {
10247
+ const title = options.title ?? "Voice Tool Contracts";
10248
+ const contracts = report.contracts.map((contract) => {
10249
+ const cases = contract.cases.map((testCase) => `<tr>
10250
+ <td>${escapeHtml14(testCase.label ?? testCase.caseId)}</td>
10251
+ <td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
10252
+ <td>${escapeHtml14(testCase.status)}</td>
10253
+ <td>${String(testCase.attempts)}</td>
10254
+ <td>${String(testCase.elapsedMs)}ms</td>
10255
+ <td>${testCase.timedOut ? "yes" : "no"}</td>
10256
+ <td>${escapeHtml14(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
10257
+ </tr>`).join("");
10258
+ return `<section class="contract ${contract.pass ? "pass" : "fail"}">
10259
+ <div class="contract-header">
10260
+ <div>
10261
+ <p class="eyebrow">${escapeHtml14(contract.toolName)}</p>
10262
+ <h2>${escapeHtml14(contract.label ?? contract.contractId)}</h2>
10263
+ </div>
10264
+ <strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
10265
+ </div>
10266
+ <table>
10267
+ <thead><tr><th>Case</th><th>Status</th><th>Result</th><th>Attempts</th><th>Elapsed</th><th>Timed out</th><th>Issues</th></tr></thead>
10268
+ <tbody>${cases}</tbody>
10269
+ </table>
10270
+ </section>`;
10271
+ }).join("");
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>`;
10273
+ };
10274
+ var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
10275
+ var createVoiceToolContractHTMLHandler = (options) => async () => {
10276
+ const report = await runVoiceToolContractSuite(options);
10277
+ const render = options.render ?? ((input) => renderVoiceToolContractHTML(input, options));
10278
+ const body = await render(report);
10279
+ return new Response(body, {
10280
+ headers: {
10281
+ "Content-Type": "text/html; charset=utf-8",
10282
+ ...options.headers
10283
+ }
10284
+ });
10285
+ };
10286
+ var createVoiceToolContractRoutes = (options) => {
10287
+ const path = options.path ?? "/api/tool-contracts";
10288
+ const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
10289
+ const routes = new Elysia13({
10290
+ name: options.name ?? "absolutejs-voice-tool-contracts"
10291
+ }).get(path, createVoiceToolContractJSONHandler(options));
10292
+ if (htmlPath) {
10293
+ routes.get(htmlPath, createVoiceToolContractHTMLHandler(options));
10294
+ }
10295
+ return routes;
10296
+ };
10106
10297
  // src/fileStore.ts
10107
10298
  import { mkdir as mkdir2, readFile, readdir, rename, rm, writeFile } from "fs/promises";
10108
10299
  import { join } from "path";
@@ -12196,7 +12387,7 @@ var createVoiceMemoryStore = () => {
12196
12387
  return { get, getOrCreate, list, remove, set };
12197
12388
  };
12198
12389
  // src/opsWebhook.ts
12199
- import { Elysia as Elysia12 } from "elysia";
12390
+ import { Elysia as Elysia14 } from "elysia";
12200
12391
  var toHex5 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
12201
12392
  var signVoiceOpsWebhookBody = async (input) => {
12202
12393
  const encoder = new TextEncoder;
@@ -12326,7 +12517,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
12326
12517
  };
12327
12518
  var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
12328
12519
  const path = options.path ?? "/api/voice-ops/webhook";
12329
- return new Elysia12().post(path, async ({ body, request, set }) => {
12520
+ return new Elysia14().post(path, async ({ body, request, set }) => {
12330
12521
  const bodyText = typeof body === "string" ? body : JSON.stringify(body);
12331
12522
  if (options.signingSecret) {
12332
12523
  const verification = await verifyVoiceOpsWebhookSignature({
@@ -14479,6 +14670,7 @@ export {
14479
14670
  summarizeVoiceSessionReplay,
14480
14671
  summarizeVoiceRoutingDecision,
14481
14672
  summarizeVoiceProviderHealth,
14673
+ summarizeVoiceProviderCapabilities,
14482
14674
  summarizeVoiceOpsTasks,
14483
14675
  summarizeVoiceOpsTaskQueue,
14484
14676
  summarizeVoiceOpsTaskAnalytics,
@@ -14491,6 +14683,7 @@ export {
14491
14683
  startVoiceOpsTask,
14492
14684
  shapeTelephonyAssistantText,
14493
14685
  selectVoiceTraceEventsForPrune,
14686
+ runVoiceToolContractSuite,
14494
14687
  runVoiceToolContract,
14495
14688
  runVoiceSessionEvals,
14496
14689
  runVoiceScenarioFixtureEvals,
@@ -14512,12 +14705,14 @@ export {
14512
14705
  reopenVoiceOpsTask,
14513
14706
  renderVoiceTraceMarkdown,
14514
14707
  renderVoiceTraceHTML,
14708
+ renderVoiceToolContractHTML,
14515
14709
  renderVoiceSessionsHTML,
14516
14710
  renderVoiceScenarioFixtureEvalHTML,
14517
14711
  renderVoiceScenarioEvalHTML,
14518
14712
  renderVoiceResilienceHTML,
14519
14713
  renderVoiceQualityHTML,
14520
14714
  renderVoiceProviderHealthHTML,
14715
+ renderVoiceProviderCapabilityHTML,
14521
14716
  renderVoiceOpsConsoleHTML,
14522
14717
  renderVoiceHandoffHealthHTML,
14523
14718
  renderVoiceEvalHTML,
@@ -14573,6 +14768,9 @@ export {
14573
14768
  createVoiceToolRuntimeContractDefaults,
14574
14769
  createVoiceToolRuntime,
14575
14770
  createVoiceToolIdempotencyKey,
14771
+ createVoiceToolContractRoutes,
14772
+ createVoiceToolContractJSONHandler,
14773
+ createVoiceToolContractHTMLHandler,
14576
14774
  createVoiceToolContract,
14577
14775
  createVoiceTaskUpdatedEvent,
14578
14776
  createVoiceTaskSLABreachedEvent,
@@ -14607,6 +14805,9 @@ export {
14607
14805
  createVoiceProviderHealthRoutes,
14608
14806
  createVoiceProviderHealthJSONHandler,
14609
14807
  createVoiceProviderHealthHTMLHandler,
14808
+ createVoiceProviderCapabilityRoutes,
14809
+ createVoiceProviderCapabilityJSONHandler,
14810
+ createVoiceProviderCapabilityHTMLHandler,
14610
14811
  createVoicePostgresTraceSinkDeliveryStore,
14611
14812
  createVoicePostgresTraceEventStore,
14612
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
+ }>;
@@ -1,3 +1,4 @@
1
+ import { Elysia } from 'elysia';
1
2
  import type { VoiceAgentTool } from './agent';
2
3
  import { type VoiceToolRuntimeOptions } from './toolRuntime';
3
4
  import type { VoiceSessionRecord, VoiceTurnRecord } from './types';
@@ -48,10 +49,32 @@ export type VoiceToolContractCaseReport = {
48
49
  export type VoiceToolContractReport = {
49
50
  cases: VoiceToolContractCaseReport[];
50
51
  contractId: string;
52
+ label?: string;
51
53
  issues: VoiceToolContractIssue[];
52
54
  pass: boolean;
53
55
  toolName: string;
54
56
  };
57
+ export type VoiceToolContractSuiteReport = {
58
+ checkedAt: number;
59
+ contracts: VoiceToolContractReport[];
60
+ failed: number;
61
+ passed: number;
62
+ status: 'fail' | 'pass';
63
+ total: number;
64
+ };
65
+ export type VoiceToolContractHandlerOptions = {
66
+ contracts: VoiceToolContractDefinition[];
67
+ };
68
+ export type VoiceToolContractHTMLHandlerOptions = VoiceToolContractHandlerOptions & {
69
+ headers?: HeadersInit;
70
+ render?: (report: VoiceToolContractSuiteReport) => string | Promise<string>;
71
+ title?: string;
72
+ };
73
+ export type VoiceToolContractRoutesOptions = VoiceToolContractHTMLHandlerOptions & {
74
+ htmlPath?: false | string;
75
+ name?: string;
76
+ path?: string;
77
+ };
55
78
  export declare const runVoiceToolContract: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TArgs = Record<string, unknown>, TToolResult = unknown, TRouteResult = unknown>(definition: VoiceToolContractDefinition<TContext, TSession, TArgs, TToolResult, TRouteResult>) => Promise<VoiceToolContractReport>;
56
79
  export declare const createVoiceToolContract: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TArgs = Record<string, unknown>, TToolResult = unknown, TRouteResult = unknown>(definition: VoiceToolContractDefinition<TContext, TSession, TArgs, TToolResult, TRouteResult>) => {
57
80
  assert: () => Promise<VoiceToolContractReport>;
@@ -59,3 +82,49 @@ export declare const createVoiceToolContract: <TContext = unknown, TSession exte
59
82
  run: () => Promise<VoiceToolContractReport>;
60
83
  };
61
84
  export declare const createVoiceToolRuntimeContractDefaults: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TRouteResult = unknown>() => VoiceToolRuntimeOptions<TContext, TSession, TRouteResult>;
85
+ export declare const runVoiceToolContractSuite: (options: VoiceToolContractHandlerOptions) => Promise<VoiceToolContractSuiteReport>;
86
+ export declare const renderVoiceToolContractHTML: (report: VoiceToolContractSuiteReport, options?: {
87
+ title?: string;
88
+ }) => string;
89
+ export declare const createVoiceToolContractJSONHandler: (options: VoiceToolContractHandlerOptions) => () => Promise<VoiceToolContractSuiteReport>;
90
+ export declare const createVoiceToolContractHTMLHandler: (options: VoiceToolContractHTMLHandlerOptions) => () => Promise<Response>;
91
+ export declare const createVoiceToolContractRoutes: (options: VoiceToolContractRoutesOptions) => Elysia<"", {
92
+ decorator: {};
93
+ store: {};
94
+ derive: {};
95
+ resolve: {};
96
+ }, {
97
+ typebox: {};
98
+ error: {};
99
+ }, {
100
+ schema: {};
101
+ standaloneSchema: {};
102
+ macro: {};
103
+ macroFn: {};
104
+ parser: {};
105
+ response: {};
106
+ }, {
107
+ [x: string]: {
108
+ get: {
109
+ body: unknown;
110
+ params: {};
111
+ query: unknown;
112
+ headers: unknown;
113
+ response: {
114
+ 200: VoiceToolContractSuiteReport;
115
+ };
116
+ };
117
+ };
118
+ }, {
119
+ derive: {};
120
+ resolve: {};
121
+ schema: {};
122
+ standaloneSchema: {};
123
+ response: {};
124
+ }, {
125
+ derive: {};
126
+ resolve: {};
127
+ schema: {};
128
+ standaloneSchema: {};
129
+ response: {};
130
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.63",
3
+ "version": "0.0.22-beta.65",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",