@absolutejs/voice 0.0.22-beta.484 → 0.0.22-beta.485

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
@@ -80,6 +80,8 @@ export type { CreateVoiceCostAccountantOptions, VoiceCostAccountant, VoiceCostBr
80
80
  export { describeVoiceAssistantMode, resolveVoiceAssistantMode, } from "./assistantMode";
81
81
  export type { VoiceAssistantMode, VoiceAssistantModality, VoiceAssistantModeDescriptor, VoiceSemanticVADConfig, } from "./assistantMode";
82
82
  export { createPunctuationSemanticTurnDetector, createRegexSemanticTurnDetector, } from "./semanticTurn";
83
+ export { VOICE_WEBHOOK_SIGNATURE_HEADER, VOICE_WEBHOOK_TIMESTAMP_HEADER, extractVoiceWebhookSignatureFromHeaders, signVoiceWebhookBody, verifyVoiceWebhookSignature, } from "./webhookVerification";
84
+ export type { VoiceWebhookVerificationInput, VoiceWebhookVerificationReason, VoiceWebhookVerificationResult, } from "./webhookVerification";
83
85
  export type { CreatePunctuationSemanticTurnDetectorOptions, CreateRegexSemanticTurnDetectorOptions, VoiceSemanticTurnDetector, VoiceSemanticTurnInput, VoiceSemanticTurnVerdict, } from "./semanticTurn";
84
86
  export { createMonologueAMDDetector } from "./amdDetector";
85
87
  export type { MonologueAMDDetectorOptions, VoiceAMDDetector, VoiceAMDDetectorInput, VoiceAMDVerdict, } from "./amdDetector";
package/dist/index.js CHANGED
@@ -35404,6 +35404,77 @@ var createRegexSemanticTurnDetector = (options) => {
35404
35404
  }
35405
35405
  };
35406
35406
  };
