@elizaos/plugin-elizacloud 2.0.0-beta.1 → 2.0.11-beta.7

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 (285) hide show
  1. package/README.md +20 -44
  2. package/auto-enable.ts +10 -5
  3. package/dist/browser/index.browser.js +2 -2
  4. package/dist/browser/index.browser.js.map +4 -4
  5. package/dist/cjs/index.node.cjs +2874 -5915
  6. package/dist/cjs/index.node.js.map +47 -116
  7. package/dist/cloud/auth-service-types.d.ts +8 -0
  8. package/dist/cloud/auth-service-types.d.ts.map +1 -0
  9. package/dist/cloud/auth-service-types.js +36 -0
  10. package/dist/cloud/auth-service-types.js.map +10 -0
  11. package/dist/cloud/auth.js +4 -51
  12. package/dist/cloud/auth.js.map +4 -4
  13. package/dist/cloud/base-url.d.ts +6 -2
  14. package/dist/cloud/base-url.d.ts.map +1 -1
  15. package/dist/cloud/base-url.js +3 -51
  16. package/dist/cloud/base-url.js.map +3 -3
  17. package/dist/cloud/bridge-client.d.ts +3 -3
  18. package/dist/cloud/bridge-client.d.ts.map +1 -1
  19. package/dist/cloud/bridge-client.js +3 -51
  20. package/dist/cloud/bridge-client.js.map +3 -3
  21. package/dist/cloud/clack-observer.d.ts +35 -0
  22. package/dist/cloud/clack-observer.d.ts.map +1 -0
  23. package/dist/cloud/clack-observer.js +143 -0
  24. package/dist/cloud/clack-observer.js.map +10 -0
  25. package/dist/cloud/cloud-manager.js +45 -92
  26. package/dist/cloud/cloud-manager.js.map +6 -6
  27. package/dist/cloud/cloud-wallet.js +2 -4835
  28. package/dist/cloud/cloud-wallet.js.map +3 -82
  29. package/dist/cloud/duffel-client.d.ts +181 -0
  30. package/dist/cloud/duffel-client.d.ts.map +1 -0
  31. package/dist/cloud/duffel-client.js +506 -0
  32. package/dist/cloud/duffel-client.js.map +11 -0
  33. package/dist/cloud/index.d.ts +6 -0
  34. package/dist/cloud/index.d.ts.map +1 -1
  35. package/dist/cloud/index.js +1782 -1
  36. package/dist/cloud/index.js.map +18 -3
  37. package/dist/cloud/lifeops-schedule-sync-client.d.ts +43 -0
  38. package/dist/cloud/lifeops-schedule-sync-client.d.ts.map +1 -0
  39. package/dist/cloud/lifeops-schedule-sync-client.js +180 -0
  40. package/dist/cloud/lifeops-schedule-sync-client.js.map +11 -0
  41. package/dist/cloud/lifeops-schedule-sync-contracts.d.ts +89 -0
  42. package/dist/cloud/lifeops-schedule-sync-contracts.d.ts.map +1 -0
  43. package/dist/cloud/lifeops-schedule-sync-contracts.js +39 -0
  44. package/dist/cloud/lifeops-schedule-sync-contracts.js.map +10 -0
  45. package/dist/cloud/managed-payment-clients.d.ts +166 -0
  46. package/dist/cloud/managed-payment-clients.d.ts.map +1 -0
  47. package/dist/cloud/managed-payment-clients.js +238 -0
  48. package/dist/cloud/managed-payment-clients.js.map +11 -0
  49. package/dist/cloud/null-observer.d.ts +35 -0
  50. package/dist/cloud/null-observer.d.ts.map +1 -0
  51. package/dist/cloud/null-observer.js +45 -0
  52. package/dist/cloud/null-observer.js.map +10 -0
  53. package/dist/cloud/setup-observer.d.ts +98 -0
  54. package/dist/cloud/setup-observer.d.ts.map +1 -0
  55. package/dist/cloud/setup-observer.js +2 -0
  56. package/dist/cloud/setup-observer.js.map +9 -0
  57. package/dist/cloud/validate-url.d.ts.map +1 -1
  58. package/dist/cloud/validate-url.js +2 -1
  59. package/dist/cloud/validate-url.js.map +3 -3
  60. package/dist/cloud/x402-payment-handler.d.ts +85 -0
  61. package/dist/cloud/x402-payment-handler.d.ts.map +1 -0
  62. package/dist/cloud/x402-payment-handler.js +119 -0
  63. package/dist/cloud/x402-payment-handler.js.map +10 -0
  64. package/dist/cloud-setup.d.ts +36 -0
  65. package/dist/cloud-setup.d.ts.map +1 -0
  66. package/dist/{onboarding.js → cloud-setup.js} +139 -139
  67. package/dist/cloud-setup.js.map +14 -0
  68. package/dist/cloud-voice-catalog.d.ts +65 -0
  69. package/dist/cloud-voice-catalog.d.ts.map +1 -0
  70. package/dist/cloud-voice-catalog.js +278 -0
  71. package/dist/cloud-voice-catalog.js.map +12 -0
  72. package/dist/index.browser.d.ts +11 -0
  73. package/dist/index.browser.d.ts.map +1 -1
  74. package/dist/index.d.ts +7 -1
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +5416 -8405
  77. package/dist/index.js.map +48 -116
  78. package/dist/index.node.d.ts +8 -1
  79. package/dist/index.node.d.ts.map +1 -1
  80. package/dist/init.js +17 -4
  81. package/dist/init.js.map +4 -4
  82. package/dist/lib/cloud-connection.d.ts +0 -1
  83. package/dist/lib/cloud-connection.d.ts.map +1 -1
  84. package/dist/lib/cloud-connection.js +14 -91
  85. package/dist/lib/cloud-connection.js.map +7 -7
  86. package/dist/lib/cloud-secrets.d.ts +5 -18
  87. package/dist/lib/cloud-secrets.d.ts.map +1 -1
  88. package/dist/lib/cloud-secrets.js +8 -36
  89. package/dist/lib/cloud-secrets.js.map +3 -3
  90. package/dist/lib/config-like.d.ts +1 -1
  91. package/dist/lib/config-like.d.ts.map +1 -1
  92. package/dist/lib/config-like.js +3 -3
  93. package/dist/lib/config-like.js.map +3 -3
  94. package/dist/lib/credential-type-map.d.ts +1 -1
  95. package/dist/lib/credential-type-map.js.map +1 -1
  96. package/dist/lib/http.d.ts +0 -11
  97. package/dist/lib/http.d.ts.map +1 -1
  98. package/dist/lib/http.js.map +2 -2
  99. package/dist/lib/server-cloud-tts.d.ts +12 -25
  100. package/dist/lib/server-cloud-tts.d.ts.map +1 -1
  101. package/dist/lib/server-cloud-tts.js +31 -329
  102. package/dist/lib/server-cloud-tts.js.map +4 -7
  103. package/dist/lib/tts-debug.d.ts +5 -3
  104. package/dist/lib/tts-debug.d.ts.map +1 -1
  105. package/dist/lib/tts-debug.js +1 -34
  106. package/dist/lib/tts-debug.js.map +3 -4
  107. package/dist/models/embeddings.d.ts.map +1 -1
  108. package/dist/models/embeddings.js +79 -69
  109. package/dist/models/embeddings.js.map +6 -6
  110. package/dist/models/image.d.ts.map +1 -1
  111. package/dist/models/image.js +42 -15
  112. package/dist/models/image.js.map +6 -6
  113. package/dist/models/index.js +676 -166
  114. package/dist/models/index.js.map +11 -12
  115. package/dist/models/research.d.ts.map +1 -1
  116. package/dist/models/research.js +24 -7
  117. package/dist/models/research.js.map +6 -6
  118. package/dist/models/speech.d.ts +61 -3
  119. package/dist/models/speech.d.ts.map +1 -1
  120. package/dist/models/speech.js +173 -17
  121. package/dist/models/speech.js.map +5 -5
  122. package/dist/models/text.d.ts +106 -1
  123. package/dist/models/text.d.ts.map +1 -1
  124. package/dist/models/text.js +452 -82
  125. package/dist/models/text.js.map +7 -8
  126. package/dist/models/tokenization.d.ts.map +1 -1
  127. package/dist/models/tokenization.js.map +2 -2
  128. package/dist/models/transcription.d.ts.map +1 -1
  129. package/dist/models/transcription.js +20 -6
  130. package/dist/models/transcription.js.map +5 -5
  131. package/dist/node/index.node.js +2828 -5838
  132. package/dist/node/index.node.js.map +47 -116
  133. package/dist/plugin.d.ts.map +1 -1
  134. package/dist/plugin.js +376 -5050
  135. package/dist/plugin.js.map +16 -92
  136. package/dist/providers/openai.js +11 -2
  137. package/dist/providers/openai.js.map +3 -3
  138. package/dist/register-routes.js +376 -5050
  139. package/dist/register-routes.js.map +16 -92
  140. package/dist/routes/cloud-billing-routes.d.ts.map +1 -1
  141. package/dist/routes/cloud-billing-routes.js +17 -60
  142. package/dist/routes/cloud-billing-routes.js.map +8 -7
  143. package/dist/routes/cloud-coding-container-routes.d.ts +8 -0
  144. package/dist/routes/cloud-coding-container-routes.d.ts.map +1 -0
  145. package/dist/routes/cloud-coding-container-routes.js +214 -0
  146. package/dist/routes/cloud-coding-container-routes.js.map +11 -0
  147. package/dist/routes/cloud-compat-routes.d.ts.map +1 -1
  148. package/dist/routes/cloud-compat-routes.js +17 -60
  149. package/dist/routes/cloud-compat-routes.js.map +8 -7
  150. package/dist/routes/cloud-features-routes.js +2 -2
  151. package/dist/routes/cloud-features-routes.js.map +4 -4
  152. package/dist/routes/cloud-relay-routes.d.ts +2 -1
  153. package/dist/routes/cloud-relay-routes.d.ts.map +1 -1
  154. package/dist/routes/cloud-relay-routes.js +84 -2
  155. package/dist/routes/cloud-relay-routes.js.map +5 -4
  156. package/dist/routes/cloud-routes-autonomous.d.ts +3 -4
  157. package/dist/routes/cloud-routes-autonomous.d.ts.map +1 -1
  158. package/dist/routes/cloud-routes-autonomous.js +11 -4893
  159. package/dist/routes/cloud-routes-autonomous.js.map +8 -87
  160. package/dist/routes/cloud-routes.d.ts +2 -2
  161. package/dist/routes/cloud-routes.d.ts.map +1 -1
  162. package/dist/routes/cloud-routes.js +343 -5058
  163. package/dist/routes/cloud-routes.js.map +13 -90
  164. package/dist/routes/cloud-status-routes-autonomous.d.ts +1 -2
  165. package/dist/routes/cloud-status-routes-autonomous.d.ts.map +1 -1
  166. package/dist/routes/cloud-status-routes-autonomous.js +4 -51
  167. package/dist/routes/cloud-status-routes-autonomous.js.map +5 -5
  168. package/dist/routes/cloud-status-routes.js +14 -90
  169. package/dist/routes/cloud-status-routes.js.map +7 -7
  170. package/dist/routes/home-remote-runner-access-url.d.ts +16 -0
  171. package/dist/routes/home-remote-runner-access-url.d.ts.map +1 -0
  172. package/dist/routes/home-remote-runner-access-url.js +91 -0
  173. package/dist/routes/home-remote-runner-access-url.js.map +10 -0
  174. package/dist/routes/travel-provider-relay-routes.d.ts +9 -0
  175. package/dist/routes/travel-provider-relay-routes.d.ts.map +1 -0
  176. package/dist/routes/travel-provider-relay-routes.js +358 -0
  177. package/dist/routes/travel-provider-relay-routes.js.map +14 -0
  178. package/dist/services/cloud-auth.d.ts +1 -1
  179. package/dist/services/cloud-auth.d.ts.map +1 -1
  180. package/dist/services/cloud-auth.js +7 -2
  181. package/dist/services/cloud-auth.js.map +4 -4
  182. package/dist/services/cloud-backup.js.map +2 -2
  183. package/dist/services/cloud-bootstrap.d.ts.map +1 -1
  184. package/dist/services/cloud-bootstrap.js.map +2 -2
  185. package/dist/services/cloud-bridge.js.map +3 -3
  186. package/dist/services/cloud-container.d.ts +5 -1
  187. package/dist/services/cloud-container.d.ts.map +1 -1
  188. package/dist/services/cloud-container.js +52 -1
  189. package/dist/services/cloud-container.js.map +4 -4
  190. package/dist/services/cloud-credential-provider.js.map +2 -2
  191. package/dist/services/cloud-model-registry.js.map +2 -2
  192. package/dist/types/cloud.d.ts +1 -0
  193. package/dist/types/cloud.d.ts.map +1 -1
  194. package/dist/types/cloud.js.map +2 -2
  195. package/dist/types/index.d.ts +1 -1
  196. package/dist/types/index.d.ts.map +1 -1
  197. package/dist/utils/cloud-sdk/client.d.ts.map +1 -1
  198. package/dist/utils/cloud-sdk/client.js +136 -4
  199. package/dist/utils/cloud-sdk/client.js.map +5 -5
  200. package/dist/utils/cloud-sdk/http.js.map +1 -1
  201. package/dist/utils/cloud-sdk/public-routes.d.ts +186 -0
  202. package/dist/utils/cloud-sdk/public-routes.d.ts.map +1 -1
  203. package/dist/utils/cloud-sdk/public-routes.js +99 -1
  204. package/dist/utils/cloud-sdk/public-routes.js.map +3 -3
  205. package/dist/utils/cloud-sdk/types.d.ts +0 -2
  206. package/dist/utils/cloud-sdk/types.d.ts.map +1 -1
  207. package/dist/utils/cloud-sdk/types.js.map +1 -1
  208. package/dist/utils/config.d.ts +10 -1
  209. package/dist/utils/config.d.ts.map +1 -1
  210. package/dist/utils/config.js +12 -2
  211. package/dist/utils/config.js.map +3 -3
  212. package/dist/utils/events.d.ts +23 -2
  213. package/dist/utils/events.d.ts.map +1 -1
  214. package/dist/utils/events.js +5 -3
  215. package/dist/utils/events.js.map +3 -3
  216. package/dist/utils/sdk-client.d.ts.map +1 -1
  217. package/dist/utils/sdk-client.js +17 -4
  218. package/dist/utils/sdk-client.js.map +4 -4
  219. package/dist/utils/waifu-metering.d.ts +108 -0
  220. package/dist/utils/waifu-metering.d.ts.map +1 -0
  221. package/dist/utils/waifu-metering.js +166 -0
  222. package/dist/utils/waifu-metering.js.map +10 -0
  223. package/package.json +51 -22
  224. package/src/cloud/auth-service-types.ts +24 -0
  225. package/src/cloud/base-url.ts +6 -62
  226. package/src/cloud/clack-observer.ts +189 -0
  227. package/src/cloud/duffel-client.ts +847 -0
  228. package/src/cloud/index.ts +10 -0
  229. package/src/cloud/lifeops-schedule-sync-client.ts +245 -0
  230. package/src/cloud/lifeops-schedule-sync-contracts.ts +124 -0
  231. package/src/cloud/managed-payment-clients.ts +374 -0
  232. package/src/cloud/null-observer.ts +45 -0
  233. package/src/cloud/setup-observer.ts +125 -0
  234. package/src/cloud/validate-url.ts +7 -1
  235. package/src/cloud/x402-payment-handler.ts +215 -0
  236. package/src/cloud-setup.ts +531 -0
  237. package/src/cloud-voice-catalog.test.ts +254 -0
  238. package/src/cloud-voice-catalog.ts +246 -0
  239. package/src/index.browser.ts +29 -0
  240. package/src/index.node.ts +31 -1
  241. package/src/index.ts +76 -4
  242. package/src/lib/cloud-connection.ts +2 -4
  243. package/src/lib/cloud-secrets.ts +10 -54
  244. package/src/lib/config-like.ts +1 -1
  245. package/src/lib/credential-type-map.ts +2 -2
  246. package/src/lib/http.ts +0 -17
  247. package/src/lib/server-cloud-tts.ts +33 -341
  248. package/src/lib/tts-debug.ts +5 -34
  249. package/src/models/embeddings.ts +140 -76
  250. package/src/models/image.ts +29 -14
  251. package/src/models/research.ts +11 -1
  252. package/src/models/speech.ts +269 -23
  253. package/src/models/text.ts +704 -110
  254. package/src/models/tokenization.ts +2 -2
  255. package/src/models/transcription.ts +7 -3
  256. package/src/plugin.ts +38 -0
  257. package/src/routes/cloud-billing-routes.ts +4 -14
  258. package/src/routes/cloud-coding-container-routes.ts +198 -0
  259. package/src/routes/cloud-compat-routes.ts +4 -14
  260. package/src/routes/cloud-features-routes.ts +1 -1
  261. package/src/routes/cloud-relay-routes.ts +47 -1
  262. package/src/routes/cloud-routes-autonomous.ts +7 -10
  263. package/src/routes/cloud-routes.ts +68 -7
  264. package/src/routes/cloud-status-routes-autonomous.ts +6 -2
  265. package/src/routes/home-remote-runner-access-url.ts +83 -0
  266. package/src/routes/travel-provider-relay-routes.ts +193 -0
  267. package/src/services/cloud-auth.ts +9 -2
  268. package/src/services/cloud-bootstrap.ts +1 -3
  269. package/src/services/cloud-bridge.ts +1 -1
  270. package/src/services/cloud-container.ts +93 -0
  271. package/src/services/cloud-credential-provider.ts +1 -1
  272. package/src/services/cloud-model-registry.ts +1 -1
  273. package/src/types/cloud.ts +22 -0
  274. package/src/types/index.ts +19 -0
  275. package/src/utils/cloud-sdk/client.ts +42 -3
  276. package/src/utils/cloud-sdk/public-routes.ts +168 -0
  277. package/src/utils/cloud-sdk/types.ts +0 -2
  278. package/src/utils/config.ts +20 -1
  279. package/src/utils/events.ts +30 -2
  280. package/src/utils/sdk-client.ts +5 -1
  281. package/src/utils/waifu-metering.ts +302 -0
  282. package/dist/onboarding.d.ts +0 -35
  283. package/dist/onboarding.d.ts.map +0 -1
  284. package/dist/onboarding.js.map +0 -14
  285. package/src/onboarding.ts +0 -396
