@absolutejs/voice 0.0.22-beta.473 → 0.0.22-beta.474

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 CHANGED
@@ -8,6 +8,107 @@ Use it when you want Vapi/Retell/Bland-style voice-agent capability, but you wan
8
8
 
9
9
  ## What's new
10
10
 
11
+ ### 0.0.22-beta.474 · Phase 5 runtime hookup — auto-wire monitor sockets to live sessions
12
+
13
+ The Phase 5 monitor primitive is now first-class in the voice runtime. Pass a `monitor` binding to `voice({...})` and every session opened against the voice plugin auto-registers in the monitor registry, outbound TTS audio auto-fans-out to all listeners, and the close/superseded/session-switch paths deregister automatically. Supervisors connecting to the listen route get a live audio stream without any manual `record.emit()` calls.
14
+
15
+ ```ts
16
+ import { Elysia } from "elysia";
17
+ import {
18
+ createVoiceInMemoryMonitorRegistry,
19
+ createVoiceLiveMonitorRoutes,
20
+ createVoiceMonitorRuntimeBinding,
21
+ voice,
22
+ } from "@absolutejs/voice";
23
+
24
+ const monitorRegistry = createVoiceInMemoryMonitorRegistry();
25
+
26
+ const app = new Elysia()
27
+ .use(
28
+ voice({
29
+ path: "/voice/realtime",
30
+ stt: deepgram({ apiKey: process.env.DEEPGRAM_API_KEY! }),
31
+ tts: elevenlabs({ apiKey: process.env.ELEVENLABS_API_KEY! }),
32
+ onTurn: async (session, turn, api) => {
33
+ // your business logic
34
+ },
35
+ session: sessionStore,
36
+ monitor: createVoiceMonitorRuntimeBinding(monitorRegistry, {
37
+ audioFormat: { channels: 1, container: "raw", encoding: "pcm_s16le", sampleRateHz: 24_000 },
38
+ }),
39
+ }),
40
+ )
41
+ .use(
42
+ createVoiceLiveMonitorRoutes({
43
+ registry: monitorRegistry,
44
+ authenticate: async ({ request }) => await verifySupervisorJWT(request),
45
+ }),
46
+ );
47
+ ```
48
+
49
+ Surface additions:
50
+ - **`createVoiceMonitorRuntimeBinding(registry, { audioFormat?, defaultSource? })`** — returns a `VoiceMonitorRuntimeBinding` you pass to the voice plugin's new `monitor` option. Internally, each session open calls `registerSession({ handle, sessionId })`, audio fans out via `emitAudio()` on every binary `socket.send`, and the close/superseded/session-switch paths call `deregister(reason)`.
51
+ - **`VoicePluginConfig.monitor?: VoiceMonitorRuntimeBinding`** — new optional field on the main voice plugin config.
52
+ - **`VoiceMonitorMutableRegistry.deregister(sessionId, reason?)`** — explicit deregister path so the runtime binding can tear down stale records on re-register without throwing. The `register()`-returned deregister fn now also accepts an optional reason.
53
+ - The runtime binding is **opt-in** — if you don't pass `monitor`, voice behaves exactly as before. No overhead on the audio-send hot path beyond a single `Map.get`.
54
+
55
+ 4 new tests cover the runtime binding's register/emit/deregister cycle, no-op-after-deregister, re-registration tear-down, and the audioFormat + defaultSource option threading. Full voice suite now 963 pass / 1 pre-existing fail.
56
+
57
+ ### 0.0.22-beta.473 · Phase 5 — live listen + control monitor sockets (Vapi `monitorPlan` parity)
58
+
59
+ Two new WebSocket routes per session that mirror Vapi's `monitorPlan.listenUrl` + `monitorPlan.controlUrl`. Supervisors can subscribe to a live call's outbound audio and send control commands (transfer, hangup, escalate, voicemail, no-answer, plus caller-defined mute/say/inject) without touching the call itself.
60
+
61
+ ```ts
62
+ import { Elysia } from "elysia";
63
+ import {
64
+ buildVoiceMonitorPlan,
65
+ createVoiceInMemoryMonitorRegistry,
66
+ createVoiceLiveMonitorRoutes,
67
+ createVoiceMonitorSession,
68
+ } from "@absolutejs/voice";
69
+
70
+ const registry = createVoiceInMemoryMonitorRegistry();
71
+
72
+ // In your runtime: when a session opens, register it so supervisors can listen.
73
+ const record = createVoiceMonitorSession({ handle, sessionId: handle.id });
74
+ const deregister = registry.register(record);
75
+ // When audio leaves the assistant, fan it out:
76
+ // record.emit({ at: Date.now(), chunk, format, source: 'assistant' });
77
+ // On call end:
78
+ // deregister();
79
+
80
+ const app = new Elysia()
81
+ .use(
82
+ createVoiceLiveMonitorRoutes({
83
+ authenticate: async ({ sessionId, route, request }) =>
84
+ await verifySupervisorJWT(request),
85
+ controlHandlers: {
86
+ say: async ({ message, session }) => {
87
+ await yourTtsRuntime.sayInSession(session.sessionId, message.text);
88
+ return { detail: `Said: ${message.text}`, ok: true, type: "say" };
89
+ },
90
+ },
91
+ htmlPath: "/voice/monitor",
92
+ registry,
93
+ }),
94
+ );
95
+
96
+ const plan = buildVoiceMonitorPlan({
97
+ baseUrl: "wss://api.example.com",
98
+ sessionId: handle.id,
99
+ });
100
+ // plan.listenUrl → wss://api.example.com/api/voice/monitor/<sessionId>/listen
101
+ // plan.controlUrl → wss://api.example.com/api/voice/monitor/<sessionId>/control
102
+ ```
103
+
104
+ Surface summary:
105
+ - **`createVoiceInMemoryMonitorRegistry()`** — `{ register, get, list, emit, emitClose }`. Voice's session runtime (or any caller) wires `register()` on open + the returned deregister on close; `emit()` fans out outbound audio frames; `emitClose()` notifies listeners that the call ended.
106
+ - **`createVoiceLiveMonitorRoutes(options)`** — Elysia plugin that mounts two `.ws()` routes per session: `:sessionId/listen` (read-only outbound audio as binary frames) and `:sessionId/control` (JSON control messages). Default handlers map `transfer`/`hangup`/`escalate`/`voicemail`/`no-answer` onto `VoiceSessionHandle` verbs; `mute`/`say`/`inject` require caller-supplied handlers via `controlHandlers`.
107
+ - **`buildVoiceMonitorPlan(input)`** — Vapi-shaped helper returning `{ listenUrl, controlUrl }` for inclusion in your call-create API response.
108
+ - **Auth hook** — `authenticate?: ({ sessionId, route, request }) => Promise<boolean>` runs at the start of both routes; rejected connections get a 4401 close.
109
+
110
+ This is a **primitive**: wiring voice's existing `activeSessions` runtime into the registry (so audio actually flows from a real call without manual `record.emit()` calls) is the next step and intentionally left out to keep this change additive. Until then, registries built by hand (e.g. from a custom telephony adapter where you control the outbound audio buffer) work out of the box.
111
+
11
112
  ### 0.0.22-beta.472 · Phase 6 — multilingual STT proof gate
