@absolutejs/voice 0.0.22-beta.561 → 0.0.22-beta.563

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.
@@ -916,6 +916,32 @@ export type CreateVoiceSessionOptions<TContext = unknown, TSession extends Voice
916
916
  fillerPhrases?: ReadonlyArray<string>;
917
917
  /** Milliseconds after turn-commit before the filler fires. Default 250ms — short enough to feel instant, long enough to skip if the LLM is very fast. */
918
918
  fillerDelayMs?: number;
919
+ /**
920
+ * Latency Theater — content-aware filler (Boardy parity move). When
921
+ * defined, the runtime calls `fillerFor({ userText, ... })` in parallel
922
+ * with the main LLM call to generate a brief acknowledgement of what the
923
+ * caller just said ("Freelance CFOs — interesting.", "Yeah, I hear you.").
924
+ * The runtime races the promise against `fillerForTimeoutMs` (default
925
+ * 600ms). If `fillerFor` returns a non-empty string in time, it's spoken
926
+ * INSTEAD of a random `fillerPhrases` entry. On timeout or null return,
927
+ * the runtime falls back to a static random phrase, so a slow / failed
928
+ * acknowledgement call never costs you the filler entirely.
929
+ *
930
+ * Return `null` (or an empty string) to explicitly skip filler for this
931
+ * turn — useful when `userText` is so short ("yes", "no", "okay") that
932
+ * acknowledging it back sounds robotic. Return throws are caught and
933
+ * treated as null.
934
+ *
935
+ * Cost-aware: callers typically wire this to a cheap nano/haiku model
936
+ * (gpt-4.1-nano, claude-haiku-4-5) that returns 2–5 words.
937
+ */
938
+ fillerFor?: (input: {
939
+ sessionId: string;
940
+ turnId: string;
941
+ userText: string;
942
+ }) => Promise<string | null>;
943
+ /** Ceiling for the `fillerFor` call before we fall back to a static phrase. Default 600ms. */
944
+ fillerForTimeoutMs?: number;
919
945
  /**
920
946
  * Default spoken ack if the model returns ONLY tool calls (no text) and the
921
947
  * turn isn't ending. Without this, the caller hears total silence after
package/dist/index.js CHANGED
@@ -3852,7 +3852,7 @@ var createVoiceSession = (options) => {
3852
3852
  };
3853
3853
  const appendTurnLatencyStage = async (input) => appendTrace({
3854
3854
  at: input.at,
3855
- payload: { stage: input.stage },
3855
+ payload: { stage: input.stage, ...input.metadata ?? {} },
3856
3856
  session: input.session,
3857
3857
  turnId: input.turnId,
3858
3858
  type: "turn_latency.stage"
@@ -3875,6 +3875,8 @@ var createVoiceSession = (options) => {
3875
3875
  let fillerToken = 0;
3876
3876
  const fillerPhrases = (options.fillerPhrases ?? []).filter((p) => typeof p === "string" && p.trim().length > 0);
3877
3877
  const fillerDelayMs = options.fillerDelayMs ?? 250;
3878
+ const fillerFor = options.fillerFor;
3879
+ const fillerForTimeoutMs = options.fillerForTimeoutMs ?? 600;
3878
3880
  const currentTurnAudio = [];
3879
3881
  const pendingUserAttachments = [];
3880
3882
  let fallbackAttemptsForCurrentTurn = 0;
@@ -4259,6 +4261,11 @@ var createVoiceSession = (options) => {
4259
4261
  return;
4260
4262
  }
4261
4263
  activeTTSTurnId = undefined;
4264
+ appendTurnLatencyStage({
4265
+ metadata: { reason },
4266
+ stage: "tts_canceled",
4267
+ turnId: cancelledTurnId
4268
+ }).catch(() => {});
4262
4269
  Promise.resolve(socket.clear?.()).catch(() => {});
4263
4270
  if (!ttsAdapterSessionCanCancel(activeSession)) {
4264
4271
  return;
@@ -4918,6 +4925,15 @@ var createVoiceSession = (options) => {
4918
4925
  };
4919
4926
  const handlePartial = async (transcript) => {
4920
4927
  if (activeTTSTurnId !== undefined && transcript.text.trim()) {
4928
+ const triggeringText = transcript.text.trim();
4929
+ appendTurnLatencyStage({
4930
+ metadata: {
4931
+ partial: triggeringText.slice(0, 200),
4932
+ source: "stt_partial"
4933
+ },
4934
+ stage: "barge_in",
4935
+ turnId: activeTTSTurnId
4936
+ }).catch(() => {});
4921
4937
  cancelActiveTTS("barge-in");
4922
4938
  }
4923
4939
  const session = await writeSession((session2) => {
@@ -5295,27 +5311,59 @@ var createVoiceSession = (options) => {
5295
5311
  const injectedInstruction = liveOpsControl?.injectedInstruction?.trim();
5296
5312
  const ttsStreamer = options.tts ? createTurnTTSStreamer(turn, session) : undefined;
5297
5313
  if (fillerPhrases.length > 0 && options.tts && !ttsStreamer) {}
5298
- if (fillerPhrases.length > 0 && options.tts) {
5314
+ if ((fillerPhrases.length > 0 || fillerFor) && options.tts) {
5299
5315
  fillerToken += 1;
5300
5316
  const myToken = fillerToken;
5301
5317
  if (fillerTimer)
5302
5318
  clearTimeout(fillerTimer);
5319
+ const fillerForPromise = fillerFor ? Promise.race([
5320
+ (async () => {
5321
+ try {
5322
+ const v = await fillerFor({
5323
+ sessionId: options.id,
5324
+ turnId: turn.id,
5325
+ userText: turn.text ?? ""
5326
+ });
5327
+ return v && v.trim().length > 0 ? v : null;
5328
+ } catch {
5329
+ return null;
5330
+ }
5331
+ })(),
5332
+ new Promise((resolve) => setTimeout(() => resolve(null), fillerForTimeoutMs))
5333
+ ]) : null;
5303
5334
  fillerTimer = setTimeout(() => {
5304
5335
  fillerTimer = null;
5305
5336
  if (myToken !== fillerToken)
5306
5337
  return;
5307
5338
  if (activeTTSTurnId === turn.id)
5308
5339
  return;
5309
- const phrase = fillerPhrases[Math.floor(Math.random() * fillerPhrases.length)] ?? "";
5310
- if (!phrase)
5311
- return;
5312
5340
  runSerial("filler.send", async () => {
5313
5341
  if (myToken !== fillerToken || activeTTSTurnId === turn.id)
5314
5342
  return;
5343
+ let phrase = null;
5344
+ let source = "static";
5345
+ if (fillerForPromise) {
5346
+ phrase = await fillerForPromise;
5347
+ if (phrase)
5348
+ source = "fillerFor";
5349
+ if (myToken !== fillerToken || activeTTSTurnId === turn.id)
5350
+ return;
5351
+ }
5352
+ if (!phrase && fillerPhrases.length > 0) {
5353
+ phrase = fillerPhrases[Math.floor(Math.random() * fillerPhrases.length)] ?? null;
5354
+ source = "static";
5355
+ }
5356
+ if (!phrase)
5357
+ return;
5315
5358
  const adapterSession = await ensureTTSSession();
5316
5359
  if (!adapterSession)
5317
5360
  return;
5318
5361
  fillerActive = true;
5362
+ appendTurnLatencyStage({
5363
+ metadata: { phrase, source },
5364
+ stage: "filler_sent",
5365
+ turnId: turn.id
5366
+ }).catch(() => {});
5319
5367
  try {
5320
5368
  await adapterSession.send(phrase);
5321
5369
  } catch {
@@ -5347,10 +5395,7 @@ var createVoiceSession = (options) => {
5347
5395
  }, onTurnTimeoutMs);
5348
5396
  });
5349
5397
  try {
5350
- committedOutput = await Promise.race([
5351
- onTurnPromise,
5352
- timeoutPromise
5353
- ]);
5398
+ committedOutput = await Promise.race([onTurnPromise, timeoutPromise]);
5354
5399
  } finally {
5355
5400
  if (timer)
5356
5401
  clearTimeout(timer);
@@ -24720,6 +24765,8 @@ var createTwilioMediaStreamBridge = (socket, options) => {
24720
24765
  ...options.semanticTurnDetector ? { semanticTurnDetector: options.semanticTurnDetector } : {},
24721
24766
  ...options.fillerPhrases ? { fillerPhrases: options.fillerPhrases } : {},
24722
24767
  ...options.fillerDelayMs !== undefined ? { fillerDelayMs: options.fillerDelayMs } : {},
24768
+ ...options.fillerFor ? { fillerFor: options.fillerFor } : {},
24769
+ ...options.fillerForTimeoutMs !== undefined ? { fillerForTimeoutMs: options.fillerForTimeoutMs } : {},
24723
24770
  ...options.defaultSilentTurnAck !== undefined ? { defaultSilentTurnAck: options.defaultSilentTurnAck } : {},
24724
24771
  ...options.routeOnTurnTimeoutMs !== undefined ? { routeOnTurnTimeoutMs: options.routeOnTurnTimeoutMs } : {},
24725
24772
  trace: options.trace,
@@ -132,6 +132,20 @@ export type TwilioMediaStreamBridgeOptions<TContext = unknown, TSession extends
132
132
  fillerPhrases?: ReadonlyArray<string>;
133
133
  /** Milliseconds after turn-commit before the filler fires. Default 250ms. */