@@ -1,30 +1,136 @@
1
1
  import type { Readable } from "node:stream";
2
- import type { IAgentRuntime } from "@elizaos/core";
3
- import { logger } from "@elizaos/core";
2
+ import type { AudioStreamResult, IAgentRuntime } from "@elizaos/core";
3
+ import { isCloudConnected, logger, toRuntimeSettings } from "@elizaos/core";
4
4
  import type { OpenAITextToSpeechParams } from "../types";
5
- import { getSetting, isBrowser } from "../utils/config";
5
+ import { getSetting, isBrowser, resolveCloudTimeoutMs } from "../utils/config";
6
6
  import { webStreamToNodeStream } from "../utils/helpers";
7
7
  import { createElizaCloudClient } from "../utils/sdk-client";
8
8
 
9
+ /**
10
+ * Narrow client interface the speech handler actually exercises. Lets tests
11
+ * substitute a fake without rebuilding the full SDK surface.
12
+ */
13
+ export interface CloudTtsClient {
14
+ routes: {
15
+ postApiV1VoiceTts<T = unknown>(options: {
16
+ headers?: Record<string, unknown>;
17
+ json: { text: string; voiceId?: string; modelId?: string };
18
+ timeoutMs?: number;
19
+ }): Promise<T>;
20
+ };
21
+ }
22
+
23
+ type CloudTtsClientFactory = (runtime: IAgentRuntime) => CloudTtsClient;
24
+
25
+ let cloudTtsClientFactory: CloudTtsClientFactory = (runtime) =>
26
+ createElizaCloudClient(runtime) as unknown as CloudTtsClient;
27
+
28
+ /**
29
+ * Test seam: substitute the SDK client factory used by `handleTextToSpeech`.
30
+ * Pass `null` to reset to the real `createElizaCloudClient`. Production code
31
+ * should never call this.
32
+ */
33
+ export function setCloudTtsClientFactoryForTesting(
34
+ factory: CloudTtsClientFactory | null,
35
+ ): void {
36
+ if (factory === null) {
37
+ cloudTtsClientFactory = (runtime) =>
38
+ createElizaCloudClient(runtime) as unknown as CloudTtsClient;
39
+ } else {
40
+ cloudTtsClientFactory = factory;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Extended TTS params accepted by the cloud handler.
46
+ *
47
+ * The runtime canonical type (`TextToSpeechParams`) only carries `text`, but
48
+ * the cloud TTS upstream maps to ElevenLabs and therefore accepts arbitrary
49
+ * ElevenLabs `voiceId` + `modelId`. We accept those via either OpenAI-style
50
+ * (`voice`, `model`) or ElevenLabs-style (`voiceId`, `modelId`) fields and
51
+ * normalize them to the upstream shape.
52
+ */
53
+ export interface CloudTextToSpeechParams extends OpenAITextToSpeechParams {
54
+ voiceId?: string;
55
+ modelId?: string;
56
+ }
57
+
58
+ /**
59
+ * Marker error used so the runtime can fall through to the next TTS handler
60
+ * (e.g. local omnivoice) when Eliza Cloud is not connected.
61
+ */
62
+ export class CloudTtsUnavailableError extends Error {
63
+ constructor(message = "Eliza Cloud is not connected") {
64
+ super(message);
65
+ this.name = "CloudTtsUnavailableError";
66
+ }
67
+ }
68
+
69
+ function normalizeTextInput(
70
+ input: string | CloudTextToSpeechParams | OpenAITextToSpeechParams,
71
+ ): CloudTextToSpeechParams {
72
+ if (typeof input === "string") return { text: input };
73
+ return input as CloudTextToSpeechParams;
74
+ }
75
+
76
+ /**
77
+ * Pull an ElevenLabs `modelId` out of (in order):
78
+ * 1. options.modelId — explicit ElevenLabs model id
79
+ * 2. options.model with `elevenlabs/` prefix or `eleven_*` shape
80
+ *
81
+ * Returns `undefined` when nothing usable was provided so the upstream
82
+ * can apply its own default (currently `eleven_flash_v2_5`).
83
+ */
84
+ function resolveModelId(
85
+ options: CloudTextToSpeechParams,
86
+ ): string | undefined {
87
+ if (options.modelId && options.modelId.trim()) {
88
+ return options.modelId.trim();
89
+ }
90
+ const model = options.model?.trim();
91
+ if (!model) return undefined;
92
+ if (model.startsWith("elevenlabs/")) {
93
+ return model.split("/").slice(1).join("/");
94
+ }
95
+ if (model.startsWith("eleven_")) {
96
+ return model;
97
+ }
98
+ return undefined;
99
+ }
100
+
101
+ /**
102
+ * Pull an ElevenLabs `voiceId` out of (in order):
103
+ * 1. options.voiceId — explicit ElevenLabs voice id (preferred)
104
+ * 2. options.voice — OpenAI-style voice name (rejected unless it looks
105
+ * like an ElevenLabs id, i.e. neither an OpenAI alias nor "nova")
106
+ *
107
+ * Returns `undefined` when nothing usable was provided so the upstream
108
+ * can apply its own default voice.
109
+ */
110
+ function resolveVoiceId(
111
+ options: CloudTextToSpeechParams,
112
+ ): string | undefined {
113
+ if (options.voiceId && options.voiceId.trim()) {
114
+ return options.voiceId.trim();
115
+ }
116
+ const voice = options.voice?.trim();
117
+ if (!voice) return undefined;
118
+ // "nova" is the OpenAI default — treat as unset so the upstream falls back
119
+ // to the cloud default voice instead of being forwarded as an opaque alias.
120
+ if (voice === "nova") return undefined;
121
+ return voice;
122
+ }
123
+
9
124
  async function fetchTextToSpeech(
10
125
  runtime: IAgentRuntime,
11
- options: OpenAITextToSpeechParams
126
+ options: CloudTextToSpeechParams,
12
127
  ): Promise<ReadableStream<Uint8Array> | Readable> {
13
- const defaultModel = getSetting(runtime, "ELIZAOS_CLOUD_TTS_MODEL", "gpt-5-mini-tts");
14
- const defaultVoice = getSetting(runtime, "ELIZAOS_CLOUD_TTS_VOICE", "nova");
15
-
16
- const model = options.model || (defaultModel as string);
17
- const voice = options.voice || (defaultVoice as string);
18
128
  const format = options.format || "mp3";
19
- const modelId = model.startsWith("elevenlabs/")
20
- ? model.split("/").slice(1).join("/")
21
- : model.startsWith("eleven_")
22
- ? model
23
- : undefined;
24
- const voiceId = voice && voice !== "nova" ? voice : undefined;
129
+ const modelId = resolveModelId(options);
130
+ const voiceId = resolveVoiceId(options);
25
131
 
26
132
  try {
27
- const res = await createElizaCloudClient(runtime).routes.postApiV1VoiceTts({
133
+ const res = (await cloudTtsClientFactory(runtime).routes.postApiV1VoiceTts({
28
134
  headers: {
29
135
  ...(format === "mp3" ? { Accept: "audio/mpeg" } : {}),
30
136
  },
@@ -33,7 +139,8 @@ async function fetchTextToSpeech(
33
139
  ...(voiceId ? { voiceId } : {}),
34
140
  ...(modelId ? { modelId } : {}),
35
141
  },
36
- });
142
+ timeoutMs: resolveCloudTimeoutMs("ELIZAOS_CLOUD_TTS_TIMEOUT_MS", 60_000),
143
+ })) as Response;
37
144
 
38
145
  if (!res.ok) {
39
146
  const err = await res.text();
@@ -55,19 +162,158 @@ async function fetchTextToSpeech(
55
162
  }
56
163
  }
57
164
 
165
+ function toUint8Array(chunk: unknown): Uint8Array {
166
+ if (chunk instanceof Uint8Array) return chunk;
167
+ if (chunk instanceof ArrayBuffer) return new Uint8Array(chunk);
168
+ if (typeof chunk === "string") return new TextEncoder().encode(chunk);
169
+ throw new TypeError(`Unexpected TTS chunk type: ${typeof chunk}`);
170
+ }
171
+
172
+ function concatChunks(chunks: Uint8Array[]): Uint8Array {
173
+ const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
174
+ const out = new Uint8Array(total);
175
+ let offset = 0;
176
+ for (const chunk of chunks) {
177
+ out.set(chunk, offset);
178
+ offset += chunk.byteLength;
179
+ }
180
+ return out;
181
+ }
182
+
183
+ async function webStreamToUint8Array(
184
+ stream: ReadableStream<Uint8Array>,
185
+ ): Promise<Uint8Array> {
186
+ const reader = stream.getReader();
187
+ const chunks: Uint8Array[] = [];
188
+ try {
189
+ while (true) {
190
+ const result = await reader.read();
191
+ if (result.done) break;
192
+ chunks.push(toUint8Array(result.value));
193
+ }
194
+ } finally {
195
+ reader.releaseLock();
196
+ }
197
+ return concatChunks(chunks);
198
+ }
199
+
200
+ async function nodeStreamToUint8Array(stream: Readable): Promise<Uint8Array> {
201
+ const chunks: Uint8Array[] = [];
202
+ for await (const chunk of stream) {
203
+ chunks.push(toUint8Array(chunk));
204
+ }
205
+ return concatChunks(chunks);
206
+ }
207
+
208
+ function isReadableStream(
209
+ stream: ReadableStream<Uint8Array> | Readable,
210
+ ): stream is ReadableStream<Uint8Array> {
211
+ return typeof (stream as { getReader?: unknown }).getReader === "function";
212
+ }
213
+
214
+ async function ttsStreamToBytes(
215
+ stream: ReadableStream<Uint8Array> | Readable,
216
+ ): Promise<Uint8Array> {
217
+ if (isReadableStream(stream)) {
218
+ return webStreamToUint8Array(stream);
219
+ }
220
+ return nodeStreamToUint8Array(stream);
221
+ }
222
+
223
+ /**
224
+ * Wrap the upstream TTS byte stream as an {@link AudioStreamResult}: `audioStream`
225
+ * yields each chunk as it arrives (so playback can start on the first byte
226
+ * instead of draining the whole clip via {@link ttsStreamToBytes}), and `bytes`
227
+ * resolves to the full concatenated audio once the stream is consumed.
228
+ */
229
+ function buildAudioStreamResult(
230
+ stream: ReadableStream<Uint8Array> | Readable,
231
+ mimeType: string,
232
+ ): AudioStreamResult {
233
+ const collected: Uint8Array[] = [];
234
+ let resolveBytes!: (value: Uint8Array) => void;
235
+ let rejectBytes!: (reason: unknown) => void;
236
+ const bytes = new Promise<Uint8Array>((resolve, reject) => {
237
+ resolveBytes = resolve;
238
+ rejectBytes = reject;
239
+ });
240
+ async function* generate(): AsyncGenerator<Uint8Array> {
241
+ try {
242
+ if (isReadableStream(stream)) {
243
+ const reader = stream.getReader();
244
+ try {
245
+ for (;;) {
246
+ const { value, done } = await reader.read();
247
+ if (done) break;
248
+ const chunk = toUint8Array(value);
249
+ collected.push(chunk);
250
+ yield chunk;
251
+ }
252
+ } finally {
253
+ reader.releaseLock();
254
+ }
255
+ } else {
256
+ for await (const value of stream) {
257
+ const chunk = toUint8Array(value);
258
+ collected.push(chunk);
259
+ yield chunk;
260
+ }
261
+ }
262
+ resolveBytes(concatChunks(collected));
263
+ } catch (err) {
264
+ rejectBytes(err);
265
+ throw err;
266
+ }
267
+ }
268
+ return { audioStream: generate(), bytes, mimeType };
269
+ }
270
+
271
+ /**
272
+ * TEXT_TO_SPEECH handler for plugin-elizacloud.
273
+ *
274
+ * Behavior:
275
+ * - When Eliza Cloud is **not** connected, throws `CloudTtsUnavailableError`
276
+ * so the runtime's model-handler fallback chain can pick the next
277
+ * provider (e.g. local omnivoice, ElevenLabs direct, etc.).
278
+ * - When connected, forwards `text`, `voiceId`, and `modelId` to the
279
+ * upstream cloud TTS proxy and returns the audio stream.
280
+ *
281
+ * Accepts both OpenAI-style (`voice` / `model`) and ElevenLabs-style
282
+ * (`voiceId` / `modelId`) input fields. ElevenLabs-style wins when both are
283
+ * present.
284
+ */
58
285
  export async function handleTextToSpeech(
59
286
  runtime: IAgentRuntime,
60
- input: string | OpenAITextToSpeechParams
61
- ): Promise<ReadableStream<Uint8Array> | Readable> {
62
- const options: OpenAITextToSpeechParams =
63
- typeof input === "string" ? { text: input } : (input as OpenAITextToSpeechParams);
287
+ input: string | CloudTextToSpeechParams | OpenAITextToSpeechParams,
288
+ ): Promise<Uint8Array | AudioStreamResult> {
289
+ if (!isCloudConnected(toRuntimeSettings(runtime))) {
290
+ throw new CloudTtsUnavailableError(
291
+ "Eliza Cloud is not connected — falling through to next TTS handler",
292
+ );
293
+ }
294
+
295
+ const options = normalizeTextInput(input);
296
+ // Explicit opt-in only (NOT the generic `stream` that useModel auto-injects
297
+ // from an ambient text-streaming turn) so byte-expecting callers like the
298
+ // GENERATE_MEDIA action keep getting a buffer.
299
+ const wantsStream =
300
+ typeof input === "object" &&
301
+ input !== null &&
302
+ (input as { audioStream?: boolean }).audioStream === true;
64
303
 
65
304
  const resolvedModel =
66
- options.model || (getSetting(runtime, "ELIZAOS_CLOUD_TTS_MODEL", "gpt-5-mini-tts") as string);
305
+ options.modelId ||
306
+ options.model ||
307
+ (getSetting(runtime, "ELIZAOS_CLOUD_TTS_MODEL", "eleven_flash_v2_5") as string);
67
308
  logger.log(`[ELIZAOS_CLOUD] Using TEXT_TO_SPEECH model: ${resolvedModel}`);
68
309
  try {
69
310
  const speechStream = await fetchTextToSpeech(runtime, options);
70
- return speechStream;
311
+ if (wantsStream) {
312
+ const format = options.format || "mp3";
313
+ const mimeType = format === "mp3" ? "audio/mpeg" : `audio/${format}`;
314
+ return buildAudioStreamResult(speechStream, mimeType);
315
+ }
316
+ return ttsStreamToBytes(speechStream);
71
317
  } catch (error) {
72
318
  const message = error instanceof Error ? error.message : String(error);
73
319
  logger.error(`Error in TEXT_TO_SPEECH: ${message}`);