12
113
 
13
114
  `runVoiceMultilingualProof(...)` turns the `voice-fixtures-multilingual` corpus (FLEURS + BSC Catalan-Spanish code-switch + CoSHE Hindi-English code-switch) into a gateable readiness/proof artifact. Buyers evaluating Vapi-replacement can now run any combination of STT adapters against the multilingual corpus and assert per-language WER / pass-rate / term-recall budgets in CI.
package/dist/index.d.ts CHANGED
@@ -225,6 +225,6 @@ export { buildVoiceProofPackInput, buildVoiceProofPack, buildVoiceProofPackFromO
225
225
  export type { VoiceProofPack, VoiceProofPackBuildContext, VoiceProofPackBuildContextOptions, VoiceProofPackBuildTiming, VoiceProofPackEvidence, VoiceProofPackInput, VoiceProofPackInputBuilderLoaderInput, VoiceProofPackInputBuilderOperationsLoaderInput, VoiceProofPackInputBuilderOptions, VoiceProofPackInputBuilderSupportBundle, VoiceProofPackRefreshState, VoiceProofPackRefreshStatus, VoiceProofPackRoutesOptions, VoiceProofPackSection, VoiceProofPackSourceValue, VoiceProofPackStatus, VoiceProofPackStaleWhileRefreshSource, VoiceProofPackStaleWhileRefreshSourceOptions, VoiceProofPackWriteResult, VoiceProofRefreshSnapshot, VoiceProofRefreshSnapshotOptions, } from "./proofPack";
226
226
  export { buildVoiceMultilingualProofReadinessCheck, renderVoiceMultilingualProofMarkdown, runVoiceMultilingualProof, } from "./multilingualProof";
227
227
  export type { VoiceMultilingualLanguageCode, VoiceMultilingualProofAdapterEntry, VoiceMultilingualProofAdapterReport, VoiceMultilingualProofDefaultThresholds, VoiceMultilingualProofLanguageMetrics, VoiceMultilingualProofLanguageReport, VoiceMultilingualProofLanguageThresholds, VoiceMultilingualProofOptions, VoiceMultilingualProofReadinessCheck, VoiceMultilingualProofReadinessOptions, VoiceMultilingualProofReport, } from "./multilingualProof";
228
- export { buildVoiceMonitorPlan, createVoiceInMemoryMonitorRegistry, createVoiceLiveMonitorRoutes, createVoiceMonitorSession, } from "./monitor";
229
- export type { VoiceMonitorAudioEvent, VoiceMonitorAudioSource, VoiceMonitorAuthenticate, VoiceMonitorAuthenticateInput, VoiceMonitorControlAck, VoiceMonitorControlHandler, VoiceMonitorControlHandlerInput, VoiceMonitorControlMessage, VoiceMonitorMutableRegistry, VoiceMonitorPlan, VoiceMonitorPlanInput, VoiceMonitorRegistry, VoiceMonitorRegistryRegisterInput, VoiceLiveMonitorRoutesOptions, VoiceMonitorSessionRecord, } from "./monitor";
228
+ export { buildVoiceMonitorPlan, createVoiceInMemoryMonitorRegistry, createVoiceLiveMonitorRoutes, createVoiceMonitorRuntimeBinding, createVoiceMonitorSession, } from "./monitor";
229
+ export type { VoiceMonitorAudioEvent, VoiceMonitorAudioSource, VoiceMonitorAuthenticate, VoiceMonitorAuthenticateInput, VoiceMonitorControlAck, VoiceMonitorControlHandler, VoiceMonitorControlHandlerInput, VoiceMonitorControlMessage, VoiceMonitorMutableRegistry, VoiceMonitorPlan, VoiceMonitorPlanInput, VoiceMonitorRegistry, VoiceMonitorRegistryRegisterInput, VoiceLiveMonitorRoutesOptions, VoiceMonitorRuntimeBindingOptions, VoiceMonitorSessionRecord, } from "./monitor";
230
230
  export * from "./types";
package/dist/index.js CHANGED
@@ -6265,6 +6265,7 @@ var voice = (config) => {
6265
6265
  if (!config.stt && !config.realtime) {
6266
6266
  throw new Error("voice requires either an stt or realtime adapter.");
6267
6267
  }
6268
+ const monitorBindings = new Map;
6268
6269
  const runtime = {
6269
6270
  activeSessions: new Map,
6270
6271
  logger: resolveLogger(config.logger),
@@ -6272,6 +6273,57 @@ var voice = (config) => {
6272
6273
  profileSwitchGuardedSessions: new Set,
6273
6274
  socketSessions: new WeakMap
6274
6275
  };
6276
+ const monitor = config.monitor;
6277
+ const registerMonitorSession = (sessionId, handle) => {
6278
+ if (!monitor)
6279
+ return;
6280
+ const existing = monitorBindings.get(sessionId);
6281
+ if (existing) {
6282
+ try {
6283
+ existing.deregister("superseded");
6284
+ } catch {}
6285
+ monitorBindings.delete(sessionId);
6286
+ }
6287
+ try {
6288
+ const binding = monitor.registerSession({ handle, sessionId });
6289
+ monitorBindings.set(sessionId, binding);
6290
+ } catch (error) {
6291
+ runtime.logger.warn?.(`[voice] failed to register session "${sessionId}" with monitor runtime: ${error instanceof Error ? error.message : String(error)}`);
6292
+ }
6293
+ };
6294
+ const deregisterMonitorSession = (sessionId, reason) => {
6295
+ const binding = monitorBindings.get(sessionId);
6296
+ if (!binding)
6297
+ return;
6298
+ monitorBindings.delete(sessionId);
6299
+ try {
6300
+ binding.deregister(reason);
6301
+ } catch (error) {
6302
+ runtime.logger.warn?.(`[voice] failed to deregister monitor binding for session "${sessionId}": ${error instanceof Error ? error.message : String(error)}`);
6303
+ }
6304
+ };
6305
+ const buildSocketAdapter = (ws, sessionId) => {
6306
+ if (!monitor)
6307
+ return createSocketAdapter(ws);
6308
+ return {
6309
+ close: async (code, reason) => {
6310
+ ws.close(code, reason);
6311
+ },
6312
+ send: async (data) => {
6313
+ if (typeof data !== "string") {
6314
+ const binding = monitorBindings.get(sessionId);
6315
+ if (binding) {
6316
+ try {
6317
+ binding.emitAudio(data);
6318
+ } catch (error) {
6319
+ runtime.logger.warn?.(`[voice] monitor emitAudio failed for session "${sessionId}": ${error instanceof Error ? error.message : String(error)}`);
6320
+ }
6321
+ }
6322
+ }
6323
+ ws.send(data);
6324
+ }
6325
+ };
6326
+ };
6275
6327
  const onTurn = normalizeOnTurn(config.onTurn);
6276
6328
  const sessionOptions = resolveSessionOptions(config);
6277
6329
  const htmxOptions = config.htmx && typeof config.htmx === "object" ? config.htmx : undefined;
@@ -6394,6 +6446,7 @@ var voice = (config) => {
6394
6446
  }
6395
6447
  const session = runtime.activeSessions.get(socketState.sessionId);
6396
6448
  runtime.activeSessions.delete(socketState.sessionId);
6449
+ deregisterMonitorSession(socketState.sessionId, reason ?? `ws-close-${String(code)}`);
6397
6450
  if (session) {
6398
6451
  await session.disconnect({
6399
6452
  code,
@@ -6417,6 +6470,7 @@ var voice = (config) => {
6417
6470
  if (message.type === "close" && current) {
6418
6471
  await current.close(message.reason);
6419
6472
  runtime.activeSessions.delete(sessionState.sessionId);
6473
+ deregisterMonitorSession(sessionState.sessionId, message.reason);
6420
6474
  }
6421
6475
  if (message.type === "call_control" && current) {
6422
6476
  if (message.action === "transfer") {
@@ -6459,6 +6513,7 @@ var voice = (config) => {
6459
6513
  if (currentSession) {
6460
6514
  await currentSession.close("session-switch");
6461
6515
  runtime.activeSessions.delete(sessionState.sessionId);
6516
+ deregisterMonitorSession(sessionState.sessionId, "session-switch");
6462
6517
  }
6463
6518
  sessionState.sessionId = message.sessionId;
6464
6519
  runtime.socketSessions.set(ws, {
@@ -6482,8 +6537,10 @@ var voice = (config) => {
6482
6537
  }
6483
6538
  const session = current ?? await createManagedSession(ws, sessionState.sessionId, sessionState.scenarioId ?? undefined);
6484
6539
  if (!current) {
6485
- runtime.activeSessions.set(sessionState.sessionId, session);
6486
- await session.connect(createSocketAdapter(ws));
6540
+ const typedSession = session;
6541
+ runtime.activeSessions.set(sessionState.sessionId, typedSession);
6542
+ registerMonitorSession(sessionState.sessionId, typedSession);
6543
+ await session.connect(buildSocketAdapter(ws, sessionState.sessionId));
6487
6544
  }
6488
6545
  await session.receiveAudio(audio);
6489
6546
  },
@@ -6493,10 +6550,13 @@ var voice = (config) => {
6493
6550
  if (existing) {
6494
6551
  await existing.close("superseded");
6495
6552
  runtime.activeSessions.delete(sessionState.sessionId);
6553
+ deregisterMonitorSession(sessionState.sessionId, "superseded");
6496
6554
  }
6497
6555
  const session = await createManagedSession(ws, sessionState.sessionId, sessionState.scenarioId ?? undefined);
6498
- runtime.activeSessions.set(sessionState.sessionId, session);
6499
- await session.connect(createSocketAdapter(ws));
6556
+ const typedSession = session;
6557
+ runtime.activeSessions.set(sessionState.sessionId, typedSession);
6558
+ registerMonitorSession(sessionState.sessionId, typedSession);
6559
+ await session.connect(buildSocketAdapter(ws, sessionState.sessionId));
6500
6560
  }
6501
6561
  }).use(htmxRoutes());
6502
6562
  };
@@ -44501,7 +44561,15 @@ var createVoiceMonitorSession = (input) => {
44501
44561
  };
44502
44562
  var createVoiceInMemoryMonitorRegistry = () => {
44503
44563
  const records = new Map;
44564
+ const deregister = (sessionId, reason) => {
44565
+ const existing = records.get(sessionId);
44566
+ if (!existing)
44567
+ return;
44568
+ records.delete(sessionId);
44569
+ existing.emitClose(reason ?? "deregistered");
44570
+ };
44504
44571
  return {
44572
+ deregister,
44505
44573
  emit: (sessionId, event) => {
44506
44574
  records.get(sessionId)?.emit(event);
44507
44575
  },
@@ -44525,10 +44593,7 @@ var createVoiceInMemoryMonitorRegistry = () => {
44525
44593
  record.onClose((reason) => wrapped.emitClose(reason));
44526
44594
  }
44527
44595
  records.set(record.sessionId, wrapped);
44528
- return () => {
44529
- records.delete(record.sessionId);
44530
- wrapped.emitClose("deregistered");
44531
- };
44596
+ return (reason) => deregister(record.sessionId, reason);
44532
44597
  }
44533
44598
  };
44534
44599
  };
@@ -44665,6 +44730,52 @@ var buildVoiceMonitorPlan = (input) => {
44665
44730
  listenUrl: `${baseUrl}${substituteSessionId(listenTemplate, input.sessionId)}`
44666
44731
  };
44667
44732
  };
44733
+ var DEFAULT_RUNTIME_AUDIO_FORMAT = {
44734
+ channels: 1,
44735
+ container: "raw",
44736
+ encoding: "pcm_s16le",
44737
+ sampleRateHz: 16000
44738
+ };
44739
+ var toUint8 = (chunk) => chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
44740
+ var createVoiceMonitorRuntimeBinding = (registry, options = {}) => {
44741
+ const audioFormat = options.audioFormat ?? DEFAULT_RUNTIME_AUDIO_FORMAT;
44742
+ const defaultSource = options.defaultSource ?? "assistant";
44743
+ return {
44744
+ registerSession: (input) => {
44745
+ registry.deregister(input.sessionId, "superseded");
44746
+ const record = createVoiceMonitorSession({
44747
+ handle: input.handle,
44748
+ metadata: input.metadata,
44749
+ sessionId: input.sessionId
44750
+ });
44751
+ const deregisterFromRegistry = registry.register(record);
44752
+ let closed = false;
44753
+ return {
44754
+ deregister: (reason) => {
44755
+ if (closed)
44756
+ return;
44757
+ closed = true;
44758
+ try {
44759
+ deregisterFromRegistry(reason);
44760
+ } catch {}
44761
+ },
44762
+ emitAudio: (chunk, opts) => {
44763
+ if (closed)
44764
+ return;
44765
+ const bytes = toUint8(chunk);
44766
+ if (bytes.byteLength === 0)
44767
+ return;
44768
+ record.emit({
44769
+ at: Date.now(),
44770
+ chunk: bytes,
44771
+ format: audioFormat,
44772
+ source: opts?.source ?? defaultSource
44773
+ });
44774
+ }
44775
+ };
44776
+ }
44777
+ };
44778
+ };
44668
44779
  var resolveSessionId3 = (ws) => {
44669
44780
  const params = ws.data?.params;
44670
44781
  if (!params)
@@ -45358,6 +45469,7 @@ export {
45358
45469
  createVoiceObservabilityExportReplayRoutes,
45359
45470
  createVoiceMonitorWebhookNotifier,
45360
45471
  createVoiceMonitorSession,
45472
+ createVoiceMonitorRuntimeBinding,
45361
45473
  createVoiceMonitorRunnerRoutes,
45362
45474
  createVoiceMonitorRunner,
45363
45475
  createVoiceMonitorRoutes,
package/dist/monitor.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Elysia } from "elysia";
2
- import type { AudioFormat, VoiceSessionHandle, VoiceSessionRecord } from "./types";
2
+ import type { AudioFormat, VoiceMonitorRuntimeBinding, VoiceSessionHandle, VoiceSessionRecord } from "./types";
3
3
  export type VoiceMonitorAudioSource = "assistant" | "caller" | (string & {});
4
4
  export type VoiceMonitorAudioEvent = {
5
5
  at: number;
@@ -23,7 +23,12 @@ export type VoiceMonitorRegistry = {
23
23
  export type VoiceMonitorMutableRegistry = VoiceMonitorRegistry & {
24
24
  emit: (sessionId: string, event: VoiceMonitorAudioEvent) => void;
25
25
  emitClose: (sessionId: string, reason?: string) => void;
26
- register: (record: VoiceMonitorSessionRecord) => () => void;
26
+ /**
27
+ * Deregister a session by id. No-op if the id isn't registered. The
28
+ * `reason` flows into the close fan-out exactly once.
29
+ */
30
+ deregister: (sessionId: string, reason?: string) => void;
31
+ register: (record: VoiceMonitorSessionRecord) => (reason?: string) => void;
27
32
  };
28
33
  export type VoiceMonitorRegistryRegisterInput = {
29
34
  handle: VoiceSessionHandle<unknown, VoiceSessionRecord, unknown>;
@@ -108,6 +113,11 @@ export type VoiceMonitorPlan = {
108
113
  listenUrl: string;
109
114
  };
110
115
  export declare const buildVoiceMonitorPlan: (input: VoiceMonitorPlanInput) => VoiceMonitorPlan;
116
+ export type VoiceMonitorRuntimeBindingOptions = {
117
+ audioFormat?: AudioFormat;
118
+ defaultSource?: VoiceMonitorAudioSource;
119
+ };
120
+ export declare const createVoiceMonitorRuntimeBinding: (registry: VoiceMonitorMutableRegistry, options?: VoiceMonitorRuntimeBindingOptions) => VoiceMonitorRuntimeBinding;
111
121
  export declare const createVoiceLiveMonitorRoutes: (options: VoiceLiveMonitorRoutesOptions) => Elysia<"", {
112
122
  decorator: {};
113
123
  store: {};
package/dist/types.d.ts CHANGED
@@ -411,6 +411,20 @@ export type VoiceSocket = {
411
411
  send: (data: string | Uint8Array | ArrayBuffer) => void | Promise<void>;
412
412
  close: (code?: number, reason?: string) => void | Promise<void>;
413
413
  };
414
+ export type VoiceMonitorRuntimeSessionBinding = {
415
+ deregister: (reason?: string) => void;
416
+ emitAudio: (chunk: Uint8Array | ArrayBuffer, options?: {
417
+ source?: "assistant" | "caller" | (string & {});
418
+ }) => void;
419
+ };
420
+ export type VoiceMonitorRuntimeRegisterInput = {
421
+ handle: VoiceSessionHandle<unknown, VoiceSessionRecord, unknown>;
422
+ metadata?: Record<string, unknown>;
423
+ sessionId: string;
424
+ };
425
+ export type VoiceMonitorRuntimeBinding = {
426
+ registerSession: (input: VoiceMonitorRuntimeRegisterInput) => VoiceMonitorRuntimeSessionBinding;
427
+ };
414
428
  export type VoiceSessionHandle<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
415
429
  id: string;
416
430
  connect: (socket: VoiceSocket) => Promise<void>;
@@ -679,6 +693,7 @@ export type VoicePluginConfig<TContext = unknown, TSession extends VoiceSessionR
679
693
  handoff?: VoiceHandoffConfig<TContext, TSession, TResult>;
680
694
  ops?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
681
695
  liveOps?: VoiceLiveOpsRuntimeConfig;
696
+ monitor?: VoiceMonitorRuntimeBinding;
682
697
  profileSwitchGuard?: VoicePluginProfileSwitchGuardConfig<TContext, TSession, TResult>;
683
698
  trace?: VoiceTraceEventStore;
684
699
  } & VoiceRouteConfig<TContext, TSession, TResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.473",
3
+ "version": "0.0.22-beta.474",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",