@absolutejs/voice 0.0.1

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.
Files changed (179) hide show
  1. package/README.md +150 -0
  2. package/dist/absolutejs/src/angular/angularDeps.d.ts +2 -0
  3. package/dist/absolutejs/src/angular/angularPatch.d.ts +1 -0
  4. package/dist/absolutejs/src/angular/injectorPatch.d.ts +1 -0
  5. package/dist/absolutejs/src/angular/islands.d.ts +3 -0
  6. package/dist/absolutejs/src/angular/lowerDeferSyntax.d.ts +18 -0
  7. package/dist/absolutejs/src/angular/lowerServerIslands.d.ts +1 -0
  8. package/dist/absolutejs/src/angular/pageHandler.d.ts +26 -0
  9. package/dist/absolutejs/src/angular/resolveAngularPackage.d.ts +7 -0
  10. package/dist/absolutejs/src/angular/ssrRender.d.ts +16 -0
  11. package/dist/absolutejs/src/angular/ssrSanitizer.d.ts +3 -0
  12. package/dist/absolutejs/src/build/angularLinkerPlugin.d.ts +11 -0
  13. package/dist/absolutejs/src/build/buildAngularVendor.d.ts +4 -0
  14. package/dist/absolutejs/src/build/buildDepVendor.d.ts +2 -0
  15. package/dist/absolutejs/src/build/buildReactVendor.d.ts +8 -0
  16. package/dist/absolutejs/src/build/buildSvelteVendor.d.ts +6 -0
  17. package/dist/absolutejs/src/build/buildVueVendor.d.ts +6 -0
  18. package/dist/absolutejs/src/build/compileAngular.d.ts +29 -0
  19. package/dist/absolutejs/src/build/compileSvelte.d.ts +13 -0
  20. package/dist/absolutejs/src/build/compileVue.d.ts +19 -0
  21. package/dist/absolutejs/src/build/generateManifest.d.ts +2 -0
  22. package/dist/absolutejs/src/build/generateReactIndexes.d.ts +1 -0
  23. package/dist/absolutejs/src/build/htmlScriptHMRPlugin.d.ts +13 -0
  24. package/dist/absolutejs/src/build/islandEntries.d.ts +32 -0
  25. package/dist/absolutejs/src/build/nativeRewrite.d.ts +5 -0
  26. package/dist/absolutejs/src/build/optimizeHtmlImages.d.ts +2 -0
  27. package/dist/absolutejs/src/build/outputLogs.d.ts +1 -0
  28. package/dist/absolutejs/src/build/resolvePackageImport.d.ts +9 -0
  29. package/dist/absolutejs/src/build/rewriteImports.d.ts +7 -0
  30. package/dist/absolutejs/src/build/rewriteReactImports.d.ts +7 -0
  31. package/dist/absolutejs/src/build/scanConventions.d.ts +5 -0
  32. package/dist/absolutejs/src/build/scanCssEntryPoints.d.ts +1 -0
  33. package/dist/absolutejs/src/build/scanEntryPoints.d.ts +1 -0
  34. package/dist/absolutejs/src/build/staticIslandPages.d.ts +9 -0
  35. package/dist/absolutejs/src/build/updateAssetPaths.d.ts +1 -0
  36. package/dist/absolutejs/src/build/wrapHTMLScript.d.ts +17 -0
  37. package/dist/absolutejs/src/cli/scripts/telemetry.d.ts +5 -0
  38. package/dist/absolutejs/src/cli/telemetryEvent.d.ts +1 -0
  39. package/dist/absolutejs/src/client/islandStore.d.ts +22 -0
  40. package/dist/absolutejs/src/client/streamSwap.d.ts +11 -0
  41. package/dist/absolutejs/src/constants.d.ts +60 -0
  42. package/dist/absolutejs/src/core/build.d.ts +9 -0
  43. package/dist/absolutejs/src/core/currentIslandRegistry.d.ts +7 -0
  44. package/dist/absolutejs/src/core/devBuild.d.ts +6 -0
  45. package/dist/absolutejs/src/core/devRouteRegistrationCallsite.d.ts +2 -0
  46. package/dist/absolutejs/src/core/devVendorPaths.d.ts +13 -0
  47. package/dist/absolutejs/src/core/index.d.ts +7 -0
  48. package/dist/absolutejs/src/core/islandManifest.d.ts +3 -0
  49. package/dist/absolutejs/src/core/islandMarkupAttributes.d.ts +12 -0
  50. package/dist/absolutejs/src/core/islandPageContext.d.ts +12 -0
  51. package/dist/absolutejs/src/core/islandSsr.d.ts +6 -0
  52. package/dist/absolutejs/src/core/islands.d.ts +16 -0
  53. package/dist/absolutejs/src/core/loadIslandRegistry.d.ts +1 -0
  54. package/dist/absolutejs/src/core/lookup.d.ts +1 -0
  55. package/dist/absolutejs/src/core/pageHandlers.d.ts +6 -0
  56. package/dist/absolutejs/src/core/prepare.d.ts +274 -0
  57. package/dist/absolutejs/src/core/prerender.d.ts +31 -0
  58. package/dist/absolutejs/src/core/renderIslandMarkup.d.ts +13 -0
  59. package/dist/absolutejs/src/core/responseEnhancers.d.ts +10 -0
  60. package/dist/absolutejs/src/core/staticStreaming.d.ts +25 -0
  61. package/dist/absolutejs/src/core/streamingSlotRegistrar.d.ts +16 -0
  62. package/dist/absolutejs/src/core/streamingSlotRegistry.d.ts +2 -0
  63. package/dist/absolutejs/src/core/streamingSlotWarningScope.d.ts +4 -0
  64. package/dist/absolutejs/src/core/svelteServerModule.d.ts +1 -0
  65. package/dist/absolutejs/src/core/wrapPageHandlerWithStreamingSlots.d.ts +2 -0
  66. package/dist/absolutejs/src/dev/assetStore.d.ts +8 -0
  67. package/dist/absolutejs/src/dev/buildHMRClient.d.ts +1 -0
  68. package/dist/absolutejs/src/dev/clientManager.d.ts +31 -0
  69. package/dist/absolutejs/src/dev/configResolver.d.ts +14 -0
  70. package/dist/absolutejs/src/dev/dependencyGraph.d.ts +10 -0
  71. package/dist/absolutejs/src/dev/devCert.d.ts +11 -0
  72. package/dist/absolutejs/src/dev/fileHashTracker.d.ts +2 -0
  73. package/dist/absolutejs/src/dev/fileWatcher.d.ts +4 -0
  74. package/dist/absolutejs/src/dev/moduleMapper.d.ts +27 -0
  75. package/dist/absolutejs/src/dev/moduleServer.d.ts +21 -0
  76. package/dist/absolutejs/src/dev/moduleVersionTracker.d.ts +7 -0
  77. package/dist/absolutejs/src/dev/pathUtils.d.ts +5 -0
  78. package/dist/absolutejs/src/dev/reactComponentClassifier.d.ts +2 -0
  79. package/dist/absolutejs/src/dev/rebuildTrigger.d.ts +10 -0
  80. package/dist/absolutejs/src/dev/simpleHTMLHMR.d.ts +4 -0
  81. package/dist/absolutejs/src/dev/simpleHTMXHMR.d.ts +4 -0
  82. package/dist/absolutejs/src/dev/transformCache.d.ts +6 -0
  83. package/dist/absolutejs/src/dev/webSocket.d.ts +9 -0
  84. package/dist/absolutejs/src/index.d.ts +5 -0
  85. package/dist/absolutejs/src/islands/pageMetadata.d.ts +13 -0
  86. package/dist/absolutejs/src/islands/sourceMetadata.d.ts +10 -0
  87. package/dist/absolutejs/src/plugins/devtoolsJson.d.ts +58 -0
  88. package/dist/absolutejs/src/plugins/hmr.d.ts +138 -0
  89. package/dist/absolutejs/src/plugins/imageOptimizer.d.ts +67 -0
  90. package/dist/absolutejs/src/plugins/index.d.ts +3 -0
  91. package/dist/absolutejs/src/plugins/networking.d.ts +2 -0
  92. package/dist/absolutejs/src/plugins/pageRouter.d.ts +1 -0
  93. package/dist/absolutejs/src/react/Island.d.ts +2 -0
  94. package/dist/absolutejs/src/react/bridgeInternals.d.ts +1 -0
  95. package/dist/absolutejs/src/react/createIsland.d.ts +2 -0
  96. package/dist/absolutejs/src/react/hooks/useIslandStore.d.ts +3 -0
  97. package/dist/absolutejs/src/react/index.d.ts +4 -0
  98. package/dist/absolutejs/src/react/pageHandler.d.ts +21 -0
  99. package/dist/absolutejs/src/svelte/lowerAwaitSlotSyntax.d.ts +4 -0
  100. package/dist/absolutejs/src/svelte/lowerIslandSyntax.d.ts +4 -0
  101. package/dist/absolutejs/src/svelte/pageHandler.d.ts +23 -0
  102. package/dist/absolutejs/src/svelte/renderToReadableStream.d.ts +14 -0
  103. package/dist/absolutejs/src/utils/cleanStaleOutputs.d.ts +1 -0
  104. package/dist/absolutejs/src/utils/cleanup.d.ts +8 -0
  105. package/dist/absolutejs/src/utils/commonAncestor.d.ts +1 -0
  106. package/dist/absolutejs/src/utils/defineConfig.d.ts +2 -0
  107. package/dist/absolutejs/src/utils/defineEnv.d.ts +10 -0
  108. package/dist/absolutejs/src/utils/escapeScriptContent.d.ts +1 -0
  109. package/dist/absolutejs/src/utils/generateHeadElement.d.ts +4 -0
  110. package/dist/absolutejs/src/utils/generateSitemap.d.ts +6 -0
  111. package/dist/absolutejs/src/utils/getDurationString.d.ts +1 -0
  112. package/dist/absolutejs/src/utils/getEnv.d.ts +1 -0
  113. package/dist/absolutejs/src/utils/imageProcessing.d.ts +33 -0
  114. package/dist/absolutejs/src/utils/index.d.ts +9 -0
  115. package/dist/absolutejs/src/utils/jsonLd.d.ts +2 -0
  116. package/dist/absolutejs/src/utils/loadConfig.d.ts +1 -0
  117. package/dist/absolutejs/src/utils/logger.d.ts +55 -0
  118. package/dist/absolutejs/src/utils/networking.d.ts +2 -0
  119. package/dist/absolutejs/src/utils/normalizePath.d.ts +9 -0
  120. package/dist/absolutejs/src/utils/registerClientScript.d.ts +36 -0
  121. package/dist/absolutejs/src/utils/resolveConvention.d.ts +9 -0
  122. package/dist/absolutejs/src/utils/ssrErrorPage.d.ts +1 -0
  123. package/dist/absolutejs/src/utils/startupBanner.d.ts +9 -0
  124. package/dist/absolutejs/src/utils/streamingSlotMetricSink.d.ts +18 -0
  125. package/dist/absolutejs/src/utils/streamingSlots.d.ts +76 -0
  126. package/dist/absolutejs/src/utils/stringModifiers.d.ts +3 -0
  127. package/dist/absolutejs/src/utils/validateSafePath.d.ts +1 -0
  128. package/dist/absolutejs/src/vue/pageHandler.d.ts +29 -0
  129. package/dist/absolutejs/types/ai.d.ts +4631 -0
  130. package/dist/absolutejs/types/angular.d.ts +29 -0
  131. package/dist/absolutejs/types/build.d.ts +67 -0
  132. package/dist/absolutejs/types/cli.d.ts +19 -0
  133. package/dist/absolutejs/types/client.d.ts +65 -0
  134. package/dist/absolutejs/types/conventions.d.ts +20 -0
  135. package/dist/absolutejs/types/env.d.ts +2 -0
  136. package/dist/absolutejs/types/image.d.ts +77 -0
  137. package/dist/absolutejs/types/index.d.ts +19 -0
  138. package/dist/absolutejs/types/island.d.ts +48 -0
  139. package/dist/absolutejs/types/jsonLd.d.ts +271 -0
  140. package/dist/absolutejs/types/messages.d.ts +144 -0
  141. package/dist/absolutejs/types/metadata.d.ts +49 -0
  142. package/dist/absolutejs/types/react.d.ts +2 -0
  143. package/dist/absolutejs/types/session.d.ts +16 -0
  144. package/dist/absolutejs/types/sitemap.d.ts +14 -0
  145. package/dist/absolutejs/types/svelte.d.ts +2 -0
  146. package/dist/absolutejs/types/telemetry.d.ts +15 -0
  147. package/dist/absolutejs/types/tool.d.ts +11 -0
  148. package/dist/absolutejs/types/typeGuards.d.ts +5 -0
  149. package/dist/absolutejs/types/vue.d.ts +14 -0
  150. package/dist/absolutejs/types/websocket.d.ts +6 -0
  151. package/dist/angular/index.js +505 -0
  152. package/dist/client/index.js +521 -0
  153. package/dist/index.js +693 -0
  154. package/dist/react/index.js +489 -0
  155. package/dist/svelte/index.js +456 -0
  156. package/dist/voice/src/angular/index.d.ts +1 -0
  157. package/dist/voice/src/angular/voice-stream.service.d.ts +15 -0
  158. package/dist/voice/src/client/actions.d.ts +58 -0
  159. package/dist/voice/src/client/connection.d.ts +12 -0
  160. package/dist/voice/src/client/createVoiceStream.d.ts +14 -0
  161. package/dist/voice/src/client/index.d.ts +3 -0
  162. package/dist/voice/src/client/microphone.d.ts +11 -0
  163. package/dist/voice/src/client/store.d.ts +7 -0
  164. package/dist/voice/src/index.d.ts +5 -0
  165. package/dist/voice/src/logger.d.ts +8 -0
  166. package/dist/voice/src/memoryStore.d.ts +2 -0
  167. package/dist/voice/src/plugin.d.ts +40 -0
  168. package/dist/voice/src/react/index.d.ts +1 -0
  169. package/dist/voice/src/react/useVoiceStream.d.ts +13 -0
  170. package/dist/voice/src/session.d.ts +2 -0
  171. package/dist/voice/src/store.d.ts +6 -0
  172. package/dist/voice/src/svelte/createVoiceStream.d.ts +14 -0
  173. package/dist/voice/src/svelte/index.d.ts +1 -0
  174. package/dist/voice/src/turnDetection.d.ts +3 -0
  175. package/dist/voice/src/types.d.ts +318 -0
  176. package/dist/voice/src/vue/index.d.ts +1 -0
  177. package/dist/voice/src/vue/useVoiceStream.d.ts +13 -0
  178. package/dist/vue/index.js +496 -0
  179. package/package.json +100 -0
