@openclaw/inworld-speech 2026.7.1 → 2026.7.2-beta.2

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.
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_INWORLD_MODEL_ID, INWORLD_TTS_MODELS, inworldTTS, listInworldVoices, normalizeInworldBaseUrl } from "./tts.js";
2
2
  import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
3
- import { asObject, parseSpeechDirectiveNumberOverride, trimToUndefined } from "openclaw/plugin-sdk/speech-core";
3
+ import { asObject, parseSpeechDirectiveNumberOverride, resolveSpeechProviderApiKey, trimToUndefined } from "openclaw/plugin-sdk/speech-core";
4
4
  import { asFiniteNumberInRange } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  //#region extensions/inworld/speech-provider.ts
6
6
  function normalizeInworldTemperature(value) {
@@ -33,6 +33,9 @@ function readInworldProviderConfig(config) {
33
33
  temperature: normalizeInworldTemperature(config.temperature) ?? defaults.temperature
34
34
  };
35
35
  }
36
+ function resolveInworldApiKey(primary, fallback) {
37
+ return resolveSpeechProviderApiKey(primary, fallback, process.env.INWORLD_API_KEY);
38
+ }
36
39
  function readInworldOverrides(overrides) {
37
40
  if (!overrides) return {};
38
41
  return {
@@ -107,18 +110,19 @@ function buildInworldSpeechProvider() {
107
110
  }),
108
111
  listVoices: async (req) => {
109
112
  const config = req.providerConfig ? readInworldProviderConfig(req.providerConfig) : void 0;
110
- const apiKey = req.apiKey || config?.apiKey || process.env.INWORLD_API_KEY;
113
+ const apiKey = resolveInworldApiKey(req.apiKey, config?.apiKey);
111
114
  if (!apiKey) throw new Error("Inworld API key missing");
112
115
  return listInworldVoices({
113
116
  apiKey,
114
- baseUrl: req.baseUrl ?? config?.baseUrl
117
+ baseUrl: req.baseUrl ?? config?.baseUrl,
118
+ timeoutMs: req.timeoutMs
115
119
  });
116
120
  },
117
- isConfigured: ({ providerConfig }) => Boolean(readInworldProviderConfig(providerConfig).apiKey || process.env.INWORLD_API_KEY),
121
+ isConfigured: ({ providerConfig }) => Boolean(resolveInworldApiKey(readInworldProviderConfig(providerConfig).apiKey)),
118
122
  synthesize: async (req) => {
119
123
  const config = readInworldProviderConfig(req.providerConfig);
120
124
  const overrides = readInworldOverrides(req.providerOverrides);
121
- const apiKey = config.apiKey || process.env.INWORLD_API_KEY;
125
+ const apiKey = resolveInworldApiKey(config.apiKey);
122
126
  if (!apiKey) throw new Error("Inworld API key missing");
123
127
  const useOpus = req.target === "voice-note";
124
128
  const audioEncoding = useOpus ? "OGG_OPUS" : "MP3";
@@ -141,7 +145,7 @@ function buildInworldSpeechProvider() {
141
145
  synthesizeTelephony: async (req) => {
142
146
  const config = readInworldProviderConfig(req.providerConfig);
143
147
  const overrides = readInworldOverrides(req.providerOverrides);
144
- const apiKey = config.apiKey || process.env.INWORLD_API_KEY;
148
+ const apiKey = resolveInworldApiKey(config.apiKey);
145
149
  if (!apiKey) throw new Error("Inworld API key missing");
146
150
  const sampleRate = 22050;
147
151
  return {
package/dist/tts.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { MAX_AUDIO_BYTES } from "openclaw/plugin-sdk/media-runtime";
2
2
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
3
3
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
4
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
4
5
  //#region extensions/inworld/tts.ts
5
6
  const DEFAULT_INWORLD_BASE_URL = "https://api.inworld.ai";
6
7
  const DEFAULT_INWORLD_VOICE_ID = "Sarah";
7
8
  const DEFAULT_INWORLD_MODEL_ID = "inworld-tts-1.5-max";
8
9
  const INWORLD_TTS_BODY_MAX_BYTES = MAX_AUDIO_BYTES * 2;
9
10
  const INWORLD_VOICES_BODY_MAX_BYTES = MAX_AUDIO_BYTES;
10
- const INWORLD_BODY_READ_IDLE_TIMEOUT_MS = 3e4;
11
+ const INWORLD_UPSTREAM_IDLE_TIMEOUT_MS = 3e4;
11
12
  const INWORLD_ERROR_BODY_MAX_BYTES = 8 * 1024;
12
13
  const INWORLD_ERROR_BODY_MAX_CHARS = 400;
13
14
  const INWORLD_ERROR_BODY_READ_IDLE_TIMEOUT_MS = 1e4;
@@ -34,7 +35,7 @@ async function readInworldErrorBodySnippet(response) {
34
35
  return error instanceof InworldErrorBodyOverflow ? "(error body exceeded diagnostic limit; truncated)" : "";
35
36
  }
36
37
  const collapsed = buffer.toString("utf8").replace(/\s+/g, " ").trim();
37
- if (collapsed.length > INWORLD_ERROR_BODY_MAX_CHARS) return `${collapsed.slice(0, INWORLD_ERROR_BODY_MAX_CHARS)}…`;
38
+ if (collapsed.length > INWORLD_ERROR_BODY_MAX_CHARS) return `${truncateUtf16Safe(collapsed, INWORLD_ERROR_BODY_MAX_CHARS)}…`;
38
39
  return collapsed;
39
40
  }
40
41
  const INWORLD_TTS_MODELS = [
@@ -93,7 +94,7 @@ async function inworldTTS(params) {
93
94
  throw new Error(`Inworld TTS API error (${response.status}): ${errorBody}`);
94
95
  }
95
96
  const body = (await readResponseWithLimit(response, INWORLD_TTS_BODY_MAX_BYTES, {
96
- chunkTimeoutMs: INWORLD_BODY_READ_IDLE_TIMEOUT_MS,
97
+ chunkTimeoutMs: INWORLD_UPSTREAM_IDLE_TIMEOUT_MS,
97
98
  onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`Inworld TTS audio stream too large: ${size} bytes (limit: ${maxBytes} bytes)`),
98
99
  onIdleTimeout: ({ chunkTimeoutMs }) => /* @__PURE__ */ new Error(`Inworld TTS audio stream stalled: no data received for ${chunkTimeoutMs}ms`)
99
100
  })).toString("utf8");
@@ -106,7 +107,7 @@ async function inworldTTS(params) {
106
107
  try {
107
108
  parsed = JSON.parse(trimmed);
108
109
  } catch {
109
- throw new Error(`Inworld TTS stream parse error: unexpected non-JSON line: ${trimmed.slice(0, 80)}`);
110
+ throw new Error(`Inworld TTS stream parse error: unexpected non-JSON line: ${truncateUtf16Safe(trimmed, 80)}`);
110
111
  }
111
112
  if (parsed.error) throw new Error(`Inworld TTS stream error (${parsed.error.code}): ${parsed.error.message}`);
112
113
  if (parsed.result?.audioContent) {
@@ -131,7 +132,7 @@ async function listInworldVoices(params) {
131
132
  method: "GET",
132
133
  headers: { Authorization: `Basic ${params.apiKey}` }
133
134
  },
134
- timeoutMs: params.timeoutMs,
135
+ timeoutMs: params.timeoutMs ?? INWORLD_UPSTREAM_IDLE_TIMEOUT_MS,
135
136
  policy: ssrfPolicyFromInworldBaseUrl(baseUrl),
136
137
  auditContext: "inworld-voices"
137
138
  });
@@ -141,7 +142,7 @@ async function listInworldVoices(params) {
141
142
  throw new Error(`Inworld voices API error (${response.status}): ${errorBody}`);
142
143
  }
143
144
  const voicesBody = (await readResponseWithLimit(response, INWORLD_VOICES_BODY_MAX_BYTES, {
144
- chunkTimeoutMs: INWORLD_BODY_READ_IDLE_TIMEOUT_MS,
145
+ chunkTimeoutMs: INWORLD_UPSTREAM_IDLE_TIMEOUT_MS,
145
146
  onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`Inworld voices response too large: ${size} bytes (limit: ${maxBytes} bytes)`),
146
147
  onIdleTimeout: ({ chunkTimeoutMs }) => /* @__PURE__ */ new Error(`Inworld voices response stalled: no data received for ${chunkTimeoutMs}ms`)
147
148
  })).toString("utf8");
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/inworld-speech",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/inworld-speech",
9
- "version": "2026.7.1"
9
+ "version": "2026.7.2-beta.2"
10
10
  }
11
11
  }
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/inworld-speech",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "description": "OpenClaw Inworld speech plugin.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,10 +18,10 @@
18
18
  "minHostVersion": ">=2026.6.8"
19
19
  },
20
20
  "compat": {
21
- "pluginApi": ">=2026.7.1"
21
+ "pluginApi": ">=2026.7.2-beta.2"
22
22
  },
23
23
  "build": {
24
- "openclawVersion": "2026.7.1",
24
+ "openclawVersion": "2026.7.2-beta.2",
25
25
  "bundledDist": false
26
26
  },
27
27
  "release": {
@@ -39,7 +39,7 @@
39
39
  "README.md"
40
40
  ],
41
41
  "peerDependencies": {
42
- "openclaw": ">=2026.7.1"
42
+ "openclaw": ">=2026.7.2-beta.2"
43
43
  },
44
44
  "peerDependenciesMeta": {
45
45
  "openclaw": {