134
134
  fillerDelayMs?: number;
135
+ /**
136
+ * Content-aware filler (Latency Theater). Called in parallel with the
137
+ * main LLM turn; if it resolves within `fillerForTimeoutMs` the runtime
138
+ * speaks the result instead of a random `fillerPhrases` entry. Return
139
+ * `null` to skip filler for this turn. See CreateVoiceSessionOptions
140
+ * for full semantics.
141
+ */
142
+ fillerFor?: (input: {
143
+ sessionId: string;
144
+ turnId: string;
145
+ userText: string;
146
+ }) => Promise<string | null>;
147
+ /** Cap on the `fillerFor` race before falling back to a static phrase. Default 600ms. */
148
+ fillerForTimeoutMs?: number;
135
149
  /**
136
150
  * Default spoken ack if the model returns ONLY tool calls (no text) and
137
151
  * the turn isn't ending. Without this, the caller hears silence and
@@ -5723,7 +5723,7 @@ var createVoiceSession = (options) => {
5723
5723
  };
5724
5724
  const appendTurnLatencyStage = async (input) => appendTrace({
5725
5725
  at: input.at,
5726
- payload: { stage: input.stage },
5726
+ payload: { stage: input.stage, ...input.metadata ?? {} },
5727
5727
  session: input.session,
5728
5728
  turnId: input.turnId,
5729
5729
  type: "turn_latency.stage"
@@ -5746,6 +5746,8 @@ var createVoiceSession = (options) => {
5746
5746
  let fillerToken = 0;
5747
5747
  const fillerPhrases = (options.fillerPhrases ?? []).filter((p) => typeof p === "string" && p.trim().length > 0);
5748
5748
  const fillerDelayMs = options.fillerDelayMs ?? 250;
5749
+ const fillerFor = options.fillerFor;
5750
+ const fillerForTimeoutMs = options.fillerForTimeoutMs ?? 600;
5749
5751
  const currentTurnAudio = [];
5750
5752
  const pendingUserAttachments = [];
5751
5753
  let fallbackAttemptsForCurrentTurn = 0;
@@ -6130,6 +6132,11 @@ var createVoiceSession = (options) => {
6130
6132
  return;
6131
6133
  }
6132
6134
  activeTTSTurnId = undefined;
6135
+ appendTurnLatencyStage({
6136
+ metadata: { reason },
6137
+ stage: "tts_canceled",
6138
+ turnId: cancelledTurnId
6139
+ }).catch(() => {});
6133
6140
  Promise.resolve(socket.clear?.()).catch(() => {});
6134
6141
  if (!ttsAdapterSessionCanCancel(activeSession)) {
6135
6142
  return;
@@ -6789,6 +6796,15 @@ var createVoiceSession = (options) => {
6789
6796
  };
6790
6797
  const handlePartial = async (transcript) => {
6791
6798
  if (activeTTSTurnId !== undefined && transcript.text.trim()) {
6799
+ const triggeringText = transcript.text.trim();
6800
+ appendTurnLatencyStage({
6801
+ metadata: {
6802
+ partial: triggeringText.slice(0, 200),
6803
+ source: "stt_partial"
6804
+ },
6805
+ stage: "barge_in",
6806
+ turnId: activeTTSTurnId
6807
+ }).catch(() => {});
6792
6808
  cancelActiveTTS("barge-in");
6793
6809
  }
6794
6810
  const session = await writeSession((session2) => {
@@ -7166,27 +7182,59 @@ var createVoiceSession = (options) => {
7166
7182
  const injectedInstruction = liveOpsControl?.injectedInstruction?.trim();
7167
7183
  const ttsStreamer = options.tts ? createTurnTTSStreamer(turn, session) : undefined;
7168
7184
  if (fillerPhrases.length > 0 && options.tts && !ttsStreamer) {}
7169
- if (fillerPhrases.length > 0 && options.tts) {
7185
+ if ((fillerPhrases.length > 0 || fillerFor) && options.tts) {
7170
7186
  fillerToken += 1;
7171
7187
  const myToken = fillerToken;
7172
7188
  if (fillerTimer)
7173
7189
  clearTimeout(fillerTimer);
7190
+ const fillerForPromise = fillerFor ? Promise.race([
7191
+ (async () => {
7192
+ try {
7193
+ const v = await fillerFor({
7194
+ sessionId: options.id,
7195
+ turnId: turn.id,
7196
+ userText: turn.text ?? ""
7197
+ });
7198
+ return v && v.trim().length > 0 ? v : null;
7199
+ } catch {
7200
+ return null;
7201
+ }
7202
+ })(),
7203
+ new Promise((resolve2) => setTimeout(() => resolve2(null), fillerForTimeoutMs))
7204
+ ]) : null;
7174
7205
  fillerTimer = setTimeout(() => {
7175
7206
  fillerTimer = null;
7176
7207
  if (myToken !== fillerToken)
7177
7208
  return;
7178
7209
  if (activeTTSTurnId === turn.id)
7179
7210
  return;
7180
- const phrase = fillerPhrases[Math.floor(Math.random() * fillerPhrases.length)] ?? "";
7181
- if (!phrase)
7182
- return;
7183
7211
  runSerial("filler.send", async () => {
7184
7212
  if (myToken !== fillerToken || activeTTSTurnId === turn.id)
7185
7213
  return;
7214
+ let phrase = null;
7215
+ let source = "static";
7216
+ if (fillerForPromise) {
7217
+ phrase = await fillerForPromise;
7218
+ if (phrase)
7219
+ source = "fillerFor";
7220
+ if (myToken !== fillerToken || activeTTSTurnId === turn.id)
7221
+ return;
7222
+ }
7223
+ if (!phrase && fillerPhrases.length > 0) {
7224
+ phrase = fillerPhrases[Math.floor(Math.random() * fillerPhrases.length)] ?? null;
7225
+ source = "static";
7226
+ }
7227
+ if (!phrase)
7228
+ return;
7186
7229
  const adapterSession = await ensureTTSSession();
7187
7230
  if (!adapterSession)
7188
7231
  return;
7189
7232
  fillerActive = true;
7233
+ appendTurnLatencyStage({
7234
+ metadata: { phrase, source },
7235
+ stage: "filler_sent",
7236
+ turnId: turn.id
7237
+ }).catch(() => {});
7190
7238
  try {
7191
7239
  await adapterSession.send(phrase);
7192
7240
  } catch {
@@ -7218,10 +7266,7 @@ var createVoiceSession = (options) => {
7218
7266
  }, onTurnTimeoutMs);
7219
7267
  });
7220
7268
  try {
7221
- committedOutput = await Promise.race([
7222
- onTurnPromise,
7223
- timeoutPromise
7224
- ]);
7269
+ committedOutput = await Promise.race([onTurnPromise, timeoutPromise]);
7225
7270
  } finally {
7226
7271
  if (timer)
7227
7272
  clearTimeout(timer);
@@ -13310,6 +13355,8 @@ var createTwilioMediaStreamBridge = (socket, options) => {
13310
13355
  ...options.semanticTurnDetector ? { semanticTurnDetector: options.semanticTurnDetector } : {},
13311
13356
  ...options.fillerPhrases ? { fillerPhrases: options.fillerPhrases } : {},
13312
13357
  ...options.fillerDelayMs !== undefined ? { fillerDelayMs: options.fillerDelayMs } : {},
13358
+ ...options.fillerFor ? { fillerFor: options.fillerFor } : {},
13359
+ ...options.fillerForTimeoutMs !== undefined ? { fillerForTimeoutMs: options.fillerForTimeoutMs } : {},
13313
13360
  ...options.defaultSilentTurnAck !== undefined ? { defaultSilentTurnAck: options.defaultSilentTurnAck } : {},
13314
13361
  ...options.routeOnTurnTimeoutMs !== undefined ? { routeOnTurnTimeoutMs: options.routeOnTurnTimeoutMs } : {},
13315
13362
  trace: options.trace,
package/package.json CHANGED
@@ -1,157 +1,157 @@
1
1
  {
2
- "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.561",
4
- "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/absolutejs/voice.git"
8
- },
9
- "files": [
10
- "dist",
11
- "README.md"
12
- ],
13
- "main": "./dist/index.js",
14
- "types": "./dist/index.d.ts",
15
- "license": "CC BY-NC 4.0",
16
- "author": "Alex Kahn",
17
- "scripts": {
18
- "build": "rm -rf dist && bun build ./src/index.ts ./src/client/index.ts ./src/react/index.ts ./src/vue/index.ts ./src/svelte/index.ts ./src/angular/index.ts ./src/testing/index.ts ./src/drizzle/index.ts --outdir dist --target bun --external elysia --external react --external vue --external @angular/core --external @absolutejs/absolute --external @absolutejs/ai --external @absolutejs/media --external drizzle-orm && bun build ./src/client/htmxBootstrap.ts --outdir dist/client --target browser --format esm && bun build ./src/embed/index.ts --outfile dist/embed/voice-widget.js --target browser --format iife --minify && bun build ./src/embed/index.ts --outdir dist/embed --target browser --format esm && tsc --emitDeclarationOnly --project tsconfig.json",
19
- "config": "absolute config",
20
- "format": "absolute prettier --write",
21
- "knip": "knip",
22
- "lint": "absolute eslint",
23
- "release": "bun run format && bun run build && bun publish",
24
- "test": "bun test ./test/*.test.ts",
25
- "test:adapters": "bun test ./test/live/*.test.ts",
26
- "test:assemblyai": "bun test ./test/live/assemblyai.live.test.ts",
27
- "test:deepgram": "bun test ./test/live/deepgram.live.test.ts",
28
- "test:elevenlabs": "bun test ./test/live/elevenlabs.live.test.ts",
29
- "test:openai": "bun test ./test/live/openai.live.test.ts",
30
- "typecheck": "absolute typecheck"
31
- },
32
- "exports": {
33
- ".": {
34
- "import": "./dist/index.js",
35
- "types": "./dist/index.d.ts"
36
- },
37
- "./client": {
38
- "browser": "./dist/client/index.js",
39
- "import": "./dist/client/index.js",
40
- "types": "./dist/client/index.d.ts"
41
- },
42
- "./react": {
43
- "browser": "./dist/react/index.js",
44
- "import": "./dist/react/index.js",
45
- "types": "./dist/react/index.d.ts"
46
- },
47
- "./vue": {
48
- "browser": "./dist/vue/index.js",
49
- "import": "./dist/vue/index.js",
50
- "types": "./dist/vue/index.d.ts"
51
- },
52
- "./svelte": {
53
- "import": "./dist/svelte/index.js",
54
- "types": "./dist/svelte/index.d.ts"
55
- },
56
- "./angular": {
57
- "import": "./dist/angular/index.js",
58
- "types": "./dist/angular/index.d.ts"
59
- },
60
- "./drizzle": {
61
- "import": "./dist/drizzle/index.js",
62
- "types": "./dist/drizzle/index.d.ts",
63
- "default": "./dist/drizzle/index.js"
64
- },
65
- "./testing": {
66
- "import": "./dist/testing/index.js",
67
- "types": "./dist/testing/index.d.ts"
68
- },
69
- "./embed": {
70
- "browser": "./dist/embed/index.js",
71
- "import": "./dist/embed/index.js",
72
- "types": "./dist/embed/index.d.ts"
73
- },
74
- "./embed/voice-widget.js": "./dist/embed/voice-widget.js"
75
- },
76
- "typesVersions": {
77
- "*": {
78
- "testing": [
79
- "dist/testing/index.d.ts"
80
- ],
81
- "client": [
82
- "dist/client/index.d.ts"
83
- ],
84
- "react": [
85
- "dist/react/index.d.ts"
86
- ],
87
- "svelte": [
88
- "dist/svelte/index.d.ts"
89
- ],
90
- "vue": [
91
- "dist/vue/index.d.ts"
92
- ],
93
- "angular": [
94
- "dist/angular/index.d.ts"
95
- ],
96
- "drizzle": [
97
- "dist/drizzle/index.d.ts"
98
- ],
99
- "embed": [
100
- "dist/embed/index.d.ts"
101
- ]
102
- }
103
- },
104
- "peerDependencies": {
105
- "@absolutejs/absolute": ">=0.19.0-beta.646",
106
- "@absolutejs/ai": ">=0.0.5",
107
- "@angular/core": ">=21.0.0",
108
- "drizzle-orm": ">=0.36.0",
109
- "elysia": ">=1.4.18",
110
- "react": ">=19.0.0",
111
- "vue": ">=3.5.0"
112
- },
113
- "peerDependenciesMeta": {
114
- "@absolutejs/ai": {
115
- "optional": true
116
- },
117
- "drizzle-orm": {
118
- "optional": true
119
- },
120
- "@angular/core": {
121
- "optional": true
122
- },
123
- "react": {
124
- "optional": true
125
- },
126
- "vue": {
127
- "optional": true
128
- }
129
- },
130
- "dependencies": {
131
- "@absolutejs/media": "0.0.1-beta.19"
132
- },
133
- "devDependencies": {
134
- "@absolutejs/absolute": "0.19.0-beta.1009",
135
- "@absolutejs/ai": "0.0.5",
136
- "@angular/core": "^21.0.0",
137
- "@electric-sql/pglite": "^0.4.5",
138
- "@eslint/js": "^10.0.1",
139
- "@stylistic/eslint-plugin": "^5.10.0",
140
- "@types/bun": "1.3.9",
141
- "@types/react": "19.2.0",
142
- "@typescript-eslint/parser": "^8.57.2",
143
- "drizzle-orm": "0.44.7",
144
- "elysia": "1.4.18",
145
- "eslint": "^10.1.0",
146
- "eslint-plugin-absolute": "0.11.0-beta.3",
147
- "eslint-plugin-promise": "^7.2.1",
148
- "eslint-plugin-security": "^4.0.0",
149
- "globals": "^17.4.0",
150
- "knip": "^6.14.1",
151
- "prettier": "^3.4.0",
152
- "react": "19.2.1",
153
- "typescript": "^5.9.3",
154
- "typescript-eslint": "^8.57.2",
155
- "vue": "3.5.27"
156
- }
2
+ "name": "@absolutejs/voice",
3
+ "version": "0.0.22-beta.563",
4
+ "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/absolutejs/voice.git"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "license": "CC BY-NC 4.0",
16
+ "author": "Alex Kahn",
17
+ "scripts": {
18
+ "build": "rm -rf dist && bun build ./src/index.ts ./src/client/index.ts ./src/react/index.ts ./src/vue/index.ts ./src/svelte/index.ts ./src/angular/index.ts ./src/testing/index.ts ./src/drizzle/index.ts --outdir dist --target bun --external elysia --external react --external vue --external @angular/core --external @absolutejs/absolute --external @absolutejs/ai --external @absolutejs/media --external drizzle-orm && bun build ./src/client/htmxBootstrap.ts --outdir dist/client --target browser --format esm && bun build ./src/embed/index.ts --outfile dist/embed/voice-widget.js --target browser --format iife --minify && bun build ./src/embed/index.ts --outdir dist/embed --target browser --format esm && tsc --emitDeclarationOnly --project tsconfig.json",
19
+ "config": "absolute config",
20
+ "format": "absolute prettier --write",
21
+ "knip": "knip",
22
+ "lint": "absolute eslint",
23
+ "release": "bun run format && bun run build && bun publish",
24
+ "test": "bun test ./test/*.test.ts",
25
+ "test:adapters": "bun test ./test/live/*.test.ts",
26
+ "test:assemblyai": "bun test ./test/live/assemblyai.live.test.ts",
27
+ "test:deepgram": "bun test ./test/live/deepgram.live.test.ts",
28
+ "test:elevenlabs": "bun test ./test/live/elevenlabs.live.test.ts",
29
+ "test:openai": "bun test ./test/live/openai.live.test.ts",
30
+ "typecheck": "absolute typecheck"
31
+ },
32
+ "exports": {
33
+ ".": {
34
+ "import": "./dist/index.js",
35
+ "types": "./dist/index.d.ts"
36
+ },
37
+ "./client": {
38
+ "browser": "./dist/client/index.js",
39
+ "import": "./dist/client/index.js",
40
+ "types": "./dist/client/index.d.ts"
41
+ },
42
+ "./react": {
43
+ "browser": "./dist/react/index.js",
44
+ "import": "./dist/react/index.js",
45
+ "types": "./dist/react/index.d.ts"
46
+ },
47
+ "./vue": {
48
+ "browser": "./dist/vue/index.js",
49
+ "import": "./dist/vue/index.js",
50
+ "types": "./dist/vue/index.d.ts"
51
+ },
52
+ "./svelte": {
53
+ "import": "./dist/svelte/index.js",
54
+ "types": "./dist/svelte/index.d.ts"
55
+ },
56
+ "./angular": {
57
+ "import": "./dist/angular/index.js",
58
+ "types": "./dist/angular/index.d.ts"
59
+ },
60
+ "./drizzle": {
61
+ "import": "./dist/drizzle/index.js",
62
+ "types": "./dist/drizzle/index.d.ts",
63
+ "default": "./dist/drizzle/index.js"
64
+ },
65
+ "./testing": {
66
+ "import": "./dist/testing/index.js",
67
+ "types": "./dist/testing/index.d.ts"
68
+ },
69
+ "./embed": {
70
+ "browser": "./dist/embed/index.js",
71
+ "import": "./dist/embed/index.js",
72
+ "types": "./dist/embed/index.d.ts"
73
+ },
74
+ "./embed/voice-widget.js": "./dist/embed/voice-widget.js"
75
+ },
76
+ "typesVersions": {
77
+ "*": {
78
+ "testing": [
79
+ "dist/testing/index.d.ts"
80
+ ],
81
+ "client": [
82
+ "dist/client/index.d.ts"
83
+ ],
84
+ "react": [
85
+ "dist/react/index.d.ts"
86
+ ],
87
+ "svelte": [
88
+ "dist/svelte/index.d.ts"
89
+ ],
90
+ "vue": [
91
+ "dist/vue/index.d.ts"
92
+ ],
93
+ "angular": [
94
+ "dist/angular/index.d.ts"
95
+ ],
96
+ "drizzle": [
97
+ "dist/drizzle/index.d.ts"
98
+ ],
99
+ "embed": [
100
+ "dist/embed/index.d.ts"
101
+ ]
102
+ }
103
+ },
104
+ "peerDependencies": {
105
+ "@absolutejs/absolute": ">=0.19.0-beta.646",
106
+ "@absolutejs/ai": ">=0.0.5",
107
+ "@angular/core": ">=21.0.0",
108
+ "drizzle-orm": ">=0.36.0",
109
+ "elysia": ">=1.4.18",
110
+ "react": ">=19.0.0",
111
+ "vue": ">=3.5.0"
112
+ },
113
+ "peerDependenciesMeta": {
114
+ "@absolutejs/ai": {
115
+ "optional": true
116
+ },
117
+ "drizzle-orm": {
118
+ "optional": true
119
+ },
120
+ "@angular/core": {
121
+ "optional": true
122
+ },
123
+ "react": {
124
+ "optional": true
125
+ },
126
+ "vue": {
127
+ "optional": true
128
+ }
129
+ },
130
+ "dependencies": {
131
+ "@absolutejs/media": "0.0.1-beta.19"
132
+ },
133
+ "devDependencies": {
134
+ "@absolutejs/absolute": "0.19.0-beta.1009",
135
+ "@absolutejs/ai": "0.0.5",
136
+ "@angular/core": "^21.0.0",
137
+ "@electric-sql/pglite": "^0.4.5",
138
+ "@eslint/js": "^10.0.1",
139
+ "@stylistic/eslint-plugin": "^5.10.0",
140
+ "@types/bun": "1.3.9",
141
+ "@types/react": "19.2.0",
142
+ "@typescript-eslint/parser": "^8.57.2",
143
+ "drizzle-orm": "0.44.7",
144
+ "elysia": "1.4.18",
145
+ "eslint": "^10.1.0",
146
+ "eslint-plugin-absolute": "0.11.0-beta.3",
147
+ "eslint-plugin-promise": "^7.2.1",
148
+ "eslint-plugin-security": "^4.0.0",
149
+ "globals": "^17.4.0",
150
+ "knip": "^6.14.1",
151
+ "prettier": "^3.4.0",
152
+ "react": "19.2.1",
153
+ "typescript": "^5.9.3",
154
+ "typescript-eslint": "^8.57.2",
155
+ "vue": "3.5.27"
156
+ }
157
157
  }