package/dist/index.js ADDED
@@ -0,0 +1,693 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __name = (target, name) => {
6
+ Object.defineProperty(target, "name", {
7
+ value: name,
8
+ enumerable: false,
9
+ configurable: true
10
+ });
11
+ return target;
12
+ };
13
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
14
+ var __typeError = (msg) => {
15
+ throw TypeError(msg);
16
+ };
17
+ var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
18
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
19
+ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
20
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
21
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
22
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
23
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
24
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
25
+ var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
26
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({
27
+ kind: __decoratorStrings[kind],
28
+ name,
29
+ metadata,
30
+ addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
31
+ });
32
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
33
+ var __runInitializers = (array, flags, self, value) => {
34
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
35
+ flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
36
+ return value;
37
+ };
38
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
39
+ var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
40
+ var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
41
+ var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
42
+ var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
43
+ get [name]() {
44
+ return __privateGet(this, extra);
45
+ },
46
+ set [name](x) {
47
+ __privateSet(this, extra, x);
48
+ }
49
+ }, name));
50
+ k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
51
+ for (var i = decorators.length - 1;i >= 0; i--) {
52
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
53
+ if (k) {
54
+ ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
55
+ if (k ^ 3)
56
+ access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
57
+ if (k > 2)
58
+ access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
59
+ }
60
+ it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
61
+ done._ = 1;
62
+ if (k ^ 4 || it === undefined)
63
+ __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
64
+ else if (typeof it !== "object" || it === null)
65
+ __typeError("Object expected");
66
+ else
67
+ __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
68
+ }
69
+ return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
70
+ };
71
+
72
+ // src/plugin.ts
73
+ import { Elysia } from "elysia";
74
+
75
+ // src/logger.ts
76
+ var noop = () => {};
77
+ var createNoopLogger = () => ({
78
+ debug: noop,
79
+ error: noop,
80
+ info: noop,
81
+ warn: noop
82
+ });
83
+ var resolveLogger = (logger) => ({
84
+ ...createNoopLogger(),
85
+ ...logger
86
+ });
87
+
88
+ // src/store.ts
89
+ var createId = () => crypto.randomUUID();
90
+ var createVoiceSessionRecord = (id) => ({
91
+ committedTurnIds: [],
92
+ createdAt: Date.now(),
93
+ currentTurn: {
94
+ finalText: "",
95
+ partialText: "",
96
+ transcripts: []
97
+ },
98
+ id,
99
+ reconnect: { attempts: 0 },
100
+ status: "active",
101
+ transcripts: [],
102
+ turns: []
103
+ });
104
+ var resetVoiceSessionRecord = (id, existing) => ({
105
+ ...createVoiceSessionRecord(id),
106
+ metadata: existing?.metadata
107
+ });
108
+ var toVoiceSessionSummary = (session) => ({
109
+ createdAt: session.createdAt,
110
+ id: session.id,
111
+ lastActivityAt: session.lastActivityAt,
112
+ status: session.status,
113
+ turnCount: session.turns.length
114
+ });
115
+
116
+ // src/turnDetection.ts
117
+ var DEFAULT_SILENCE_MS = 700;
118
+ var buildTurnText = (transcripts, partialText) => {
119
+ const finalText = transcripts.map((transcript) => transcript.text.trim()).filter(Boolean).join(" ").trim();
120
+ if (finalText) {
121
+ return finalText;
122
+ }
123
+ return partialText.trim();
124
+ };
125
+
126
+ // src/session.ts
127
+ var DEFAULT_RECONNECT_TIMEOUT = 30000;
128
+ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
129
+ var toError = (value) => value instanceof Error ? value : new Error(String(value));
130
+ var createEmptyCurrentTurn = () => ({
131
+ finalText: "",
132
+ partialText: "",
133
+ transcripts: []
134
+ });
135
+ var cloneTranscript = (transcript) => ({ ...transcript });
136
+ var setTurnResult = (session, turnId, input) => {
137
+ session.turns = session.turns.map((turn) => turn.id === turnId ? {
138
+ ...turn,
139
+ assistantText: input.assistantText ?? turn.assistantText,
140
+ result: input.result ?? turn.result
141
+ } : turn);
142
+ };
143
+ var createVoiceSession = (options) => {
144
+ const logger = resolveLogger(options.logger);
145
+ const reconnect = {
146
+ maxAttempts: options.reconnect.maxAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS,
147
+ strategy: options.reconnect.strategy ?? "resume-last-turn",
148
+ timeout: options.reconnect.timeout ?? DEFAULT_RECONNECT_TIMEOUT
149
+ };
150
+ const turnDetection = {
151
+ silenceMs: options.turnDetection.silenceMs ?? DEFAULT_SILENCE_MS
152
+ };
153
+ let socket = options.socket;
154
+ let sttSession = null;
155
+ let silenceTimer = null;
156
+ const clearSilenceTimer = () => {
157
+ if (!silenceTimer) {
158
+ return;
159
+ }
160
+ clearTimeout(silenceTimer);
161
+ silenceTimer = null;
162
+ };
163
+ const send = async (message) => {
164
+ try {
165
+ await Promise.resolve(socket.send(JSON.stringify(message)));
166
+ } catch (error) {
167
+ logger.warn("voice socket send failed", {
168
+ error: toError(error).message,
169
+ sessionId: options.id,
170
+ type: message.type
171
+ });
172
+ }
173
+ };
174
+ const readSession = async () => options.store.getOrCreate(options.id);
175
+ const writeSession = async (mutate) => {
176
+ const session = await options.store.getOrCreate(options.id);
177
+ mutate(session);
178
+ await options.store.set(options.id, session);
179
+ return session;
180
+ };
181
+ const closeAdapter = async (reason) => {
182
+ if (!sttSession) {
183
+ return;
184
+ }
185
+ const activeSession = sttSession;
186
+ sttSession = null;
187
+ try {
188
+ await activeSession.close(reason);
189
+ } catch (error) {
190
+ logger.warn("voice stt close failed", {
191
+ error: toError(error).message,
192
+ sessionId: options.id
193
+ });
194
+ }
195
+ };
196
+ const scheduleSilenceCommit = () => {
197
+ clearSilenceTimer();
198
+ silenceTimer = setTimeout(() => {
199
+ api.commitTurn("silence");
200
+ }, turnDetection.silenceMs);
201
+ };
202
+ const handleError = async (event) => {
203
+ await send({
204
+ message: event.error.message,
205
+ recoverable: event.recoverable,
206
+ type: "error"
207
+ });
208
+ if (!event.recoverable) {
209
+ await api.fail(event.error);
210
+ }
211
+ };
212
+ const handleClose = async (event) => {
213
+ if (event.recoverable === false) {
214
+ await api.fail(new Error(event.reason ?? "Speech-to-text session closed"));
215
+ }
216
+ };
217
+ const handlePartial = async (transcript) => {
218
+ await writeSession((session) => {
219
+ session.currentTurn.lastAudioAt = Date.now();
220
+ session.currentTurn.partialText = transcript.text;
221
+ session.lastActivityAt = Date.now();
222
+ session.status = "active";
223
+ });
224
+ await send({
225
+ transcript,
226
+ type: "partial"
227
+ });
228
+ scheduleSilenceCommit();
229
+ };
230
+ const handleFinal = async (transcript) => {
231
+ await writeSession((session) => {
232
+ const alreadyPresent = session.currentTurn.transcripts.some((existing) => existing.id === transcript.id);
233
+ if (!alreadyPresent) {
234
+ session.currentTurn.transcripts = [
235
+ ...session.currentTurn.transcripts,
236
+ cloneTranscript(transcript)
237
+ ];
238
+ session.transcripts = [
239
+ ...session.transcripts,
240
+ cloneTranscript(transcript)
241
+ ];
242
+ }
243
+ session.currentTurn.finalText = buildTurnText(session.currentTurn.transcripts, session.currentTurn.partialText);
244
+ session.currentTurn.lastAudioAt = Date.now();
245
+ session.lastActivityAt = Date.now();
246
+ session.status = "active";
247
+ });
248
+ await send({
249
+ transcript,
250
+ type: "final"
251
+ });
252
+ scheduleSilenceCommit();
253
+ };
254
+ const ensureAdapter = async () => {
255
+ if (sttSession) {
256
+ return sttSession;
257
+ }
258
+ sttSession = await options.stt.open({
259
+ format: {
260
+ channels: 1,
261
+ container: "raw",
262
+ encoding: "pcm_s16le",
263
+ sampleRateHz: 16000
264
+ },
265
+ sessionId: options.id
266
+ });
267
+ sttSession.on("partial", ({ transcript }) => {
268
+ handlePartial(transcript);
269
+ });
270
+ sttSession.on("final", ({ transcript }) => {
271
+ handleFinal(transcript);
272
+ });
273
+ sttSession.on("endOfTurn", ({ reason }) => {
274
+ clearSilenceTimer();
275
+ api.commitTurn(reason);
276
+ });
277
+ sttSession.on("error", (event) => {
278
+ handleError(event);
279
+ });
280
+ sttSession.on("close", (event) => {
281
+ handleClose(event);
282
+ });
283
+ return sttSession;
284
+ };
285
+ const completeTurn = async (session, turn) => {
286
+ const output = await options.route.onTurn({
287
+ api,
288
+ context: options.context,
289
+ session,
290
+ turn
291
+ });
292
+ if (output?.assistantText) {
293
+ await writeSession((currentSession) => {
294
+ setTurnResult(currentSession, turn.id, {
295
+ assistantText: output.assistantText
296
+ });
297
+ });
298
+ await send({
299
+ text: output.assistantText,
300
+ turnId: turn.id,
301
+ type: "assistant"
302
+ });
303
+ }
304
+ if (output?.result !== undefined) {
305
+ await writeSession((currentSession) => {
306
+ setTurnResult(currentSession, turn.id, {
307
+ result: output.result
308
+ });
309
+ });
310
+ }
311
+ if (output?.complete) {
312
+ await api.complete(output.result);
313
+ }
314
+ };
315
+ const api = {
316
+ id: options.id,
317
+ close: async (reason) => {
318
+ clearSilenceTimer();
319
+ await closeAdapter(reason);
320
+ await Promise.resolve(socket.close(1000, reason));
321
+ },
322
+ commitTurn: async (reason = "manual") => {
323
+ clearSilenceTimer();
324
+ const session = await readSession();
325
+ if (session.status === "completed" || session.status === "failed") {
326
+ return;
327
+ }
328
+ const text = buildTurnText(session.currentTurn.transcripts, session.currentTurn.partialText);
329
+ if (!text) {
330
+ return;
331
+ }
332
+ const turn = {
333
+ committedAt: Date.now(),
334
+ id: createId(),
335
+ text,
336
+ transcripts: session.currentTurn.transcripts.length > 0 ? session.currentTurn.transcripts.map(cloneTranscript) : [
337
+ {
338
+ id: createId(),
339
+ isFinal: false,
340
+ text
341
+ }
342
+ ]
343
+ };
344
+ const updatedSession = await writeSession((currentSession) => {
345
+ currentSession.committedTurnIds = [
346
+ ...currentSession.committedTurnIds,
347
+ turn.id
348
+ ];
349
+ currentSession.currentTurn = createEmptyCurrentTurn();
350
+ currentSession.lastActivityAt = Date.now();
351
+ currentSession.status = "active";
352
+ currentSession.turns = [...currentSession.turns, turn];
353
+ });
354
+ logger.info("voice turn committed", {
355
+ reason,
356
+ sessionId: options.id,
357
+ turnId: turn.id
358
+ });
359
+ await send({
360
+ turn,
361
+ type: "turn"
362
+ });
363
+ await completeTurn(updatedSession, turn);
364
+ },
365
+ complete: async (result) => {
366
+ clearSilenceTimer();
367
+ const session = await writeSession((currentSession) => {
368
+ if (currentSession.status === "completed") {
369
+ return;
370
+ }
371
+ currentSession.lastActivityAt = Date.now();
372
+ currentSession.status = "completed";
373
+ if (result !== undefined && currentSession.turns.length > 0) {
374
+ const lastTurn = currentSession.turns.at(-1);
375
+ if (lastTurn) {
376
+ setTurnResult(currentSession, lastTurn.id, {
377
+ result
378
+ });
379
+ }
380
+ }
381
+ });
382
+ await send({
383
+ sessionId: options.id,
384
+ type: "complete"
385
+ });
386
+ await closeAdapter("complete");
387
+ await options.route.onComplete({
388
+ api,
389
+ context: options.context,
390
+ session
391
+ });
392
+ },
393
+ connect: async (nextSocket) => {
394
+ socket = nextSocket;
395
+ const existingSession = await options.store.get(options.id);
396
+ let session = existingSession ?? createVoiceSessionRecord(options.id);
397
+ let shouldFireOnSession = !existingSession;
398
+ if (existingSession?.status === "reconnecting") {
399
+ const nextAttempts = existingSession.reconnect.attempts + 1;
400
+ const reconnectExpired = existingSession.reconnect.lastDisconnectAt !== undefined && Date.now() - existingSession.reconnect.lastDisconnectAt > reconnect.timeout;
401
+ const tooManyAttempts = nextAttempts > reconnect.maxAttempts;
402
+ if (reconnect.strategy === "fail" && (reconnectExpired || tooManyAttempts)) {
403
+ await api.fail(new Error("Voice session reconnect policy exhausted"));
404
+ return;
405
+ }
406
+ if (reconnect.strategy === "restart" && (reconnectExpired || tooManyAttempts)) {
407
+ session = resetVoiceSessionRecord(options.id, existingSession);
408
+ shouldFireOnSession = true;
409
+ } else {
410
+ session = {
411
+ ...existingSession,
412
+ reconnect: {
413
+ ...existingSession.reconnect,
414
+ attempts: nextAttempts
415
+ },
416
+ status: "active"
417
+ };
418
+ }
419
+ }
420
+ await options.store.set(options.id, session);
421
+ await send({
422
+ sessionId: options.id,
423
+ status: session.status,
424
+ type: "session"
425
+ });
426
+ if (shouldFireOnSession) {
427
+ await options.route.onSession?.({
428
+ api,
429
+ context: options.context,
430
+ session
431
+ });
432
+ }
433
+ if (session.status === "completed") {
434
+ await send({
435
+ sessionId: options.id,
436
+ type: "complete"
437
+ });
438
+ return;
439
+ }
440
+ await ensureAdapter();
441
+ },
442
+ disconnect: async (event) => {
443
+ clearSilenceTimer();
444
+ await closeAdapter(event?.reason);
445
+ if (reconnect.strategy === "fail") {
446
+ await api.fail(new Error(event?.reason ?? "Voice socket disconnected"));
447
+ return;
448
+ }
449
+ await writeSession((session) => {
450
+ if (session.status === "completed" || session.status === "failed") {
451
+ return;
452
+ }
453
+ session.lastActivityAt = Date.now();
454
+ session.reconnect.lastDisconnectAt = Date.now();
455
+ session.status = "reconnecting";
456
+ });
457
+ },
458
+ fail: async (error) => {
459
+ clearSilenceTimer();
460
+ const session = await writeSession((currentSession) => {
461
+ currentSession.lastActivityAt = Date.now();
462
+ currentSession.status = "failed";
463
+ });
464
+ const resolvedError = toError(error);
465
+ await send({
466
+ message: resolvedError.message,
467
+ recoverable: false,
468
+ type: "error"
469
+ });
470
+ await closeAdapter("failed");
471
+ await options.route.onError?.({
472
+ api,
473
+ context: options.context,
474
+ error: resolvedError,
475
+ session,
476
+ sessionId: options.id
477
+ });
478
+ },
479
+ receiveAudio: async (audio) => {
480
+ const session = await readSession();
481
+ if (session.status === "completed" || session.status === "failed") {
482
+ return;
483
+ }
484
+ const adapter = await ensureAdapter();
485
+ await writeSession((currentSession) => {
486
+ currentSession.currentTurn.lastAudioAt = Date.now();
487
+ currentSession.lastActivityAt = Date.now();
488
+ currentSession.status = "active";
489
+ });
490
+ await adapter.send(audio);
491
+ },
492
+ snapshot: async () => readSession()
493
+ };
494
+ return api;
495
+ };
496
+
497
+ // src/plugin.ts
498
+ var isArrayBufferView = (value) => typeof value === "object" && value !== null && ArrayBuffer.isView(value);
499
+ var isVoiceClientMessage = (value) => {
500
+ if (!value || typeof value !== "object" || !("type" in value)) {
501
+ return false;
502
+ }
503
+ switch (value.type) {
504
+ case "close":
505
+ return true;
506
+ case "end_turn":
507
+ return true;
508
+ case "ping":
509
+ return true;
510
+ case "start":
511
+ return !("sessionId" in value) || typeof value.sessionId === "string";
512
+ default:
513
+ return false;
514
+ }
515
+ };
516
+ var parseClientMessage = (raw) => {
517
+ if (typeof raw === "string") {
518
+ try {
519
+ const parsed = JSON.parse(raw);
520
+ return isVoiceClientMessage(parsed) ? parsed : null;
521
+ } catch {
522
+ return null;
523
+ }
524
+ }
525
+ if (isVoiceClientMessage(raw)) {
526
+ return raw;
527
+ }
528
+ return null;
529
+ };
530
+ var resolveSessionId = (runtime, ws) => {
531
+ const existing = runtime.socketSessions.get(ws);
532
+ if (existing) {
533
+ return existing;
534
+ }
535
+ const query = ws.data && typeof ws.data === "object" && "query" in ws.data ? ws.data.query : undefined;
536
+ const providedSessionId = typeof query?.sessionId === "string" && query.sessionId.trim() ? query.sessionId.trim() : createId();
537
+ runtime.socketSessions.set(ws, providedSessionId);
538
+ return providedSessionId;
539
+ };
540
+ var toAudioChunk = (raw) => {
541
+ if (raw instanceof ArrayBuffer) {
542
+ return raw;
543
+ }
544
+ if (isArrayBufferView(raw)) {
545
+ return raw;
546
+ }
547
+ return null;
548
+ };
549
+ var createSocketAdapter = (ws) => ({
550
+ close: async (code, reason) => {
551
+ ws.close(code, reason);
552
+ },
553
+ send: async (data) => {
554
+ ws.send(data);
555
+ }
556
+ });
557
+ var voice = (config) => {
558
+ const runtime = {
559
+ activeSessions: new Map,
560
+ logger: resolveLogger(config.logger),
561
+ socketSessions: new WeakMap
562
+ };
563
+ return new Elysia({ name: "absolutejs-voice" }).ws(config.path, {
564
+ close: async (ws, code, reason) => {
565
+ const sessionId = runtime.socketSessions.get(ws);
566
+ if (!sessionId) {
567
+ return;
568
+ }
569
+ const session = runtime.activeSessions.get(sessionId);
570
+ runtime.activeSessions.delete(sessionId);
571
+ if (session) {
572
+ await session.disconnect({
573
+ code,
574
+ reason,
575
+ recoverable: true,
576
+ type: "close"
577
+ });
578
+ }
579
+ },
580
+ message: async (ws, raw) => {
581
+ const sessionId = resolveSessionId(runtime, ws);
582
+ const current = runtime.activeSessions.get(sessionId);
583
+ const message = parseClientMessage(raw);
584
+ if (message) {
585
+ if (message.type === "ping") {
586
+ ws.send(JSON.stringify({ type: "pong" }));
587
+ }
588
+ if (message.type === "end_turn" && current) {
589
+ await current.commitTurn("manual");
590
+ }
591
+ if (message.type === "close" && current) {
592
+ await current.close(message.reason);
593
+ runtime.activeSessions.delete(sessionId);
594
+ }
595
+ if (message.type === "start" && message.sessionId && message.sessionId !== sessionId) {
596
+ runtime.socketSessions.set(ws, message.sessionId);
597
+ }
598
+ return;
599
+ }
600
+ const audio = toAudioChunk(raw);
601
+ if (!audio) {
602
+ return;
603
+ }
604
+ const session = current ?? createVoiceSession({
605
+ context: ws.data,
606
+ id: sessionId,
607
+ logger: config.logger,
608
+ reconnect: {
609
+ maxAttempts: config.reconnect?.maxAttempts ?? 10,
610
+ strategy: config.reconnect?.strategy ?? "resume-last-turn",
611
+ timeout: config.reconnect?.timeout ?? 30000
612
+ },
613
+ route: {
614
+ onComplete: config.onComplete,
615
+ onError: config.onError,
616
+ onSession: config.onSession,
617
+ onTurn: config.onTurn
618
+ },
619
+ socket: createSocketAdapter(ws),
620
+ store: config.session,
621
+ stt: config.stt,
622
+ turnDetection: {
623
+ silenceMs: config.turnDetection?.silenceMs ?? 700
624
+ }
625
+ });
626
+ if (!current) {
627
+ runtime.activeSessions.set(sessionId, session);
628
+ await session.connect(createSocketAdapter(ws));
629
+ }
630
+ await session.receiveAudio(audio);
631
+ },
632
+ open: async (ws) => {
633
+ const sessionId = resolveSessionId(runtime, ws);
634
+ const existing = runtime.activeSessions.get(sessionId);
635
+ if (existing) {
636
+ await existing.close("superseded");
637
+ runtime.activeSessions.delete(sessionId);
638
+ }
639
+ const session = createVoiceSession({
640
+ context: ws.data,
641
+ id: sessionId,
642
+ logger: config.logger,
643
+ reconnect: {
644
+ maxAttempts: config.reconnect?.maxAttempts ?? 10,
645
+ strategy: config.reconnect?.strategy ?? "resume-last-turn",
646
+ timeout: config.reconnect?.timeout ?? 30000
647
+ },
648
+ route: {
649
+ onComplete: config.onComplete,
650
+ onError: config.onError,
651
+ onSession: config.onSession,
652
+ onTurn: config.onTurn
653
+ },
654
+ socket: createSocketAdapter(ws),
655
+ store: config.session,
656
+ stt: config.stt,
657
+ turnDetection: {
658
+ silenceMs: config.turnDetection?.silenceMs ?? 700
659
+ }
660
+ });
661
+ runtime.activeSessions.set(sessionId, session);
662
+ await session.connect(createSocketAdapter(ws));
663
+ }
664
+ });
665
+ };
666
+ // src/memoryStore.ts
667
+ var createVoiceMemoryStore = () => {
668
+ const sessions = new Map;
669
+ const get = async (id) => sessions.get(id);
670
+ const getOrCreate = async (id) => {
671
+ let session = sessions.get(id);
672
+ if (!session) {
673
+ session = createVoiceSessionRecord(id);
674
+ sessions.set(id, session);
675
+ }
676
+ return session;
677
+ };
678
+ const set = async (id, value) => {
679
+ sessions.set(id, value);
680
+ };
681
+ const list = async () => Array.from(sessions.values()).map((session) => toVoiceSessionSummary(session)).sort((first, second) => (second.lastActivityAt ?? second.createdAt) - (first.lastActivityAt ?? first.createdAt));
682
+ const remove = async (id) => {
683
+ sessions.delete(id);
684
+ };
685
+ return { get, getOrCreate, list, remove, set };
686
+ };
687
+ export {
688
+ voice,
689
+ createVoiceSessionRecord,
690
+ createVoiceSession,
691
+ createVoiceMemoryStore,
692
+ createId
693
+ };