35407
+ // src/webhookVerification.ts
35408
+ var VOICE_WEBHOOK_SIGNATURE_HEADER = "x-absolutejs-signature";
35409
+ var VOICE_WEBHOOK_TIMESTAMP_HEADER = "x-absolutejs-timestamp";
35410
+ var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
35411
+ var timingSafeEqual3 = (left, right) => {
35412
+ if (left.length !== right.length) {
35413
+ return false;
35414
+ }
35415
+ let result = 0;
35416
+ for (let index = 0;index < left.length; index += 1) {
35417
+ result |= left.charCodeAt(index) ^ right.charCodeAt(index);
35418
+ }
35419
+ return result === 0;
35420
+ };
35421
+ var computeSignature = async (input) => {
35422
+ const encoder2 = new TextEncoder;
35423
+ const key = await crypto.subtle.importKey("raw", encoder2.encode(input.secret), { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
35424
+ const payload = encoder2.encode(`${input.timestamp}.${input.body}`);
35425
+ const signature = await crypto.subtle.sign("HMAC", key, payload);
35426
+ return `sha256=${toHex6(new Uint8Array(signature))}`;
35427
+ };
35428
+ var signVoiceWebhookBody = async (input) => computeSignature(input);
35429
+ var verifyVoiceWebhookSignature = async (input) => {
35430
+ if (!input.secret) {
35431
+ return { ok: false, reason: "missing-secret" };
35432
+ }
35433
+ if (!input.signature) {
35434
+ return { ok: false, reason: "missing-signature" };
35435
+ }
35436
+ if (!input.signature.startsWith("sha256=")) {
35437
+ return { ok: false, reason: "unsupported-algorithm" };
35438
+ }
35439
+ if (!input.timestamp) {
35440
+ return { ok: false, reason: "missing-timestamp" };
35441
+ }
35442
+ const timestampMs = Number(input.timestamp);
35443
+ const toleranceMs = Math.max(0, input.toleranceMs ?? 5 * 60 * 1000);
35444
+ if (!Number.isFinite(timestampMs) || toleranceMs > 0 && Math.abs((input.now ?? Date.now()) - timestampMs) > toleranceMs) {
35445
+ return { ok: false, reason: "stale-timestamp" };
35446
+ }
35447
+ const expected = await computeSignature({
35448
+ body: input.body,
35449
+ secret: input.secret,
35450
+ timestamp: input.timestamp
35451
+ });
35452
+ if (!timingSafeEqual3(expected, input.signature)) {
35453
+ return { ok: false, reason: "signature-mismatch" };
35454
+ }
35455
+ return { ok: true };
35456
+ };
35457
+ var extractVoiceWebhookSignatureFromHeaders = (headers) => {
35458
+ const get = (name) => {
35459
+ if (headers instanceof Headers) {
35460
+ return headers.get(name);
35461
+ }
35462
+ const lowerTarget = name.toLowerCase();
35463
+ for (const [key, value] of Object.entries(headers)) {
35464
+ if (key.toLowerCase() === lowerTarget) {
35465
+ if (Array.isArray(value)) {
35466
+ return value[0] ?? null;
35467
+ }
35468
+ return value ?? null;
35469
+ }
35470
+ }
35471
+ return null;
35472
+ };
35473
+ return {
35474
+ signature: get(VOICE_WEBHOOK_SIGNATURE_HEADER),
35475
+ timestamp: get(VOICE_WEBHOOK_TIMESTAMP_HEADER)
35476
+ };
35477
+ };
35407
35478
  // src/amdDetector.ts
35408
35479
  var createMonologueAMDDetector = (options = {}) => {
35409
35480
  const minMonologueMs = options.minMonologueMs ?? 8000;
@@ -41951,7 +42022,7 @@ var createVoiceMemoryStore = () => {
41951
42022
  };
41952
42023
  // src/opsWebhook.ts
41953
42024
  import { Elysia as Elysia66 } from "elysia";
41954
- var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
42025
+ var toHex7 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
41955
42026
  var signVoiceOpsWebhookBody = async (input) => {
41956
42027
  const encoder2 = new TextEncoder;
41957
42028
  const key = await crypto.subtle.importKey("raw", encoder2.encode(input.secret), {
@@ -41959,9 +42030,9 @@ var signVoiceOpsWebhookBody = async (input) => {
41959
42030
  name: "HMAC"
41960
42031
  }, false, ["sign"]);
41961
42032
  const signature = await crypto.subtle.sign("HMAC", key, encoder2.encode(`${input.timestamp}.${input.body}`));
41962
- return `sha256=${toHex6(new Uint8Array(signature))}`;
42033
+ return `sha256=${toHex7(new Uint8Array(signature))}`;
41963
42034
  };
41964
- var timingSafeEqual3 = (left, right) => {
42035
+ var timingSafeEqual4 = (left, right) => {
41965
42036
  const encoder2 = new TextEncoder;
41966
42037
  const leftBytes = encoder2.encode(left);
41967
42038
  const rightBytes = encoder2.encode(right);
@@ -42068,7 +42139,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
42068
42139
  secret: input.secret,
42069
42140
  timestamp: input.timestamp
42070
42141
  });
42071
- if (!timingSafeEqual3(expected, input.signature)) {
42142
+ if (!timingSafeEqual4(expected, input.signature)) {
42072
42143
  return {
42073
42144
  ok: false,
42074
42145
  reason: "invalid-signature"
@@ -45977,6 +46048,7 @@ export {
45977
46048
  voiceGuardrailPolicyPresets,
45978
46049
  voiceComplianceRedactionDefaults,
45979
46050
  voice,
46051
+ verifyVoiceWebhookSignature,
45980
46052
  verifyVoiceTwilioWebhookSignature,
45981
46053
  verifyVoiceTelnyxWebhookSignature,
45982
46054
  verifyVoicePlivoWebhookSignature,
@@ -46020,6 +46092,7 @@ export {
46020
46092
  summarizeVoiceAssistantRuns,
46021
46093
  summarizeVoiceAssistantHealth,
46022
46094
  startVoiceOpsTask,
46095
+ signVoiceWebhookBody,
46023
46096
  signVoiceTwilioWebhook,
46024
46097
  signVoicePlivoWebhook,
46025
46098
  shapeTelephonyAssistantText,
@@ -46219,6 +46292,7 @@ export {
46219
46292
  filterVoiceAuditEvents,
46220
46293
  fetchVoiceProofTarget,
46221
46294
  failVoiceOpsTask,
46295
+ extractVoiceWebhookSignatureFromHeaders,
46222
46296
  extractVoiceMediaPipelineIssueEntries,
46223
46297
  exportVoiceTrace,
46224
46298
  exportVoiceAuditTrail,
@@ -46765,6 +46839,8 @@ export {
46765
46839
  appendVoiceProviderRouterTraceEvent,
46766
46840
  appendVoiceIOProviderRouterTraceEvent,
46767
46841
  acknowledgeVoiceMonitorIssue,
46842
+ VOICE_WEBHOOK_TIMESTAMP_HEADER,
46843
+ VOICE_WEBHOOK_SIGNATURE_HEADER,
46768
46844
  VOICE_LIVE_OPS_ACTIONS,
46769
46845
  TURN_PROFILE_DEFAULTS,
46770
46846
  DEFAULT_VOICE_REDACTION_PATTERNS,
@@ -0,0 +1,27 @@
1
+ export type VoiceWebhookVerificationReason = "missing-secret" | "missing-signature" | "missing-timestamp" | "signature-mismatch" | "stale-timestamp" | "unsupported-algorithm";
2
+ export type VoiceWebhookVerificationResult = {
3
+ ok: true;
4
+ } | {
5
+ ok: false;
6
+ reason: VoiceWebhookVerificationReason;
7
+ };
8
+ export type VoiceWebhookVerificationInput = {
9
+ body: string;
10
+ now?: number;
11
+ secret?: string;
12
+ signature?: string | null;
13
+ timestamp?: string | null;
14
+ toleranceMs?: number;
15
+ };
16
+ export declare const VOICE_WEBHOOK_SIGNATURE_HEADER = "x-absolutejs-signature";
17
+ export declare const VOICE_WEBHOOK_TIMESTAMP_HEADER = "x-absolutejs-timestamp";
18
+ export declare const signVoiceWebhookBody: (input: {
19
+ body: string;
20
+ secret: string;
21
+ timestamp: string;
22
+ }) => Promise<string>;
23
+ export declare const verifyVoiceWebhookSignature: (input: VoiceWebhookVerificationInput) => Promise<VoiceWebhookVerificationResult>;
24
+ export declare const extractVoiceWebhookSignatureFromHeaders: (headers: Headers | Record<string, string | string[] | undefined>) => {
25
+ signature: string | null;
26
+ timestamp: string | null;
27
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.484",
3
+ "version": "0.0.22-beta.485",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",