@openclaw/voice-call 2026.10.2 → 2026.10.5-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.
package/dist/index.js CHANGED
@@ -1,17 +1,18 @@
1
1
  import { definePluginEntry } from "./api.js";
2
2
  import { VOICE_CALL_CLI_DESCRIPTOR } from "./cli-output-mode.js";
3
- import { i as resolveVoiceCallConfig, t as VoiceCallConfigSchema, u as validateProviderConfig } from "./.setup/config-CX9J-053.mjs";
4
- import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-CbSKxSKk.mjs";
3
+ import { g as CallBriefSchema, i as resolveVoiceCallConfig, t as VoiceCallConfigSchema, u as validateProviderConfig } from "./.setup/config-CsrGuamE.mjs";
4
+ import { C as TerminalStates, t as createVoiceCallRuntime } from "./.setup/runtime-entry-STjFmi_B.mjs";
5
+ import { AsyncLocalStorage } from "node:async_hooks";
5
6
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
7
  import { ErrorCodes, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
7
8
  import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton";
8
9
  import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
9
10
  import { resolvePluginServiceScheduler } from "openclaw/plugin-sdk/runtime";
10
- import { asNonArrayRecord, asOptionalRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
+ import { asNonArrayRecord, asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
12
  import { jsonResult } from "openclaw/plugin-sdk/tool-results";
12
- import { Type } from "typebox";
13
13
  import { resolveTimerTimeoutMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
14
14
  import { randomUUID } from "node:crypto";
15
+ import { Type } from "typebox";
15
16
  //#region extensions/voice-call/src/command-service.ts
16
17
  var VoiceCallCommandInputError = class extends Error {};
17
18
  function toVoiceCallStatus(call) {
@@ -76,9 +77,12 @@ function createVoiceCallCommandService(ensureRuntime) {
76
77
  async initiate(params, missingToMessage = "to required") {
77
78
  const rt = await ensureRuntime();
78
79
  const to = requireInput(params.to ?? rt.config.toNumber, missingToMessage);
80
+ const parsedBrief = CallBriefSchema.optional().safeParse(params.brief);
81
+ if (!parsedBrief.success) throw new VoiceCallCommandInputError(`Invalid brief: ${parsedBrief.error.message}`);
79
82
  const result = await rt.manager.initiateCall(to, params.sessionKey, {
80
83
  message: params.message,
81
84
  mode: params.mode,
85
+ brief: parsedBrief.data,
82
86
  dtmfSequence: params.dtmfSequence,
83
87
  ...params.requesterSessionKey ? { requesterSessionKey: params.requesterSessionKey } : {},
84
88
  ...params.agentId ? { agentId: params.agentId } : {}
@@ -92,6 +96,30 @@ function createVoiceCallCommandService(ensureRuntime) {
92
96
  async continueCall(callId, message) {
93
97
  return await (await prepareContinue(callId, message)).run();
94
98
  },
99
+ async steer(params) {
100
+ const callId = requireInput(params.callId, "callId and message required");
101
+ const message = requireInput(params.message?.trim(), "callId and message required");
102
+ if (message.length > 500) throw new VoiceCallCommandInputError("Steering message must be at most 500 characters");
103
+ const rt = await ensureRuntime();
104
+ params.assertCurrent?.();
105
+ const call = rt.manager.getCall(callId);
106
+ if (!call || TerminalStates.has(call.state)) throw new VoiceCallCommandInputError("Call is not active");
107
+ const requester = call.metadata?.requesterSessionKey;
108
+ if (!params.operator && (!params.requesterSessionKey || requester !== params.requesterSessionKey)) throw new VoiceCallCommandInputError("Only the requester session or an operator may steer this call");
109
+ if (!rt.config.realtime.enabled) throw new VoiceCallCommandInputError("Steering requires an active realtime call");
110
+ await rt.manager.updateCallMetadata(call, (metadata) => ({
111
+ ...metadata,
112
+ ownerInstructions: [...Array.isArray(metadata?.ownerInstructions) ? metadata.ownerInstructions : [], message].slice(-8)
113
+ }), {
114
+ persist: true,
115
+ assertCurrent: params.assertCurrent
116
+ });
117
+ params.assertCurrent?.();
118
+ if (rt.manager.getCall(callId) !== call || TerminalStates.has(call.state)) throw new VoiceCallCommandInputError("Call is not active");
119
+ const instruction = params.mode === "say" ? `Owner instruction for this call: say this now, verbatim, then listen. Answer: ${JSON.stringify(message)}` : `Owner instruction for this call: ${JSON.stringify(message)}. Apply this guidance from now on while respecting the call brief's disclosure and approval limits.`;
120
+ requireSuccess(rt.webhookServer.speakRealtime(callId, instruction), "Steering failed");
121
+ return { success: true };
122
+ },
95
123
  async speak(params) {
96
124
  const request = await resolveCallMessage(params.callId, params.message);
97
125
  if (request.rt.config.realtime.enabled) {
@@ -111,9 +139,11 @@ function createVoiceCallCommandService(ensureRuntime) {
111
139
  requireSuccess(await (await ensureRuntime()).manager.sendDtmf(resolvedCallId, resolvedDigits), "dtmf failed");
112
140
  return { success: true };
113
141
  },
114
- async endCall(callId) {
142
+ async endCall(callId, execution) {
115
143
  const resolvedCallId = requireInput(callId, "callId required");
116
- requireSuccess(await (await ensureRuntime()).manager.endCall(resolvedCallId), "end failed");
144
+ const rt = execution?.runtime ?? await ensureRuntime();
145
+ execution?.assertAuthority?.();
146
+ requireSuccess(await rt.manager.endCall(resolvedCallId), "end failed");
117
147
  return { success: true };
118
148
  },
119
149
  async status(callId) {
@@ -193,27 +223,62 @@ function createVoiceCallContinueOperationStore(params) {
193
223
  };
194
224
  }
195
225
  //#endregion
196
- //#region extensions/voice-call/index.ts
197
- const VOICE_CALL_WRITE_METHOD_SCOPE = { scope: "operator.write" };
198
- const VOICE_CALL_READ_METHOD_SCOPE = { scope: "operator.read" };
199
- const voiceCallConfigSchema = { parse(value) {
200
- const config = asOptionalRecord(value) ?? {};
201
- const enabled = typeof config.enabled === "boolean" ? config.enabled : true;
202
- return VoiceCallConfigSchema.parse({
203
- ...config,
204
- enabled,
205
- provider: config.provider ?? (enabled ? "mock" : void 0)
206
- });
207
- } };
226
+ //#region extensions/voice-call/src/tool-call-scope.ts
227
+ async function resolveActiveVoiceCallToolScope(params) {
228
+ if (!isRecord(params.binding)) return;
229
+ const binding = params.binding;
230
+ const callId = normalizeOptionalString(binding.callId);
231
+ if (binding.kind !== "active-call" || !callId) return;
232
+ if (params.action !== "end_call") throw new VoiceCallCommandInputError("This realtime consult may only end_call.");
233
+ const requestedCallId = normalizeOptionalString(params.requestedCallId);
234
+ if (requestedCallId && requestedCallId !== callId) throw new VoiceCallCommandInputError(`This realtime consult is bound to call ${callId}.`);
235
+ params.assertAuthority();
236
+ params.signal?.throwIfAborted();
237
+ const activeCall = await params.runtime.manager.getCallForStream(callId);
238
+ if (!activeCall || activeCall.callId !== callId) throw new VoiceCallCommandInputError("The bound realtime call is no longer active.");
239
+ return {
240
+ callId,
241
+ execution: {
242
+ runtime: params.runtime,
243
+ assertAuthority: params.assertAuthority
244
+ }
245
+ };
246
+ }
247
+ //#endregion
248
+ //#region extensions/voice-call/src/tool-schema.ts
249
+ const VoiceCallBriefToolSchema = Type.Object({
250
+ task: Type.Optional(Type.String({ maxLength: 2e3 })),
251
+ context: Type.Optional(Type.String({ maxLength: 4e3 })),
252
+ language: Type.Optional(Type.String({ maxLength: 100 })),
253
+ identity: Type.Optional(Type.Union([Type.String({ maxLength: 500 }), Type.Object({
254
+ introduction: Type.String({ maxLength: 500 }),
255
+ disclose: Type.Optional(Type.Union([Type.Literal("volunteer"), Type.Literal("when-asked")]))
256
+ }, { additionalProperties: false })])),
257
+ disclosures: Type.Optional(Type.Array(Type.String({ maxLength: 500 }), { maxItems: 20 })),
258
+ approvals: Type.Optional(Type.String({ maxLength: 2e3 })),
259
+ voicemailMessage: Type.Optional(Type.String({ maxLength: 1e3 })),
260
+ successCriteria: Type.Optional(Type.String({ maxLength: 1e3 })),
261
+ maxDurationSeconds: Type.Optional(Type.Integer({ minimum: 1 }))
262
+ }, { additionalProperties: false });
208
263
  const VoiceCallToolSchema = Type.Union([
209
264
  Type.Object({
210
265
  action: Type.Literal("initiate_call"),
266
+ brief: Type.Optional(VoiceCallBriefToolSchema),
211
267
  to: Type.Optional(Type.String({ description: "Call target" })),
212
268
  message: Type.String({ description: "Intro message" }),
213
269
  mode: Type.Optional(Type.Union([Type.Literal("notify"), Type.Literal("conversation")])),
214
270
  sessionKey: Type.Optional(Type.String({ description: "OpenClaw session key for the call" })),
215
271
  dtmfSequence: Type.Optional(Type.String({ description: "DTMF digits to play before connect" }))
216
272
  }),
273
+ Type.Object({
274
+ action: Type.Literal("steer_call"),
275
+ callId: Type.String({ description: "Exact active call ID" }),
276
+ message: Type.String({
277
+ maxLength: 500,
278
+ description: "Owner instruction for the live call"
279
+ }),
280
+ mode: Type.Optional(Type.Union([Type.Literal("say"), Type.Literal("guidance")]))
281
+ }),
217
282
  Type.Object({
218
283
  action: Type.Literal("continue_call"),
219
284
  callId: Type.String({ description: "Call ID" }),
@@ -246,6 +311,19 @@ const VoiceCallToolSchema = Type.Union([
246
311
  dtmfSequence: Type.Optional(Type.String({ description: "DTMF digits to play before connect" }))
247
312
  })
248
313
  ]);
314
+ //#endregion
315
+ //#region extensions/voice-call/index.ts
316
+ const VOICE_CALL_WRITE_METHOD_SCOPE = { scope: "operator.write" };
317
+ const VOICE_CALL_READ_METHOD_SCOPE = { scope: "operator.read" };
318
+ const voiceCallConfigSchema = { parse(value) {
319
+ const config = asOptionalRecord(value) ?? {};
320
+ const enabled = typeof config.enabled === "boolean" ? config.enabled : true;
321
+ return VoiceCallConfigSchema.parse({
322
+ ...config,
323
+ enabled,
324
+ provider: config.provider ?? (enabled ? "mock" : void 0)
325
+ });
326
+ } };
249
327
  function isCliOnlyProcess() {
250
328
  return process.env.OPENCLAW_CLI === "1" && !process.argv.slice(2).includes("gateway");
251
329
  }
@@ -287,9 +365,10 @@ var voice_call_default = definePluginEntry({
287
365
  const config = resolveVoiceCallConfig(voiceCallConfigSchema.parse(api.pluginConfig));
288
366
  const validation = validateProviderConfig(config);
289
367
  const runtimeCoordinator = getVoiceCallRuntimeCoordinator();
290
- const runtimeRegistration = api.registrationMode !== "full" && runtimeCoordinator.current ? runtimeCoordinator.current : {
368
+ const runtimeRegistration = {
291
369
  epoch: ++runtimeCoordinator.epochCounter,
292
- generation: { retired: false }
370
+ generation: { retired: false },
371
+ ensureRuntime: () => ensureRegisteredRuntime()
293
372
  };
294
373
  const continueOperationStore = createVoiceCallContinueOperationStore({
295
374
  config,
@@ -300,6 +379,7 @@ var voice_call_default = definePluginEntry({
300
379
  activateRuntimeGeneration(runtimeGeneration);
301
380
  if (!config.enabled) throw new Error("Voice call disabled in plugin config");
302
381
  if (!validation.valid) throw new Error(validation.errors.join("; "));
382
+ if ((config.reports.enabled || config.live.transcript) && !runtimeGeneration.runInServiceContext) throw new VoiceCallRuntimeLifecycleError("Voice call requester reporting requires service startup");
303
383
  while (true) {
304
384
  activateRuntimeGeneration(runtimeGeneration);
305
385
  const slot = runtimeCoordinator.slot;
@@ -334,24 +414,26 @@ var voice_call_default = definePluginEntry({
334
414
  }
335
415
  const scheduler = runtimeGeneration.scheduler;
336
416
  if (!scheduler || scheduler.signal.aborted) throw new VoiceCallRuntimeLifecycleError("Voice call service is not running; start the Gateway and retry");
337
- const startingSlot = {
417
+ const runtimePromise = createVoiceCallRuntime({
418
+ scheduler,
419
+ config,
420
+ coreConfig: api.config,
421
+ fullConfig: api.config,
422
+ agentRuntime: api.runtime.agent,
423
+ stateRuntime: api.runtime.state,
424
+ ttsRuntime: api.runtime.tts,
425
+ deliveryRuntime: api.runtime,
426
+ runInServiceContext: runtimeGeneration.runInServiceContext,
427
+ logger: api.logger
428
+ });
429
+ runtimeCoordinator.slot = {
338
430
  state: "starting",
339
431
  owner: runtimeGeneration,
340
- promise: createVoiceCallRuntime({
341
- scheduler,
342
- config,
343
- coreConfig: api.config,
344
- fullConfig: api.config,
345
- agentRuntime: api.runtime.agent,
346
- stateRuntime: api.runtime.state,
347
- ttsRuntime: api.runtime.tts,
348
- logger: api.logger
349
- })
432
+ promise: runtimePromise
350
433
  };
351
- runtimeCoordinator.slot = startingSlot;
352
434
  }
353
435
  };
354
- const ensureRuntime = async (runtimeGeneration = runtimeRegistration.generation) => {
436
+ const ensureRegisteredRuntime = async (runtimeGeneration = runtimeRegistration.generation) => {
355
437
  try {
356
438
  const runtime = await ensureRuntimeForGeneration(runtimeGeneration);
357
439
  runtimeGeneration.serviceHealth?.clearFailure();
@@ -361,6 +443,7 @@ var voice_call_default = definePluginEntry({
361
443
  throw err;
362
444
  }
363
445
  };
446
+ const ensureRuntime = () => (runtimeCoordinator.current ?? runtimeRegistration).ensureRuntime();
364
447
  const commands = createVoiceCallCommandService(ensureRuntime);
365
448
  const registerGatewayCommand = (method, handler, scope) => {
366
449
  api.registerGatewayMethod(method, async (options) => {
@@ -380,7 +463,24 @@ var voice_call_default = definePluginEntry({
380
463
  message,
381
464
  mode: params?.mode === "notify" || params?.mode === "conversation" ? params.mode : void 0,
382
465
  sessionKey: normalizeOptionalString(params?.sessionKey),
383
- requesterSessionKey: normalizeOptionalString(params?.requesterSessionKey)
466
+ requesterSessionKey: normalizeOptionalString(params?.requesterSessionKey),
467
+ brief: params?.brief
468
+ });
469
+ }, VOICE_CALL_WRITE_METHOD_SCOPE);
470
+ registerGatewayCommand("voicecall.steer", ({ params, client, hasCurrentClientAuthority, signal }) => {
471
+ const scopes = client?.connect.scopes ?? [];
472
+ const operator = scopes.includes("operator.write") || scopes.includes("operator.admin");
473
+ if (params.mode !== void 0 && params.mode !== "say" && params.mode !== "guidance") throw new VoiceCallCommandInputError("mode must be say or guidance");
474
+ return commands.steer({
475
+ callId: normalizeOptionalString(params.callId),
476
+ message: normalizeOptionalString(params.message),
477
+ mode: params.mode,
478
+ requesterSessionKey: client?.internal?.agentToolCaller?.sessionKey,
479
+ operator,
480
+ assertCurrent: () => {
481
+ signal?.throwIfAborted();
482
+ if (hasCurrentClientAuthority && !hasCurrentClientAuthority()) throw new VoiceCallCommandInputError("Steering caller authority expired");
483
+ }
384
484
  });
385
485
  }, VOICE_CALL_WRITE_METHOD_SCOPE);
386
486
  registerGatewayCommand("voicecall.continue", ({ params }) => commands.continueCall(normalizeOptionalString(params?.callId), normalizeOptionalString(params?.message)), VOICE_CALL_WRITE_METHOD_SCOPE);
@@ -414,68 +514,90 @@ var voice_call_default = definePluginEntry({
414
514
  dtmfSequence: normalizeOptionalString(params?.dtmfSequence),
415
515
  sessionKey: normalizeOptionalString(params?.sessionKey),
416
516
  requesterSessionKey: normalizeOptionalString(params?.requesterSessionKey),
517
+ brief: params?.brief,
417
518
  agentId: normalizedAgentId
418
519
  });
419
520
  }, VOICE_CALL_WRITE_METHOD_SCOPE);
420
- api.registerTool((toolContext) => ({
421
- name: "voice_call",
422
- label: "Voice Call",
423
- description: "Make phone calls and have voice conversations via the voice-call plugin.",
424
- parameters: VoiceCallToolSchema,
425
- async execute(_toolCallId, params) {
426
- const rawParams = asNonArrayRecord(params);
427
- const requesterSessionKey = normalizeOptionalString(toolContext.sessionKey);
428
- const contextAgentId = normalizeOptionalString(toolContext.agentId) ?? parseAgentSessionKey(requesterSessionKey)?.agentId;
429
- const agentId = contextAgentId ? normalizeAgentId(contextAgentId) : void 0;
430
- try {
431
- await ensureRuntime();
432
- if (typeof rawParams.action === "string") switch (rawParams.action) {
433
- case "initiate_call": {
434
- const message = normalizeOptionalString(rawParams.message);
435
- if (!message) throw new VoiceCallCommandInputError("message required");
436
- return jsonResult(await commands.initiate({
437
- to: normalizeOptionalString(rawParams.to),
438
- message,
439
- dtmfSequence: normalizeOptionalString(rawParams.dtmfSequence),
440
- mode: rawParams.mode === "notify" || rawParams.mode === "conversation" ? rawParams.mode : void 0,
441
- sessionKey: normalizeOptionalString(rawParams.sessionKey),
442
- agentId,
443
- requesterSessionKey
521
+ api.registerTool((toolContext) => {
522
+ const boundRuntimeRegistration = asOptionalRecord(toolContext.toolBindings?.voice_call)?.kind === "active-call" ? runtimeCoordinator.current : void 0;
523
+ const boundRuntimeGeneration = boundRuntimeRegistration?.generation;
524
+ const assertActiveCallAuthority = () => {
525
+ if (!boundRuntimeRegistration || !boundRuntimeGeneration || runtimeCoordinator.current !== boundRuntimeRegistration || boundRuntimeRegistration.generation !== boundRuntimeGeneration || boundRuntimeGeneration.retired) throw new VoiceCallCommandInputError("The bound realtime call service is no longer active.");
526
+ };
527
+ return {
528
+ name: "voice_call",
529
+ label: "Voice Call",
530
+ description: "Make phone calls and have voice conversations via the voice-call plugin.",
531
+ parameters: VoiceCallToolSchema,
532
+ async execute(_toolCallId, params, signal) {
533
+ const rawParams = asNonArrayRecord(params);
534
+ const requesterSessionKey = normalizeOptionalString(toolContext.sessionKey);
535
+ const contextAgentId = normalizeOptionalString(toolContext.agentId) ?? parseAgentSessionKey(requesterSessionKey)?.agentId;
536
+ const agentId = contextAgentId ? normalizeAgentId(contextAgentId) : void 0;
537
+ try {
538
+ const rt = await ensureRuntime();
539
+ const callScope = await resolveActiveVoiceCallToolScope({
540
+ action: rawParams.action,
541
+ binding: toolContext.toolBindings?.voice_call,
542
+ requestedCallId: rawParams.callId,
543
+ runtime: rt,
544
+ assertAuthority: assertActiveCallAuthority,
545
+ signal
546
+ });
547
+ const initiateParams = {
548
+ to: normalizeOptionalString(rawParams.to),
549
+ message: normalizeOptionalString(rawParams.message),
550
+ dtmfSequence: normalizeOptionalString(rawParams.dtmfSequence),
551
+ sessionKey: normalizeOptionalString(rawParams.sessionKey),
552
+ agentId,
553
+ requesterSessionKey,
554
+ brief: rawParams.brief
555
+ };
556
+ if (typeof rawParams.action === "string") switch (rawParams.action) {
557
+ case "initiate_call":
558
+ if (!initiateParams.message) throw new VoiceCallCommandInputError("message required");
559
+ return jsonResult(await commands.initiate({
560
+ ...initiateParams,
561
+ mode: rawParams.mode === "notify" || rawParams.mode === "conversation" ? rawParams.mode : void 0
562
+ }));
563
+ case "steer_call":
564
+ if (rawParams.mode !== void 0 && rawParams.mode !== "say" && rawParams.mode !== "guidance") throw new VoiceCallCommandInputError("mode must be say or guidance");
565
+ return jsonResult(await commands.steer({
566
+ callId: normalizeOptionalString(rawParams.callId),
567
+ message: normalizeOptionalString(rawParams.message),
568
+ mode: rawParams.mode,
569
+ requesterSessionKey,
570
+ assertCurrent: () => signal?.throwIfAborted()
571
+ }));
572
+ case "continue_call": return jsonResult(await commands.continueCall(normalizeOptionalString(rawParams.callId), normalizeOptionalString(rawParams.message)));
573
+ case "speak_to_user": return jsonResult(await commands.speak({
574
+ callId: normalizeOptionalString(rawParams.callId),
575
+ message: normalizeOptionalString(rawParams.message)
444
576
  }));
577
+ case "send_dtmf": return jsonResult(await commands.sendDtmf(normalizeOptionalString(rawParams.callId), normalizeOptionalString(rawParams.digits)));
578
+ case "end_call":
579
+ if (!callScope) signal?.throwIfAborted();
580
+ return jsonResult(await commands.endCall(callScope?.callId ?? normalizeOptionalString(rawParams.callId), callScope?.execution));
581
+ case "get_status": {
582
+ const callId = normalizeOptionalString(rawParams.callId);
583
+ if (!callId) throw new VoiceCallCommandInputError("callId required");
584
+ return jsonResult(await commands.status(callId));
585
+ }
445
586
  }
446
- case "continue_call": return jsonResult(await commands.continueCall(normalizeOptionalString(rawParams.callId), normalizeOptionalString(rawParams.message)));
447
- case "speak_to_user": return jsonResult(await commands.speak({
448
- callId: normalizeOptionalString(rawParams.callId),
449
- message: normalizeOptionalString(rawParams.message)
450
- }));
451
- case "send_dtmf": return jsonResult(await commands.sendDtmf(normalizeOptionalString(rawParams.callId), normalizeOptionalString(rawParams.digits)));
452
- case "end_call": return jsonResult(await commands.endCall(normalizeOptionalString(rawParams.callId)));
453
- case "get_status": {
454
- const callId = normalizeOptionalString(rawParams.callId);
455
- if (!callId) throw new VoiceCallCommandInputError("callId required");
456
- return jsonResult(await commands.status(callId));
587
+ if ((rawParams.mode ?? "call") === "status") {
588
+ const sid = normalizeOptionalString(rawParams.sid) ?? "";
589
+ if (!sid) throw new Error("sid required for status");
590
+ return jsonResult(await commands.status(sid));
457
591
  }
592
+ return jsonResult(await commands.initiate(initiateParams, "to required for call"));
593
+ } catch (err) {
594
+ return jsonResult({ error: formatErrorMessage(err) });
458
595
  }
459
- if ((rawParams.mode ?? "call") === "status") {
460
- const sid = normalizeOptionalString(rawParams.sid) ?? "";
461
- if (!sid) throw new Error("sid required for status");
462
- return jsonResult(await commands.status(sid));
463
- }
464
- return jsonResult(await commands.initiate({
465
- to: normalizeOptionalString(rawParams.to),
466
- dtmfSequence: normalizeOptionalString(rawParams.dtmfSequence),
467
- message: normalizeOptionalString(rawParams.message),
468
- sessionKey: normalizeOptionalString(rawParams.sessionKey),
469
- agentId,
470
- requesterSessionKey
471
- }, "to required for call"));
472
- } catch (err) {
473
- return jsonResult({ error: formatErrorMessage(err) });
474
596
  }
475
- }
476
- }));
597
+ };
598
+ });
477
599
  api.registerCli(async ({ program }) => {
478
- const { registerVoiceCallCli } = await import("./.setup/cli-Cn1leykM.mjs");
600
+ const { registerVoiceCallCli } = await import("./.setup/cli-dyyK8Upw.mjs");
479
601
  registerVoiceCallCli({
480
602
  program,
481
603
  config,
@@ -501,6 +623,8 @@ var voice_call_default = definePluginEntry({
501
623
  runtimeRegistration.generation = { retired: false };
502
624
  }
503
625
  runtimeRegistration.generation.scheduler = ctx.scheduler;
626
+ const serviceContext = AsyncLocalStorage.snapshot();
627
+ runtimeRegistration.generation.runInServiceContext = (run) => serviceContext(run);
504
628
  runtimeRegistration.generation.serviceHealth = ctx.serviceHealth;
505
629
  activateRuntimeGeneration(runtimeRegistration.generation);
506
630
  } catch (err) {
@@ -515,8 +639,7 @@ var voice_call_default = definePluginEntry({
515
639
  api.logger.error(`[voice-call] Runtime not started: ${error.message}`);
516
640
  return;
517
641
  }
518
- const startingGeneration = runtimeRegistration.generation;
519
- ensureRuntime(startingGeneration).catch((err) => {
642
+ ensureRegisteredRuntime().catch((err) => {
520
643
  if (err instanceof VoiceCallRuntimeLifecycleError) return;
521
644
  ctx.serviceHealth?.reportFailure(err);
522
645
  api.logger.error(`[voice-call] Failed to start runtime: ${formatErrorMessage(err)}`);
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-CbSKxSKk.mjs";
1
+ import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-STjFmi_B.mjs";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -109,12 +109,13 @@ function migrateVoiceCallPluginConfig(config) {
109
109
  });
110
110
  if (migration.changes.length === 0) return null;
111
111
  const plugins = structuredClone(config.plugins ?? {});
112
- const entries = { ...plugins.entries };
113
- entries["voice-call"] = {
114
- ...isRecord(entries["voice-call"]) ? entries["voice-call"] : {},
115
- config: migration.config
112
+ plugins.entries = {
113
+ ...plugins.entries,
114
+ "voice-call": {
115
+ ...plugins.entries?.["voice-call"],
116
+ config: migration.config
117
+ }
116
118
  };
117
- plugins.entries = entries;
118
119
  return {
119
120
  config: {
120
121
  ...config,
@@ -129,7 +130,7 @@ var setup_api_default = definePluginEntry({
129
130
  name: "Voice Call Setup",
130
131
  description: "Lightweight Voice Call setup hooks",
131
132
  register(api) {
132
- api.registerConfigMigration((config) => migrateVoiceCallPluginConfig(config));
133
+ api.registerConfigMigration(migrateVoiceCallPluginConfig);
133
134
  }
134
135
  });
135
136
  //#endregion