@openclaw/voice-call 2026.9.1-beta.1 → 2026.9.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.
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-ptAKgGER.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-gE2Yd4V8.js";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -1,19 +1,6 @@
1
1
  import { asFiniteNumber, asOptionalRecord, isRecord, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
2
2
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
3
  //#region extensions/voice-call/src/config-migration.ts
4
- /** Merge legacy provider-specific values into the canonical providers map. */
5
- function mergeProviderConfig(providersValue, providerId, compatValues) {
6
- if (Object.keys(compatValues).length === 0) return asOptionalRecord(providersValue);
7
- const providers = asOptionalRecord(providersValue) ?? {};
8
- const existing = asOptionalRecord(providers[providerId]) ?? {};
9
- return {
10
- ...providers,
11
- [providerId]: {
12
- ...existing,
13
- ...compatValues
14
- }
15
- };
16
- }
17
4
  /** Migrate legacy voice-call config input to the current canonical shape. */
18
5
  function migrateVoiceCallLegacyConfigInput(params) {
19
6
  const raw = asOptionalRecord(params.value) ?? {};
@@ -22,28 +9,71 @@ function migrateVoiceCallLegacyConfigInput(params) {
22
9
  const twilio = asOptionalRecord(raw.twilio);
23
10
  const streaming = asOptionalRecord(raw.streaming);
24
11
  const configPathPrefix = params.configPathPrefix ?? "plugins.entries.voice-call.config";
25
- const legacyStreamingOpenAICompat = {};
26
- const streamingOpenAIApiKey = readStringField(streaming, "openaiApiKey");
27
- if (streamingOpenAIApiKey) legacyStreamingOpenAICompat.apiKey = streamingOpenAIApiKey;
28
- const streamingSttModel = readStringField(streaming, "sttModel");
29
- if (streamingSttModel) legacyStreamingOpenAICompat.model = streamingSttModel;
30
- const streamingSilenceDurationMs = asFiniteNumber(streaming?.silenceDurationMs);
31
- if (streamingSilenceDurationMs !== void 0) legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
32
- const streamingVadThreshold = asFiniteNumber(streaming?.vadThreshold);
33
- if (streamingVadThreshold !== void 0) legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
12
+ const changes = [];
13
+ if (raw.provider === "log") changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
14
+ if (typeof twilio?.from === "string") {
15
+ const source = `${configPathPrefix}.twilio.from`;
16
+ const target = `${configPathPrefix}.fromNumber`;
17
+ changes.push(raw.fromNumber != null ? `Removed ${source} (kept ${target}).` : `Moved ${source} → ${target}.`);
18
+ }
34
19
  const streamingProvider = readStringField(streaming, "provider");
35
20
  const legacyStreamingProvider = readStringField(streaming, "sttProvider");
36
21
  const normalizedStreaming = streaming ? {
37
22
  ...streaming,
38
- provider: streamingProvider ?? legacyStreamingProvider,
39
- providers: mergeProviderConfig(streaming.providers, "openai", legacyStreamingOpenAICompat)
23
+ provider: streamingProvider ?? legacyStreamingProvider
40
24
  } : void 0;
41
25
  if (normalizedStreaming) {
42
26
  delete normalizedStreaming.sttProvider;
43
- delete normalizedStreaming.openaiApiKey;
44
- delete normalizedStreaming.sttModel;
45
- delete normalizedStreaming.silenceDurationMs;
46
- delete normalizedStreaming.vadThreshold;
27
+ if (legacyStreamingProvider !== void 0) {
28
+ const source = `${configPathPrefix}.streaming.sttProvider`;
29
+ const target = `${configPathPrefix}.streaming.provider`;
30
+ changes.push(streamingProvider !== void 0 ? `Removed ${source} (kept ${target}).` : `Moved ${source} → ${target}.`);
31
+ }
32
+ for (const [legacyKey, canonicalKey, value] of [
33
+ [
34
+ "openaiApiKey",
35
+ "apiKey",
36
+ readStringField(streaming, "openaiApiKey")
37
+ ],
38
+ [
39
+ "sttModel",
40
+ "model",
41
+ readStringField(streaming, "sttModel")
42
+ ],
43
+ [
44
+ "silenceDurationMs",
45
+ "silenceDurationMs",
46
+ asFiniteNumber(streaming?.silenceDurationMs)
47
+ ],
48
+ [
49
+ "vadThreshold",
50
+ "vadThreshold",
51
+ asFiniteNumber(streaming?.vadThreshold)
52
+ ]
53
+ ]) {
54
+ if (!Object.hasOwn(normalizedStreaming, legacyKey)) continue;
55
+ delete normalizedStreaming[legacyKey];
56
+ const source = `${configPathPrefix}.streaming.${legacyKey}`;
57
+ if (value === void 0 || value === "") {
58
+ changes.push(`Removed invalid ${source}.`);
59
+ continue;
60
+ }
61
+ const providers = asOptionalRecord(normalizedStreaming.providers);
62
+ const existing = asOptionalRecord(providers?.openai);
63
+ const target = `${configPathPrefix}.streaming.providers.openai.${canonicalKey}`;
64
+ if (existing?.[canonicalKey] !== void 0) {
65
+ changes.push(`Removed ${source} (kept ${target}).`);
66
+ continue;
67
+ }
68
+ normalizedStreaming.providers = {
69
+ ...providers,
70
+ openai: {
71
+ ...existing,
72
+ [canonicalKey]: value
73
+ }
74
+ };
75
+ changes.push(`Moved ${source} → ${target}.`);
76
+ }
47
77
  }
48
78
  const normalizedTwilio = twilio ? { ...twilio } : void 0;
49
79
  if (normalizedTwilio) delete normalizedTwilio.from;
@@ -61,16 +91,6 @@ function migrateVoiceCallLegacyConfigInput(params) {
61
91
  streaming: normalizedStreaming,
62
92
  realtime: normalizedRealtime
63
93
  };
64
- const changes = [];
65
- if (raw.provider === "log") changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
66
- if (typeof twilio?.from === "string" && typeof raw.fromNumber !== "string") changes.push(`Moved ${configPathPrefix}.twilio.from → ${configPathPrefix}.fromNumber.`);
67
- if (typeof streaming?.sttProvider === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttProvider → ${configPathPrefix}.streaming.provider.`);
68
- if (typeof streaming?.openaiApiKey === "string") changes.push(`Moved ${configPathPrefix}.streaming.openaiApiKey → ${configPathPrefix}.streaming.providers.openai.apiKey.`);
69
- if (typeof streaming?.sttModel === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`);
70
- if (asFiniteNumber(streaming?.silenceDurationMs) !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`);
71
- else if (typeof streaming?.silenceDurationMs === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
72
- if (asFiniteNumber(streaming?.vadThreshold) !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`);
73
- else if (typeof streaming?.vadThreshold === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.vadThreshold.`);
74
94
  if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) changes.push(`Removed ${configPathPrefix}.realtime.agentContext.includeSystemPrompt.`);
75
95
  return {
76
96
  config,
@@ -133,14 +133,7 @@ const { setRuntime: setVoiceCallStateRuntime, tryGetRuntime: getOptionalVoiceCal
133
133
  //#region extensions/voice-call/src/manager/replay-keys.ts
134
134
  /** Match the existing voice-call webhook replay-cache cardinality. */
135
135
  const MAX_MANAGER_REPLAY_KEYS = 1e4;
136
- function pruneOldestSetEntries(keys, maxEntries) {
137
- while (keys.size > maxEntries) {
138
- const oldest = keys.values().next().value;
139
- if (oldest === void 0) break;
140
- keys.delete(oldest);
141
- }
142
- }
143
- function pruneOldestMapEntries(keys, maxEntries) {
136
+ function pruneOldestEntries(keys, maxEntries) {
144
137
  while (keys.size > maxEntries) {
145
138
  const oldest = keys.keys().next().value;
146
139
  if (oldest === void 0) break;
@@ -150,7 +143,7 @@ function pruneOldestMapEntries(keys, maxEntries) {
150
143
  /** Remember one manager replay key without refreshing duplicate insertion order. */
151
144
  function rememberManagerReplayKey(keys, key, maxEntries = MAX_MANAGER_REPLAY_KEYS) {
152
145
  keys.add(key);
153
- pruneOldestSetEntries(keys, maxEntries);
146
+ pruneOldestEntries(keys, maxEntries);
154
147
  }
155
148
  /** Append one per-call replay key and retain only the newest bounded suffix. */
156
149
  function appendCallReplayKey(keys, key, maxEntries = 500) {
@@ -170,7 +163,7 @@ function reserveRejectedProviderCall(calls, providerCallId, maxEntries = MAX_MAN
170
163
  if (calls.has(providerCallId)) return;
171
164
  const reservation = Symbol(providerCallId);
172
165
  calls.set(providerCallId, reservation);
173
- pruneOldestMapEntries(calls, maxEntries);
166
+ pruneOldestEntries(calls, maxEntries);
174
167
  return reservation;
175
168
  }
176
169
  /** Release a rejected-call reservation only when the failing attempt still owns it. */
@@ -189,7 +182,7 @@ const MAX_CALL_RECORD_EVENTS = 1e3;
189
182
  const CALL_RECORD_EVENT_META_MAX_ENTRIES = 1100;
190
183
  const CALL_RECORD_CHUNK_MAX_ENTRIES = 48048;
191
184
  /** Raw UTF-8 bytes stored per call record chunk before base64 encoding. */
192
- const RAW_CALL_RECORD_CHUNK_BYTES = 47 * 1024;
185
+ const RAW_CALL_RECORD_CHUNK_BYTES = 48128;
193
186
  let callRecordEventSequence = 0;
194
187
  /** Return the pre-SQLite JSONL call log path for migration/compat checks. */
195
188
  function resolveVoiceCallLegacyCallLogPath(storePath) {
@@ -205,7 +198,7 @@ function resolvePluginStateEnv(storePath) {
205
198
  /** Open the plugin state stores when the runtime is available. */
206
199
  function createCallRecordStateStores(storePath) {
207
200
  const runtime = getOptionalVoiceCallStateRuntime();
208
- if (!runtime) return null;
201
+ if (!runtime) throw new Error("Voice Call state runtime not initialized");
209
202
  const env = resolvePluginStateEnv(storePath);
210
203
  return {
211
204
  events: runtime.state.openSyncKeyedStore({
@@ -392,7 +385,6 @@ function readCallRecordEvents(stores) {
392
385
  function persistCallRecord(storePath, call) {
393
386
  try {
394
387
  const stores = createCallRecordStateStores(storePath);
395
- if (!stores) throw new Error("Voice Call state runtime not initialized");
396
388
  const order = nextCallRecordOrder();
397
389
  registerCallRecordEvent(stores, buildNewEventKey(order), call, order);
398
390
  } catch (err) {
@@ -444,13 +436,11 @@ function readCallHistoryFromStore(storePath) {
444
436
  }
445
437
  return [];
446
438
  }
447
- /** Find the newest retained snapshots matching each call identifier namespace. */
448
- async function findCallMatchesInStore(storePath, callId) {
449
- const calls = readCallHistoryFromStore(storePath);
450
- return {
451
- byCallId: calls.findLast((call) => call.callId === callId),
452
- byProviderCallId: calls.findLast((call) => call.providerCallId === callId)
453
- };
439
+ /** Resolve an internal ID or retained provider alias to its newest logical call snapshot. */
440
+ function findCallInStore(storePath, callId) {
441
+ const calls = readCallRecordEvents(createCallRecordStateStores(storePath));
442
+ const match = calls.findLast((call) => call.callId === callId) ?? calls.findLast((call) => call.providerCallId === callId);
443
+ return match ? calls.findLast((call) => call.callId === match.callId) : void 0;
454
444
  }
455
445
  /** Return the newest persisted call history rows up to the requested limit. */
456
446
  async function getCallHistoryFromStore(storePath, limit = 50) {
@@ -464,4 +454,4 @@ function resolveDefaultVoiceCallStoreDir(env = process.env) {
464
454
  return path.join(resolveStateDir(env), "voice-calls");
465
455
  }
466
456
  //#endregion
467
- export { releaseRejectedProviderCall as _, CALL_RECORD_EVENT_META_MAX_ENTRIES as a, setVoiceCallStateRuntime as b, buildVoiceCallLegacyJsonlEventKey as c, loadActiveCallsFromStore as d, parseVoiceCallRecordLine as f, appendCallReplayKey as g, resolveVoiceCallLegacyCallLogPath as h, CALL_RECORD_EVENT_CHUNKS_NAMESPACE as i, findCallMatchesInStore as l, prepareVoiceCallRecordForStorage as m, CALL_RECORD_CHUNK_MAX_ENTRIES as n, MAX_CALL_RECORD_EVENTS as o, persistCallRecord as p, CALL_RECORD_EVENTS_NAMESPACE as r, RAW_CALL_RECORD_CHUNK_BYTES as s, resolveDefaultVoiceCallStoreDir as t, getCallHistoryFromStore as u, rememberManagerReplayKey as v, TerminalStates as x, reserveRejectedProviderCall as y };
457
+ export { releaseRejectedProviderCall as _, CALL_RECORD_EVENT_META_MAX_ENTRIES as a, setVoiceCallStateRuntime as b, buildVoiceCallLegacyJsonlEventKey as c, loadActiveCallsFromStore as d, parseVoiceCallRecordLine as f, appendCallReplayKey as g, resolveVoiceCallLegacyCallLogPath as h, CALL_RECORD_EVENT_CHUNKS_NAMESPACE as i, findCallInStore as l, prepareVoiceCallRecordForStorage as m, CALL_RECORD_CHUNK_MAX_ENTRIES as n, MAX_CALL_RECORD_EVENTS as o, persistCallRecord as p, CALL_RECORD_EVENTS_NAMESPACE as r, RAW_CALL_RECORD_CHUNK_BYTES as s, resolveDefaultVoiceCallStoreDir as t, getCallHistoryFromStore as u, rememberManagerReplayKey as v, TerminalStates as x, reserveRejectedProviderCall as y };
@@ -1,4 +1,4 @@
1
- import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BmMfqKzQ.js";
1
+ import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-9je4i7qf.js";
2
2
  import crypto from "node:crypto";
3
3
  //#region extensions/voice-call/src/providers/telnyx.ts
4
4
  function normalizeTelnyxDirection(direction) {
@@ -1,8 +1,8 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
3
  import { n as requireSupportedTwilioApiHostname, r as resolveTwilioApiBaseUrl } from "./twilio-region-4fkgz3UG.js";
4
- import { _ as escapeXml, a as normalizeProviderStatus, i as mapProviderStatusToEndReason, p as chunkAudio, r as isProviderStatusTerminal, s as getHeader, v as mapVoiceToPolly } from "./runtime-entry-ptAKgGER.js";
5
- import { c as verifyTwilioWebhook, i as readVoiceCallProviderJsonResponse, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-BmMfqKzQ.js";
4
+ import { a as normalizeProviderStatus, i as mapProviderStatusToEndReason, p as chunkAudio, r as isProviderStatusTerminal, s as getHeader, v as escapeXml, y as mapVoiceToPolly } from "./runtime-entry-gE2Yd4V8.js";
5
+ import { c as verifyTwilioWebhook, i as readVoiceCallProviderJsonResponse, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-9je4i7qf.js";
6
6
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
8
8
  import crypto from "node:crypto";
@@ -218,8 +218,9 @@ var TwilioProvider = class TwilioProvider {
218
218
  this.callStreamMap.set(callSid, streamSid);
219
219
  this.activeStreamCalls.add(callSid);
220
220
  }
221
- hasRegisteredStream(callSid) {
222
- return this.callStreamMap.has(callSid);
221
+ hasRegisteredStream(callSid, streamSid) {
222
+ const current = this.callStreamMap.get(callSid);
223
+ return current !== void 0 && (streamSid === void 0 || current === streamSid);
223
224
  }
224
225
  unregisterCallStream(callSid, streamSid) {
225
226
  const currentStreamSid = this.callStreamMap.get(callSid);
@@ -1,6 +1,6 @@
1
1
  import { isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
2
2
  //#region extensions/voice-call/src/webhook-replay.ts
3
- const REPLAY_WINDOW_MS = 600 * 1e3;
3
+ const REPLAY_WINDOW_MS = 6e5;
4
4
  const REPLAY_CACHE_MAX_ENTRIES = 1e4;
5
5
  const REPLAY_CACHE_PRUNE_INTERVAL = 64;
6
6
  function createWebhookReplayCache() {
@@ -1,4 +1,4 @@
1
- const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\.)*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i;
1
+ const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\[\x20-\x7e])*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i;
2
2
  /**
3
3
  * Returns true if the value is an Email
4
4
  * @specification https://datatracker.ietf.org/doc/html/rfc5322
@@ -1,6 +1,9 @@
1
1
  import * as FormatBidi from './format/bidi.mjs';
2
2
  import * as LabelUnicode from './label/unicode.mjs';
3
3
  import * as LabelPuny from './label/puny.mjs';
4
+ // ------------------------------------------------------------------
5
+ // IsLabel
6
+ // ------------------------------------------------------------------
4
7
  function IsValidLabelLength(value) {
5
8
  return value.length > 0 && value.length <= 63;
6
9
  }
@@ -8,9 +11,24 @@ function IsLabel(value) {
8
11
  return IsValidLabelLength(value) && (LabelPuny.IsPunyLabel(value) ||
9
12
  LabelUnicode.IsUnicodeLabel(value));
10
13
  }
14
+ // ------------------------------------------------------------------
15
+ // NormalizeHostname
16
+ //
17
+ // Normalizes a hostname for label validation. Normalizes for NFC
18
+ // such that equivalent codepoint sequences can be uniformly
19
+ // compared. It also removes codepoints that IDNA mapping ignores,
20
+ // and converts full stop variants to ASCII '.' so the hostname can
21
+ // be split into labels.
22
+ // ------------------------------------------------------------------
11
23
  function NormalizeHostname(value) {
12
- return value.normalize('NFC').replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.');
24
+ return value
25
+ .normalize('NFC')
26
+ .replace(/[\u00ad\u034f\u180b-\u180d\u200b\ufe00-\ufe0f\u{e0100}-\u{e01ef}]/gu, '') // RE_IGNORED_MAPPING
27
+ .replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.'); // RE_FULL_STOP_MAPPING
13
28
  }
29
+ // ------------------------------------------------------------------
30
+ // IsIdnHostname
31
+ // ------------------------------------------------------------------
14
32
  export function IsIdnHostname(value) {
15
33
  if (value.length === 0 || value.includes(' '))
16
34
  return false;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "typebox",
3
3
  "description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
4
- "version": "1.3.16",
4
+ "version": "1.3.17",
5
5
  "keywords": [
6
6
  "typescript",
7
7
  "jsonschema"
@@ -242,7 +242,7 @@
242
242
  },
243
243
  "agentId": {
244
244
  "label": "Response Agent ID",
245
- "help": "Agent workspace used for voice response generation. Defaults to \"main\".",
245
+ "help": "Agent used for voice responses and session storage. Defaults to the sole configured agent; set explicitly for a multi-agent fleet.",
246
246
  "advanced": true
247
247
  },
248
248
  "sessionScope": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.9.1-beta.1",
3
+ "version": "2026.9.1",
4
4
  "description": "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,12 +8,12 @@
8
8
  },
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "typebox": "1.3.16",
11
+ "typebox": "1.3.17",
12
12
  "ws": "8.21.3",
13
13
  "zod": "4.4.3"
14
14
  },
15
15
  "peerDependencies": {
16
- "openclaw": ">=2026.9.1-beta.1"
16
+ "openclaw": ">=2026.9.1"
17
17
  },
18
18
  "peerDependenciesMeta": {
19
19
  "openclaw": {
@@ -24,16 +24,17 @@
24
24
  "extensions": [
25
25
  "./index.ts"
26
26
  ],
27
+ "setupEntry": "./dist/setup-api.js",
27
28
  "install": {
28
29
  "npmSpec": "@openclaw/voice-call",
29
30
  "defaultChoice": "npm",
30
31
  "minHostVersion": ">=2026.4.10"
31
32
  },
32
33
  "compat": {
33
- "pluginApi": ">=2026.9.1-beta.1"
34
+ "pluginApi": ">=2026.9.1"
34
35
  },
35
36
  "build": {
36
- "openclawVersion": "2026.9.1-beta.1"
37
+ "openclawVersion": "2026.9.1"
37
38
  },
38
39
  "release": {
39
40
  "publishToClawHub": true,
@@ -41,7 +42,8 @@
41
42
  },
42
43
  "runtimeExtensions": [
43
44
  "./dist/index.js"
44
- ]
45
+ ],
46
+ "runtimeSetupEntry": "./dist/setup-api.js"
45
47
  },
46
48
  "files": [
47
49
  "dist/**",