@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/README.md +118 -3
- package/dist/.setup/{guarded-json-api-x55yqHvW.mjs → carrier-api-BYKw_fsQ.mjs} +94 -90
- package/dist/.setup/{cli-Cn1leykM.mjs → cli-dyyK8Upw.mjs} +66 -84
- package/dist/.setup/{config-CX9J-053.mjs → config-CsrGuamE.mjs} +95 -69
- package/dist/.setup/{mock-eLv_Dc4o.mjs → mock-rySvj-G6.mjs} +12 -3
- package/dist/.setup/{plivo-CpPIENu4.mjs → plivo-COfD-Kuc.mjs} +35 -89
- package/dist/.setup/{realtime-handler-Do7Sz7Gh.mjs → realtime-handler-BdAuKAC2.mjs} +749 -464
- package/dist/.setup/{response-generator-DAhbXEta.mjs → response-generator-COu0_8-d.mjs} +14 -32
- package/dist/.setup/{runtime-entry-CbSKxSKk.mjs → runtime-entry-STjFmi_B.mjs} +1053 -446
- package/dist/.setup/{telnyx-DPilutCH.mjs → telnyx-B4P0peEe.mjs} +30 -61
- package/dist/.setup/{twilio-BWFNGyVx.mjs → twilio-BzAYgH7M.mjs} +75 -90
- package/dist/.setup/{voice-mapping-B_iAUkER.mjs → voice-mapping-1lzFmzOF.mjs} +0 -10
- package/dist/cli-output-mode.js +1 -0
- package/dist/doctor-contract-api.js +19 -23
- package/dist/index.js +212 -89
- package/dist/runtime-entry.js +1 -1
- package/dist/setup-api.js +7 -6
- package/openclaw.plugin.json +210 -2
- package/package.json +4 -4
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-
|
|
4
|
-
import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-
|
|
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
|
-
|
|
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/
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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-
|
|
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
|
-
|
|
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)}`);
|
package/dist/runtime-entry.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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(
|
|
133
|
+
api.registerConfigMigration(migrateVoiceCallPluginConfig);
|
|
133
134
|
}
|
|
134
135
|
});
|
|
135
136
|
//#endregion
|