@agenticmail/core 0.9.38 → 0.9.40

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.cjs CHANGED
@@ -1610,6 +1610,7 @@ __export(index_exports, {
1610
1610
  getTelegramMe: () => getTelegramMe,
1611
1611
  getTelegramUpdates: () => getTelegramUpdates,
1612
1612
  getTelegramWebhookInfo: () => getTelegramWebhookInfo,
1613
+ getVoiceProvider: () => getVoiceProvider,
1613
1614
  hostSessionStoragePath: () => hostSessionStoragePath,
1614
1615
  inferPhoneRegion: () => inferPhoneRegion,
1615
1616
  invalidateSkillCache: () => invalidateSkillCache,
@@ -1622,6 +1623,7 @@ __export(index_exports, {
1622
1623
  isTelegramStopCommand: () => isTelegramStopCommand,
1623
1624
  isValidPhoneNumber: () => isValidPhoneNumber,
1624
1625
  listSkills: () => listSkills,
1626
+ listVoiceProviders: () => listVoiceProviders,
1625
1627
  loadAgentPersona: () => loadAgentPersona,
1626
1628
  loadHostSession: () => loadHostSession,
1627
1629
  loadSkill: () => loadSkill,
@@ -1642,6 +1644,7 @@ __export(index_exports, {
1642
1644
  personaPathFor: () => personaPathFor,
1643
1645
  planBridgeWake: () => planBridgeWake,
1644
1646
  pollForOperatorAnswer: () => pollForOperatorAnswer,
1647
+ readAgentPersonaFile: () => readAgentPersonaFile,
1645
1648
  recallMemory: () => recallMemory,
1646
1649
  recordToolCall: () => recordToolCall,
1647
1650
  redactBotToken: () => redactBotToken,
@@ -1650,6 +1653,7 @@ __export(index_exports, {
1650
1653
  redactSecret: () => redactSecret,
1651
1654
  redactSmsConfig: () => redactSmsConfig,
1652
1655
  redactTelegramConfig: () => redactTelegramConfig,
1656
+ registerVoiceProvider: () => registerVoiceProvider,
1653
1657
  renderSkillAsPrompt: () => renderSkillAsPrompt,
1654
1658
  requireBinary: () => requireBinary,
1655
1659
  requireWhisperModel: () => requireWhisperModel,
@@ -1657,6 +1661,7 @@ __export(index_exports, {
1657
1661
  resolveConfig: () => resolveConfig,
1658
1662
  resolveExtensionPolicy: () => resolveExtensionPolicy,
1659
1663
  resolveTlsRejectUnauthorized: () => resolveTlsRejectUnauthorized,
1664
+ resolveVoiceRuntime: () => resolveVoiceRuntime,
1660
1665
  safeJoin: () => safeJoin,
1661
1666
  sanitizeEmail: () => sanitizeEmail,
1662
1667
  saveAgentPersona: () => saveAgentPersona,
@@ -1678,6 +1683,7 @@ __export(index_exports, {
1678
1683
  threadIdFor: () => threadIdFor,
1679
1684
  tokenize: () => tokenize,
1680
1685
  tryJoin: () => tryJoin,
1686
+ updateAgentPersonaFrontmatter: () => updateAgentPersonaFrontmatter,
1681
1687
  userSkillsDir: () => userSkillsDir,
1682
1688
  validateApiUrl: () => validateApiUrl,
1683
1689
  validatePhoneMissionPolicy: () => validatePhoneMissionPolicy,
@@ -2588,6 +2594,13 @@ function resolveConfig(overrides) {
2588
2594
  dataDir: env.AGENTICMAIL_DATA_DIR?.replace(/^~(?=\/|$)/, (0, import_node_os.homedir)()) ?? DEFAULT_CONFIG.dataDir
2589
2595
  };
2590
2596
  if (env.OPENAI_API_KEY) config.openaiApiKey = env.OPENAI_API_KEY;
2597
+ if (env.XAI_API_KEY) {
2598
+ config.voiceProviderKeys = config.voiceProviderKeys ?? {};
2599
+ config.voiceProviderKeys.grok = env.XAI_API_KEY;
2600
+ }
2601
+ if (env.AGENTICMAIL_VOICE_RUNTIME && env.AGENTICMAIL_VOICE_RUNTIME.trim()) {
2602
+ config.voiceRuntime = env.AGENTICMAIL_VOICE_RUNTIME.trim();
2603
+ }
2591
2604
  const configPath = (0, import_node_path.join)(config.dataDir, "config.json");
2592
2605
  if ((0, import_node_fs.existsSync)(configPath)) {
2593
2606
  try {
@@ -7802,7 +7815,14 @@ function validatePhoneMissionPolicy(policy) {
7802
7815
  // manager) can read a concrete value without juggling undefined.
7803
7816
  // Caller-omitted → DEFAULT_*. Caller-set → clamped to server caps.
7804
7817
  extensionPolicy: resolveExtensionPolicy(policy.extensionPolicy),
7805
- callbackPolicy: resolveCallbackPolicy(policy.callbackPolicy)
7818
+ callbackPolicy: resolveCallbackPolicy(policy.callbackPolicy),
7819
+ // v0.9.95 — voice-runtime overrides. Pass-through; the registry
7820
+ // validates the provider id and voice-against-catalogue at
7821
+ // session-open time so we don't have to import the registry
7822
+ // here (would create a cycle with realtime-bridge.ts).
7823
+ voiceRuntime: typeof policy.voiceRuntime === "string" && policy.voiceRuntime.trim() ? policy.voiceRuntime.trim() : void 0,
7824
+ voiceModel: typeof policy.voiceModel === "string" && policy.voiceModel.trim() ? policy.voiceModel.trim() : void 0,
7825
+ voice: typeof policy.voice === "string" && policy.voice.trim() ? policy.voice.trim() : void 0
7806
7826
  },
7807
7827
  issues: []
7808
7828
  };
@@ -12203,6 +12223,140 @@ function withTimeout(promise, ms) {
12203
12223
  return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
12204
12224
  }
12205
12225
 
12226
+ // src/phone/voice-providers/registry.ts
12227
+ var PROVIDERS2 = /* @__PURE__ */ new Map();
12228
+ function registerVoiceProvider(provider) {
12229
+ if (PROVIDERS2.has(provider.id)) {
12230
+ throw new Error(`Voice provider "${provider.id}" registered twice \u2014 id collision.`);
12231
+ }
12232
+ PROVIDERS2.set(provider.id, provider);
12233
+ }
12234
+ function listVoiceProviders() {
12235
+ return Array.from(PROVIDERS2.values());
12236
+ }
12237
+ function getVoiceProvider(id) {
12238
+ return PROVIDERS2.get(id);
12239
+ }
12240
+ function resolveVoiceRuntime(providerId, config, options = {}) {
12241
+ const id = (providerId || "openai").trim() || "openai";
12242
+ const provider = PROVIDERS2.get(id);
12243
+ if (!provider) {
12244
+ const known = Array.from(PROVIDERS2.keys()).join(", ") || "(none registered)";
12245
+ throw new Error(
12246
+ `Unknown voice runtime "${id}". Known providers: ${known}. Add a new one by dropping a file into packages/core/src/phone/voice-providers/.`
12247
+ );
12248
+ }
12249
+ let apiKey = "";
12250
+ let apiKeySource = "";
12251
+ if (provider.apiKeyConfigField) {
12252
+ const legacy = config[provider.apiKeyConfigField];
12253
+ if (legacy && legacy.trim()) {
12254
+ apiKey = legacy.trim();
12255
+ apiKeySource = `config.${provider.apiKeyConfigField}`;
12256
+ }
12257
+ }
12258
+ if (!apiKey) {
12259
+ const fromMap = config.voiceProviderKeys?.[provider.id];
12260
+ if (fromMap && fromMap.trim()) {
12261
+ apiKey = fromMap.trim();
12262
+ apiKeySource = `config.voiceProviderKeys.${provider.id}`;
12263
+ }
12264
+ }
12265
+ if (!apiKey) {
12266
+ const fromEnv = process.env[provider.apiKeyEnvVar];
12267
+ if (fromEnv && fromEnv.trim()) {
12268
+ apiKey = fromEnv.trim();
12269
+ apiKeySource = `env ${provider.apiKeyEnvVar}`;
12270
+ }
12271
+ }
12272
+ if (!apiKey) {
12273
+ throw new Error(
12274
+ `Voice provider "${provider.id}" (${provider.displayName}) selected, but no API key is configured. Set ${provider.apiKeyEnvVar} in your environment or save it to ~/.agenticmail/config.json under voiceProviderKeys.${provider.id}.`
12275
+ );
12276
+ }
12277
+ const model = options.model && options.model.trim() || provider.defaultModel;
12278
+ const url = `${provider.websocketBaseUrl}?model=${encodeURIComponent(model)}`;
12279
+ let voice = "";
12280
+ let voiceSource = "";
12281
+ const requested = (options.voice || "").trim();
12282
+ if (requested) {
12283
+ if (provider.voices.includes(requested) || provider.customVoicesSupported) {
12284
+ voice = requested;
12285
+ voiceSource = "mission policy";
12286
+ } else {
12287
+ console.warn(
12288
+ `[voice-providers] Voice "${requested}" is not in ${provider.id}'s catalogue (${provider.voices.join(", ")}). Falling through to ${provider.defaultVoice}.`
12289
+ );
12290
+ }
12291
+ }
12292
+ if (!voice) {
12293
+ const installDefault = config.voiceProviderVoices?.[provider.id];
12294
+ if (installDefault && installDefault.trim()) {
12295
+ const v = installDefault.trim();
12296
+ if (provider.voices.includes(v) || provider.customVoicesSupported) {
12297
+ voice = v;
12298
+ voiceSource = `config.voiceProviderVoices.${provider.id}`;
12299
+ }
12300
+ }
12301
+ }
12302
+ if (!voice) {
12303
+ voice = provider.defaultVoice;
12304
+ voiceSource = `${provider.id} default`;
12305
+ }
12306
+ return {
12307
+ providerId: provider.id,
12308
+ providerDisplayName: provider.displayName,
12309
+ url,
12310
+ model,
12311
+ apiKey,
12312
+ apiKeySource,
12313
+ voice,
12314
+ voiceSource
12315
+ };
12316
+ }
12317
+
12318
+ // src/phone/voice-providers/openai.ts
12319
+ registerVoiceProvider({
12320
+ id: "openai",
12321
+ displayName: "OpenAI Realtime (gpt-realtime)",
12322
+ websocketBaseUrl: "wss://api.openai.com/v1/realtime",
12323
+ defaultModel: "gpt-realtime",
12324
+ apiKeyEnvVar: "OPENAI_API_KEY",
12325
+ // Legacy: the original config.json schema used a dedicated
12326
+ // `openaiApiKey` field for this key. The resolver checks that field
12327
+ // before the generic voiceProviderKeys map so existing installs
12328
+ // continue to work without migration.
12329
+ apiKeyConfigField: "openaiApiKey",
12330
+ description: "OpenAI Realtime (gpt-realtime). Default voice runtime; supports linear PCM @ 24 kHz (46elks) and G.711 \xB5-law @ 8 kHz (Twilio) without transcoding.",
12331
+ // v0.9.95 — voice catalogue. Names match what the Realtime session
12332
+ // accepts under `audio.output.voice`. `marin` and `cedar` are the
12333
+ // GA gpt-realtime additions; the rest are the legacy roster carried
12334
+ // forward from gpt-4o-realtime-preview.
12335
+ voices: ["alloy", "ash", "ballad", "cedar", "coral", "echo", "marin", "sage", "shimmer", "verse"],
12336
+ defaultVoice: "marin"
12337
+ });
12338
+
12339
+ // src/phone/voice-providers/grok.ts
12340
+ registerVoiceProvider({
12341
+ id: "grok",
12342
+ displayName: "xAI Grok Voice Agent",
12343
+ websocketBaseUrl: "wss://api.x.ai/v1/realtime",
12344
+ defaultModel: "grok-voice-latest",
12345
+ apiKeyEnvVar: "XAI_API_KEY",
12346
+ description: 'xAI Grok Voice Agent \u2014 OpenAI-Realtime-compatible WebSocket protocol; select via mission policy.voiceRuntime="grok" or env AGENTICMAIL_VOICE_RUNTIME=grok.',
12347
+ // v0.9.95 — voice catalogue. xAI ships 5 expressive built-in
12348
+ // voices for the realtime Voice Agent. Three are explicitly named
12349
+ // in the announcement (Ara, Eve, Leo); the other two are
12350
+ // browsable in console.x.ai's Voice Library along with the
12351
+ // broader 80+ voice catalogue. The full live list is at
12352
+ // GET /v1/tts/voices. We don't try to enumerate all 80+ here —
12353
+ // customVoicesSupported lets operators paste any voice_id from
12354
+ // their console.
12355
+ voices: ["ara", "eve", "leo"],
12356
+ defaultVoice: "ara",
12357
+ customVoicesSupported: true
12358
+ });
12359
+
12206
12360
  // src/telemetry.ts
12207
12361
  var import_crypto = require("crypto");
12208
12362
  var import_fs = require("fs");
@@ -16336,8 +16490,19 @@ function loadAgentPersona(agentName) {
16336
16490
  const path2 = personaPathFor(agentName);
16337
16491
  try {
16338
16492
  if ((0, import_node_fs15.existsSync)(path2)) {
16339
- const content = (0, import_node_fs15.readFileSync)(path2, "utf-8").trim();
16340
- if (content) return content;
16493
+ const raw = (0, import_node_fs15.readFileSync)(path2, "utf-8");
16494
+ if (raw.trim()) {
16495
+ const text = raw.replace(/\r\n/g, "\n");
16496
+ if (text.startsWith("---\n")) {
16497
+ const close = text.indexOf("\n---", 4);
16498
+ if (close > 0) {
16499
+ let cursor = close + 4;
16500
+ while (cursor < text.length && (text[cursor] === "\n" || text[cursor] === "\r")) cursor++;
16501
+ return text.slice(cursor).trim();
16502
+ }
16503
+ }
16504
+ return text.trim();
16505
+ }
16341
16506
  }
16342
16507
  } catch {
16343
16508
  }
@@ -16357,6 +16522,61 @@ function saveAgentPersona(agentName, content) {
16357
16522
  (0, import_node_fs15.writeFileSync)(path2, content.trim() + "\n", { mode: 420 });
16358
16523
  return path2;
16359
16524
  }
16525
+ function readAgentPersonaFile(agentName) {
16526
+ const path2 = personaPathFor(agentName);
16527
+ let raw = "";
16528
+ try {
16529
+ if ((0, import_node_fs15.existsSync)(path2)) raw = (0, import_node_fs15.readFileSync)(path2, "utf-8");
16530
+ } catch {
16531
+ }
16532
+ if (!raw.trim()) {
16533
+ return { frontmatter: {}, body: loadAgentPersona(agentName) };
16534
+ }
16535
+ const text = raw.replace(/\r\n/g, "\n");
16536
+ if (!text.startsWith("---\n")) {
16537
+ return { frontmatter: {}, body: text.trim() };
16538
+ }
16539
+ const close = text.indexOf("\n---", 4);
16540
+ if (close < 0) {
16541
+ return { frontmatter: {}, body: text.trim() };
16542
+ }
16543
+ const yamlBlock = text.slice(4, close);
16544
+ const bodyStart = close + 4;
16545
+ let cursor = bodyStart;
16546
+ while (cursor < text.length && (text[cursor] === "\n" || text[cursor] === "\r")) cursor++;
16547
+ const body = text.slice(cursor).trim();
16548
+ const frontmatter = {};
16549
+ for (const line of yamlBlock.split("\n")) {
16550
+ const m = /^\s*([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*?)\s*$/.exec(line);
16551
+ if (!m) continue;
16552
+ const key = m[1];
16553
+ const value = m[2].replace(/^["']|["']$/g, "");
16554
+ if (key === "voice") frontmatter.voice = value;
16555
+ else if (key === "voiceRuntime") frontmatter.voiceRuntime = value;
16556
+ }
16557
+ return { frontmatter, body };
16558
+ }
16559
+ function updateAgentPersonaFrontmatter(agentName, patch) {
16560
+ const { frontmatter, body } = readAgentPersonaFile(agentName);
16561
+ const merged = { ...frontmatter };
16562
+ if (patch.voice !== void 0) merged.voice = patch.voice;
16563
+ if (patch.voiceRuntime !== void 0) merged.voiceRuntime = patch.voiceRuntime;
16564
+ const lines = [];
16565
+ if (merged.voice && merged.voice.trim()) lines.push(`voice: ${merged.voice.trim()}`);
16566
+ if (merged.voiceRuntime && merged.voiceRuntime.trim()) lines.push(`voiceRuntime: ${merged.voiceRuntime.trim()}`);
16567
+ const path2 = personaPathFor(agentName);
16568
+ const dir2 = path2.substring(0, path2.lastIndexOf("/"));
16569
+ if (!(0, import_node_fs15.existsSync)(dir2)) (0, import_node_fs15.mkdirSync)(dir2, { recursive: true });
16570
+ const content = lines.length > 0 ? `---
16571
+ ${lines.join("\n")}
16572
+ ---
16573
+
16574
+ ${body}
16575
+ ` : `${body}
16576
+ `;
16577
+ (0, import_node_fs15.writeFileSync)(path2, content, { mode: 420 });
16578
+ return path2;
16579
+ }
16360
16580
  // Annotate the CommonJS export names for ESM import in node:
16361
16581
  0 && (module.exports = {
16362
16582
  AGENT_ROLES,
@@ -16517,6 +16737,7 @@ function saveAgentPersona(agentName, content) {
16517
16737
  getTelegramMe,
16518
16738
  getTelegramUpdates,
16519
16739
  getTelegramWebhookInfo,
16740
+ getVoiceProvider,
16520
16741
  hostSessionStoragePath,
16521
16742
  inferPhoneRegion,
16522
16743
  invalidateSkillCache,
@@ -16529,6 +16750,7 @@ function saveAgentPersona(agentName, content) {
16529
16750
  isTelegramStopCommand,
16530
16751
  isValidPhoneNumber,
16531
16752
  listSkills,
16753
+ listVoiceProviders,
16532
16754
  loadAgentPersona,
16533
16755
  loadHostSession,
16534
16756
  loadSkill,
@@ -16549,6 +16771,7 @@ function saveAgentPersona(agentName, content) {
16549
16771
  personaPathFor,
16550
16772
  planBridgeWake,
16551
16773
  pollForOperatorAnswer,
16774
+ readAgentPersonaFile,
16552
16775
  recallMemory,
16553
16776
  recordToolCall,
16554
16777
  redactBotToken,
@@ -16557,6 +16780,7 @@ function saveAgentPersona(agentName, content) {
16557
16780
  redactSecret,
16558
16781
  redactSmsConfig,
16559
16782
  redactTelegramConfig,
16783
+ registerVoiceProvider,
16560
16784
  renderSkillAsPrompt,
16561
16785
  requireBinary,
16562
16786
  requireWhisperModel,
@@ -16564,6 +16788,7 @@ function saveAgentPersona(agentName, content) {
16564
16788
  resolveConfig,
16565
16789
  resolveExtensionPolicy,
16566
16790
  resolveTlsRejectUnauthorized,
16791
+ resolveVoiceRuntime,
16567
16792
  safeJoin,
16568
16793
  sanitizeEmail,
16569
16794
  saveAgentPersona,
@@ -16585,6 +16810,7 @@ function saveAgentPersona(agentName, content) {
16585
16810
  threadIdFor,
16586
16811
  tokenize,
16587
16812
  tryJoin,
16813
+ updateAgentPersonaFrontmatter,
16588
16814
  userSkillsDir,
16589
16815
  validateApiUrl,
16590
16816
  validatePhoneMissionPolicy,
package/dist/index.d.cts CHANGED
@@ -229,6 +229,31 @@ interface AgenticMailConfig {
229
229
  * `config.json`. Optional — no other feature depends on it.
230
230
  */
231
231
  openaiApiKey?: string;
232
+ /**
233
+ * v0.9.93 — voice-runtime provider keys. Each is a thin Bearer-token
234
+ * for a provider's realtime API; required only when that provider
235
+ * is selected as the runtime for a call. New providers register
236
+ * themselves in `packages/core/src/phone/voice-providers/` and
237
+ * declare which key field they consume — this record is the central
238
+ * pool. Read from per-provider env vars (e.g. `XAI_API_KEY`) at boot.
239
+ */
240
+ voiceProviderKeys?: Record<string, string>;
241
+ /**
242
+ * v0.9.95 — install-wide default voice CHARACTER per provider
243
+ * (e.g. `{ openai: 'cedar', grok: 'ara' }`). Optional — when a
244
+ * provider's slot is empty, the bridge falls back to the
245
+ * provider's own `defaultVoice`. Per-agent persona + per-call
246
+ * mission policy still beat this. The CLI's `agenticmail
247
+ * setup-voice` voice picker writes here.
248
+ */
249
+ voiceProviderVoices?: Record<string, string>;
250
+ /**
251
+ * v0.9.93 — default voice-runtime provider id for phone missions
252
+ * that don't pin one on their own policy. `'openai'` (the existing
253
+ * default) or any provider registered in `voice-providers/`.
254
+ * Read from `AGENTICMAIL_VOICE_RUNTIME` env var or `config.json`.
255
+ */
256
+ voiceRuntime?: string;
232
257
  masterKey: string;
233
258
  dataDir: string;
234
259
  }
@@ -2748,6 +2773,25 @@ interface OpenClawPhoneMissionPolicy {
2748
2773
  * {@link DEFAULT_CALLBACK_POLICY} when omitted.
2749
2774
  */
2750
2775
  callbackPolicy?: PhoneCallbackPolicy;
2776
+ /**
2777
+ * v0.9.95 — voice-runtime provider id ('openai', 'grok', any
2778
+ * future plugin). Per-call override that beats agent persona
2779
+ * frontmatter, install default, and bridge default. Optional.
2780
+ */
2781
+ voiceRuntime?: string;
2782
+ /**
2783
+ * v0.9.95 — model name override (e.g. `'gpt-realtime-mini'` for
2784
+ * cost-tuning, `'grok-voice-fast'` for latency). Optional.
2785
+ */
2786
+ voiceModel?: string;
2787
+ /**
2788
+ * v0.9.95 — voice CHARACTER override (e.g. `'cedar'`, `'ara'`, or
2789
+ * a custom-voice id from Grok). Optional. Validated against the
2790
+ * provider's catalogue at session-open time; unknown names against
2791
+ * a fixed-catalogue provider fall through to the provider default
2792
+ * with a log warning.
2793
+ */
2794
+ voice?: string;
2751
2795
  }
2752
2796
  interface StartPhoneMissionInput {
2753
2797
  to: string;
@@ -3824,6 +3868,161 @@ declare class RealtimeVoiceBridge {
3824
3868
  private safeSend;
3825
3869
  }
3826
3870
 
3871
+ /**
3872
+ * Voice-runtime provider plugin interface.
3873
+ *
3874
+ * Each backend (OpenAI Realtime, Grok Voice Agent, future Anthropic
3875
+ * realtime, Cartesia, ElevenLabs ConvAI, etc.) lives in its own file
3876
+ * under `packages/core/src/phone/voice-providers/` and registers
3877
+ * itself with the registry by calling {@link registerVoiceProvider}
3878
+ * at module load.
3879
+ *
3880
+ * Adding a new provider is meant to be a literal FILE DROP:
3881
+ *
3882
+ * 1. Create `voice-providers/<id>.ts` exporting a {@link VoiceProvider}.
3883
+ * 2. Add a single `import './<id>.js';` line to `voice-providers/index.ts`
3884
+ * so the side-effect registration runs.
3885
+ * 3. (Optional) document the env var the provider expects.
3886
+ *
3887
+ * No other file in the codebase needs to know about the new provider —
3888
+ * the realtime bridge looks providers up by id through the registry.
3889
+ *
3890
+ * Currently every supported provider is an OpenAI-Realtime-compatible
3891
+ * WebSocket. If a future provider diverges enough to need its own wire
3892
+ * protocol (custom event shape, gRPC, WebRTC SDP, etc.), the seam to
3893
+ * extend is here: add `buildSessionUpdate` / `parseInboundEvent` /
3894
+ * etc. hooks to {@link VoiceProvider}, then make `realtime-bridge.ts`
3895
+ * route through them instead of speaking OpenAI Realtime directly.
3896
+ */
3897
+ /**
3898
+ * One voice-runtime provider. Carries enough information for the
3899
+ * bridge to open the right WebSocket with the right auth + model.
3900
+ */
3901
+ interface VoiceProvider {
3902
+ /**
3903
+ * Stable identifier used in mission policy / config (`'openai'`,
3904
+ * `'grok'`, …). Keep lowercase, no spaces — this is what operators
3905
+ * type into `AGENTICMAIL_VOICE_RUNTIME=` or pass via mission policy.
3906
+ */
3907
+ id: string;
3908
+ /** Human-readable display name for logs + the web UI. */
3909
+ displayName: string;
3910
+ /** WebSocket base URL (the bridge appends `?model=…`). */
3911
+ websocketBaseUrl: string;
3912
+ /**
3913
+ * Default model when the caller doesn't pin one. Sent as the
3914
+ * `?model=…` query string AND echoed in the session.update.
3915
+ */
3916
+ defaultModel: string;
3917
+ /**
3918
+ * The env var the operator sets for this provider's API key
3919
+ * (`OPENAI_API_KEY`, `XAI_API_KEY`, …). The bootstrap / config
3920
+ * loader reads from this name and stores into
3921
+ * `AgenticMailConfig.voiceProviderKeys[<id>]`. Used in the
3922
+ * "you didn't set the key" error message too.
3923
+ */
3924
+ apiKeyEnvVar: string;
3925
+ /**
3926
+ * Optional fallback config-field name. When the provider's API key
3927
+ * has a long-standing dedicated config field (e.g. OpenAI's
3928
+ * `config.openaiApiKey`), this lets the resolver check that field
3929
+ * BEFORE looking in the generic `voiceProviderKeys` map — so
3930
+ * existing installs don't need to migrate. Leave undefined for
3931
+ * new providers.
3932
+ */
3933
+ apiKeyConfigField?: 'openaiApiKey';
3934
+ /**
3935
+ * Per-provider notes that surface in error / boot-log messages.
3936
+ * Optional — defaults are fine for the standard providers.
3937
+ */
3938
+ description?: string;
3939
+ /**
3940
+ * v0.9.95 — built-in voice catalogue. The names the operator can
3941
+ * pick when configuring a default voice for the agent or pinning
3942
+ * one per-call. Empty array means "use whatever defaultVoice we
3943
+ * ship" — used by providers like Grok that support arbitrary
3944
+ * voice ids (cloned voices) instead of a fixed list.
3945
+ */
3946
+ voices: string[];
3947
+ /**
3948
+ * v0.9.95 — default voice the bridge picks when neither the
3949
+ * mission policy nor the agent persona pins one. Per-provider:
3950
+ * OpenAI defaults to "marin"; Grok lets the provider pick.
3951
+ */
3952
+ defaultVoice: string;
3953
+ /**
3954
+ * v0.9.95 — true when the provider accepts arbitrary voice ids
3955
+ * beyond `voices` (e.g. Grok's Custom Voices API returns a
3956
+ * `voice_id` that plugs in here). Tells the CLI picker to show a
3957
+ * free-text input + a "paste a custom voice id" option after the
3958
+ * built-in list.
3959
+ */
3960
+ customVoicesSupported?: boolean;
3961
+ }
3962
+ /**
3963
+ * The bridge needs all the inputs needed to open a session in one
3964
+ * resolved struct — URL with model encoded, the API key, the source
3965
+ * for logging. {@link resolveVoiceRuntime} produces this.
3966
+ */
3967
+ interface VoiceRuntimeConnection {
3968
+ providerId: string;
3969
+ providerDisplayName: string;
3970
+ /** Full WebSocket URL including `?model=…`. */
3971
+ url: string;
3972
+ /** Resolved model name (also passed in the URL). */
3973
+ model: string;
3974
+ /** Bearer token for the `Authorization` header. */
3975
+ apiKey: string;
3976
+ /** Human-readable source of the key, for boot logs only (e.g. `"env XAI_API_KEY"`). */
3977
+ apiKeySource: string;
3978
+ /**
3979
+ * v0.9.95 — resolved voice name (sent in session.update under
3980
+ * `audio.output.voice`). Picked from (in order):
3981
+ * 1. caller-passed `options.voice` (mission policy)
3982
+ * 2. agent persona's `voice:` frontmatter
3983
+ * 3. install default in `config.voiceProviderVoices[<providerId>]`
3984
+ * 4. provider's `defaultVoice` (e.g. OpenAI "marin", Grok "ara")
3985
+ */
3986
+ voice: string;
3987
+ /** Human-readable source of the voice pick, for boot logs / audit. */
3988
+ voiceSource: string;
3989
+ }
3990
+
3991
+ /**
3992
+ * Voice-provider registry — drop-in plugin discovery.
3993
+ *
3994
+ * Providers register themselves at module-load time via
3995
+ * {@link registerVoiceProvider}. The barrel `voice-providers/index.ts`
3996
+ * imports each provider file for its side effect, populating this map
3997
+ * before any caller asks for one.
3998
+ *
3999
+ * No reflection, no filesystem scan, no decorator magic — adding a
4000
+ * provider is "create a file + add one import line". That gives us
4001
+ * both first-class TypeScript types AND the file-drop ergonomics.
4002
+ */
4003
+
4004
+ /**
4005
+ * Register a provider. Called once per file at module load. Throws on
4006
+ * duplicate id so an accidental copy-paste collision is loud, not
4007
+ * silently overriding.
4008
+ */
4009
+ declare function registerVoiceProvider(provider: VoiceProvider): void;
4010
+ /** All registered providers — for `agenticmail voice list` / web UI menus. */
4011
+ declare function listVoiceProviders(): VoiceProvider[];
4012
+ /** Look up a provider by id. Returns undefined for unknown ids. */
4013
+ declare function getVoiceProvider(id: string): VoiceProvider | undefined;
4014
+ /**
4015
+ * Resolve a provider id + the runtime's config / overrides into the
4016
+ * connection struct the bridge consumes (URL with model, api key,
4017
+ * source). Throws if the provider id is unknown or its API key isn't
4018
+ * configured — the realtime-ws layer turns the throw into a clear
4019
+ * startup error instead of a mid-call surprise.
4020
+ */
4021
+ declare function resolveVoiceRuntime(providerId: string | undefined, config: AgenticMailConfig, options?: {
4022
+ model?: string;
4023
+ voice?: string;
4024
+ }): VoiceRuntimeConnection;
4025
+
3827
4026
  type PhoneTransportProvider = '46elks' | 'twilio';
3828
4027
  /** Providers that support starting outbound call-control missions. */
3829
4028
  declare const PHONE_CALL_CONTROL_PROVIDERS: readonly PhoneTransportProvider[];
@@ -6235,11 +6434,16 @@ declare function buildDefaultPersona(agentName: string): string;
6235
6434
  /** Resolve the per-agent persona path on disk. */
6236
6435
  declare function personaPathFor(agentName: string): string;
6237
6436
  /**
6238
- * Load the persona for {@link agentName}. Auto-creates the file with
6239
- * {@link buildDefaultPersona} content on first read. Idempotent: a
6240
- * second call returns whatever's on disk. Never throws a permission
6241
- * error or filesystem quirk falls back to the in-memory default so the
6242
- * voice / email / telegram path is never crashed by a missing file.
6437
+ * Load the persona BODY for {@link agentName}. Auto-creates the file
6438
+ * with {@link buildDefaultPersona} content on first read. Idempotent.
6439
+ * Never throws a permission error or filesystem quirk falls back
6440
+ * to the in-memory default so the voice / email / telegram path is
6441
+ * never crashed by a missing file.
6442
+ *
6443
+ * v0.9.95 — if the file has YAML frontmatter (voice / voiceRuntime
6444
+ * keys, written by `agenticmail persona --voice <name>`), the
6445
+ * frontmatter is stripped from the returned string. Use
6446
+ * {@link readAgentPersonaFile} to get the parsed frontmatter too.
6243
6447
  */
6244
6448
  declare function loadAgentPersona(agentName: string): string;
6245
6449
  /**
@@ -6247,5 +6451,43 @@ declare function loadAgentPersona(agentName: string): string;
6247
6451
  * edit command. Returns the path written to.
6248
6452
  */
6249
6453
  declare function saveAgentPersona(agentName: string, content: string): string;
6454
+ /**
6455
+ * v0.9.95 — structured per-agent voice preferences. Stored as YAML
6456
+ * frontmatter at the top of the persona file:
6457
+ *
6458
+ * ---
6459
+ * voice: cedar
6460
+ * voiceRuntime: openai
6461
+ * ---
6462
+ * # Who you are
6463
+ * ...
6464
+ *
6465
+ * Both fields optional. Unknown keys are ignored on read. The CLI's
6466
+ * `agenticmail persona --voice <name>` writes here. The realtime
6467
+ * bridge consults this between the mission policy and the install
6468
+ * default so an agent can have a consistent voice across every call
6469
+ * without the caller pinning it on every invocation.
6470
+ */
6471
+ interface AgentPersonaFrontmatter {
6472
+ voice?: string;
6473
+ voiceRuntime?: string;
6474
+ }
6475
+ /**
6476
+ * Read frontmatter + body from the persona file. Best-effort; missing
6477
+ * file returns empty frontmatter + an auto-seeded body. Robust to:
6478
+ * - No frontmatter at all (legacy files written before 0.9.95).
6479
+ * - Frontmatter with leading whitespace / CRLF.
6480
+ * - Junk lines in the YAML block — we only pick the keys we know.
6481
+ */
6482
+ declare function readAgentPersonaFile(agentName: string): {
6483
+ frontmatter: AgentPersonaFrontmatter;
6484
+ body: string;
6485
+ };
6486
+ /**
6487
+ * Update one or more frontmatter keys on an agent's persona file.
6488
+ * Preserves the existing body. Auto-seeds the body if the file
6489
+ * didn't exist yet.
6490
+ */
6491
+ declare function updateAgentPersonaFrontmatter(agentName: string, patch: AgentPersonaFrontmatter): string;
6250
6492
 
6251
- export { AGENT_ROLES, AGENT_STATE_ROOT, ASK_OPERATOR_TOOL, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryEntry, type AgentMemoryFields, AgentMemoryManager, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type AudioAction, type AudioEditOptions, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeMailContext, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type BridgeWakeRoute, type CachedMessage, CloudflareClient, type CreateAgentOptions, type CreateMemoryInput, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_CALLBACK_POLICY, DEFAULT_EXTENSION_POLICY, DEFAULT_REALTIME_AUDIO_FORMAT, DEFAULT_REALTIME_MODEL, DEFAULT_REALTIME_VOICE, DEFAULT_SESSION_MAX_AGE_MS, DEFAULT_WEB_SEARCH_ENDPOINT, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, ELKS_REALTIME_AUDIO_FORMATS, ELKS_REALTIME_WS_PATH, END_CALL_TOOL, EXTEND_CALL_TIME_TOOL, type ElksRealtimeAudioFormat, type ElksRealtimeAudioMessage, type ElksRealtimeByeMessage, type ElksRealtimeHelloMessage, type ElksRealtimeInboundMessage, type ElksRealtimeOutboundMessage, ElksRealtimeTransport, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, GET_CALL_STATUS_TOOL, GET_DATETIME_TOOL, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type GetDatetimeOptions, type GetUpdatesOptions, type HostName, type HostSession, type HostSessionResumeMode, type ImageAction, type ImageEditOptions, type InboundEmail, type InboundSmsEvent, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, LOAD_SKILL_TOOL, type LinkAdvisory, type LocalSmtpConfig, MEMORY_CATEGORIES, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type MediaBinary, type MediaCapability, type MediaCapabilityReport, type MediaFileResult, type MediaInfoResult, MediaManager, type MediaManagerOptions, type MediaStreamInfo, type MemoryCategory, type MemoryImportance, type MemoryQueryOptions, type MemoryRecaller, MemorySearchIndex, type MemorySource, type MemoryStats, OPENAI_REALTIME_URL, OPERATOR_QUERY_POLL_INTERVAL_MS, OPERATOR_QUERY_SUBJECT_TAG, OPERATOR_QUERY_TIMEOUT_MS, OPERATOR_QUERY_TIMEOUT_SENTINEL, type OpenClawPhoneMissionPolicy, type OperatorQueryNotificationInput, type OperatorQueryPollOptions, type OperatorQueryUrgency, type OperatorReplyKind, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, PERSONA_FILENAME, PHONE_CALLBACK_MAX_DELAY_SECONDS, PHONE_CALLBACK_MIN_DELAY_SECONDS, PHONE_CALL_CONTROL_PROVIDERS, PHONE_MAX_CONCURRENT_MISSIONS, PHONE_MIN_WEBHOOK_SECRET_LENGTH, PHONE_MISSION_STATES, PHONE_RATE_LIMIT_PER_HOUR, PHONE_RATE_LIMIT_PER_MINUTE, PHONE_REGION_SCOPES, PHONE_SERVER_MAX_ATTEMPTS, PHONE_SERVER_MAX_CALLBACK_CHAIN, PHONE_SERVER_MAX_CALL_DURATION_SECONDS, PHONE_SERVER_MAX_COST_PER_MISSION, PHONE_SERVER_MAX_EXTENSION_REQUESTS_PER_CALL, PHONE_SERVER_MAX_EXTENSION_SECONDS_PER_REQUEST, PHONE_SERVER_MAX_TOTAL_EXTENSION_SECONDS, PHONE_TASK_MAX_LENGTH, type ParsedAttachment, type ParsedEmail, type ParsedOperatorReply, type ParsedSms, type ParsedTelegramMessage, PathTraversalError, type PhoneAlternativePolicy, type PhoneCallMission, type PhoneCallbackPolicy, type PhoneConfirmPolicy, type PhoneExtensionPolicy, PhoneManager, type PhoneMissionStartValidationResult, type PhoneMissionState, type PhoneMissionTranscriptEntry, type PhoneMissionValidationIssue, type PhoneMissionValidationResult, type PhoneNumberRisk, type PhoneOperatorQuery, PhoneRateLimitError, type PhoneRegionScope, type PhoneScheduledCallback, type PhoneTransportConfig, type PhoneTransportProfile, type PhoneTransportProvider, type PhoneTransportValidationResult, PhoneWebhookAuthError, type PhoneWebhookResult, type PlanBridgeWakeArgs, type PurchasedDomain, REALTIME_AUDIO_SAMPLE_RATE, REALTIME_MAX_AUDIO_FRAME_BASE64, REALTIME_TOOL_CALL_TIMEOUT_MS, REALTIME_TOOL_DEFINITIONS, RECALL_MEMORY_TOOL, REDACTED, RELAY_PRESETS, type RealtimeBridgePort, type RealtimeBridgeTranscriptEntry, type RealtimeInboundEvent, type RealtimeInstructionOptions, type RealtimeSessionConfigOptions, type RealtimeToolCall, type RealtimeToolDefinition, type RealtimeToolHandler, type RealtimeToolResult, type RealtimeTransportAdapter, type RealtimeTransportProvider, RealtimeVoiceBridge, type RealtimeVoiceBridgeOptions, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SCHEDULE_CALLBACK_TOOL, SEARCH_EMAIL_TOOL, SEARCH_SKILLS_TOOL, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type ScheduledCallbackRequest, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, type SendTelegramMessageOptions, type SendTelegramMessageResult, ServiceManager, type ServiceStatus, type SetWebhookOptions, type SetupConfig, SetupManager, type SetupResult, type Severity, type Skill, type SkillCategory, type SkillContext, type SkillExitStrategy, type SkillSummary, type SkillTactic, type SkillValidationError, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type StartPhoneCallOptions, type StartPhoneCallResult, type StartPhoneMissionInput, TELEGRAM_API_BASE, TELEGRAM_CHUNK_SIZE, TELEGRAM_MESSAGE_LIMIT, TELEGRAM_MIN_WEBHOOK_SECRET_LENGTH, TELEGRAM_OPERATOR_QUERY_TAG, TELEGRAM_STOP_WORDS, TELEGRAM_WEBHOOK_SECRET_RE, TELEPHONY_TRANSPORT_CAPABILITIES, TWILIO_MEDIA_SAMPLE_RATE, TWILIO_REALTIME_WS_PATH, TelegramApiError, type TelegramApiOptions, type TelegramBotInfo, type TelegramChatType, type TelegramConfig, TelegramManager, type TelegramMessage, type TelegramMode, type TelephonyTransportCapability, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type ToolExecutor, type TtsGenerateOptions, type TunnelConfig, TunnelManager, type TwilioConnectedMessage, type TwilioMarkMessage, type TwilioMediaMessage, type TwilioRealtimeInboundMessage, type TwilioRealtimeOutboundMessage, TwilioRealtimeTransport, type TwilioStartMessage, type TwilioStopMessage, type TwilioStreamTwiMLOptions, UnsafeApiUrlError, type UpdateMemoryInput, type ValidatedPhoneMissionStart, type VideoAction, type VideoEditOptions, type VideoTimelineEntry, type VideoUnderstandOptions, type VideoUnderstandResult, type VoiceCloneOptions, WARNING_THRESHOLD, WEB_SEARCH_TOOL, WEB_SEARCH_UNTRUSTED_PREFIX, type WatcherOptions, type WebSearchOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildDefaultPersona, buildElksAudioMessage, buildElksByeMessage, buildElksHandshakeMessages, buildElksInterruptMessage, buildElksListeningMessage, buildElksSendingMessage, buildInboundSecurityAdvisory, buildOpenAIRealtimeUrl, buildPhoneTransportConfig, buildRealtimeInstructions, buildRealtimeSessionConfig, buildRealtimeToolGuidance, buildTwilioClearMessage, buildTwilioMarkMessage, buildTwilioMediaMessage, buildTwilioSayTwiML, buildTwilioSignature, buildTwilioStreamTwiML, callTelegramApi, classifyEmailRoute, classifyPhoneNumberRisk, classifyResumeError, clearMediaCapabilityCache, closeDatabase, composeBridgeWakePrompt, createRealtimeTransport, createTestDatabase, createToolExecutor, debug, debugWarn, deleteTelegramWebhook, detectBinary, ensureDataDir, escapeXml, extractEmailAddress, extractVerificationCode, flushTelemetry, forgetHostSession, formatOperatorQueryTelegramMessage, getDatabase, getDatetime, getMediaCapabilities, getOperatorEmail, getSmsProvider, getTelegramChat, getTelegramMe, getTelegramUpdates, getTelegramWebhookInfo, hostSessionStoragePath, inferPhoneRegion, invalidateSkillCache, isInternalEmail, isLoopbackMailHost, isOperatorReplySender, isPhoneRegionAllowed, isSessionFresh, isTelegramChatAllowed, isTelegramStopCommand, isValidPhoneNumber, listSkills, loadAgentPersona, loadHostSession, loadSkill, mapProviderSmsStatus, nextTelegramOffset, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, operatorQuerySubject, parseElksRealtimeMessage, parseEmail, parseGoogleVoiceSms, parseOperatorQueryReply, parseTelegramOperatorReply, parseTelegramUpdate, parseTwilioRealtimeMessage, personaPathFor, planBridgeWake, pollForOperatorAnswer, recallMemory, recordToolCall, redactBotToken, redactObject, redactPhoneTransportConfig, redactSecret, redactSmsConfig, redactTelegramConfig, renderSkillAsPrompt, requireBinary, requireWhisperModel, resolveCallbackPolicy, resolveConfig, resolveExtensionPolicy, resolveTlsRejectUnauthorized, safeJoin, sanitizeEmail, saveAgentPersona, saveConfig, saveHostSession, saveUserSkill, scanOutboundEmail, scoreEmail, searchSkills, sendTelegramMessage, setOperatorEmail, setTelegramWebhook, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, splitTelegramMessage, startRelayBridge, stem, stripTelegramMarkdown, threadIdFor, tokenize, tryJoin, userSkillsDir, validateApiUrl, validatePhoneMissionPolicy, validatePhoneMissionStart, validatePhoneTransportProfile, validateSkill, validateTwilioSignature, webSearch };
6493
+ export { AGENT_ROLES, AGENT_STATE_ROOT, ASK_OPERATOR_TOOL, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryEntry, type AgentMemoryFields, AgentMemoryManager, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentPersonaFrontmatter, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type AudioAction, type AudioEditOptions, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeMailContext, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type BridgeWakeRoute, type CachedMessage, CloudflareClient, type CreateAgentOptions, type CreateMemoryInput, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_CALLBACK_POLICY, DEFAULT_EXTENSION_POLICY, DEFAULT_REALTIME_AUDIO_FORMAT, DEFAULT_REALTIME_MODEL, DEFAULT_REALTIME_VOICE, DEFAULT_SESSION_MAX_AGE_MS, DEFAULT_WEB_SEARCH_ENDPOINT, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, ELKS_REALTIME_AUDIO_FORMATS, ELKS_REALTIME_WS_PATH, END_CALL_TOOL, EXTEND_CALL_TIME_TOOL, type ElksRealtimeAudioFormat, type ElksRealtimeAudioMessage, type ElksRealtimeByeMessage, type ElksRealtimeHelloMessage, type ElksRealtimeInboundMessage, type ElksRealtimeOutboundMessage, ElksRealtimeTransport, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, GET_CALL_STATUS_TOOL, GET_DATETIME_TOOL, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type GetDatetimeOptions, type GetUpdatesOptions, type HostName, type HostSession, type HostSessionResumeMode, type ImageAction, type ImageEditOptions, type InboundEmail, type InboundSmsEvent, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, LOAD_SKILL_TOOL, type LinkAdvisory, type LocalSmtpConfig, MEMORY_CATEGORIES, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type MediaBinary, type MediaCapability, type MediaCapabilityReport, type MediaFileResult, type MediaInfoResult, MediaManager, type MediaManagerOptions, type MediaStreamInfo, type MemoryCategory, type MemoryImportance, type MemoryQueryOptions, type MemoryRecaller, MemorySearchIndex, type MemorySource, type MemoryStats, OPENAI_REALTIME_URL, OPERATOR_QUERY_POLL_INTERVAL_MS, OPERATOR_QUERY_SUBJECT_TAG, OPERATOR_QUERY_TIMEOUT_MS, OPERATOR_QUERY_TIMEOUT_SENTINEL, type OpenClawPhoneMissionPolicy, type OperatorQueryNotificationInput, type OperatorQueryPollOptions, type OperatorQueryUrgency, type OperatorReplyKind, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, PERSONA_FILENAME, PHONE_CALLBACK_MAX_DELAY_SECONDS, PHONE_CALLBACK_MIN_DELAY_SECONDS, PHONE_CALL_CONTROL_PROVIDERS, PHONE_MAX_CONCURRENT_MISSIONS, PHONE_MIN_WEBHOOK_SECRET_LENGTH, PHONE_MISSION_STATES, PHONE_RATE_LIMIT_PER_HOUR, PHONE_RATE_LIMIT_PER_MINUTE, PHONE_REGION_SCOPES, PHONE_SERVER_MAX_ATTEMPTS, PHONE_SERVER_MAX_CALLBACK_CHAIN, PHONE_SERVER_MAX_CALL_DURATION_SECONDS, PHONE_SERVER_MAX_COST_PER_MISSION, PHONE_SERVER_MAX_EXTENSION_REQUESTS_PER_CALL, PHONE_SERVER_MAX_EXTENSION_SECONDS_PER_REQUEST, PHONE_SERVER_MAX_TOTAL_EXTENSION_SECONDS, PHONE_TASK_MAX_LENGTH, type ParsedAttachment, type ParsedEmail, type ParsedOperatorReply, type ParsedSms, type ParsedTelegramMessage, PathTraversalError, type PhoneAlternativePolicy, type PhoneCallMission, type PhoneCallbackPolicy, type PhoneConfirmPolicy, type PhoneExtensionPolicy, PhoneManager, type PhoneMissionStartValidationResult, type PhoneMissionState, type PhoneMissionTranscriptEntry, type PhoneMissionValidationIssue, type PhoneMissionValidationResult, type PhoneNumberRisk, type PhoneOperatorQuery, PhoneRateLimitError, type PhoneRegionScope, type PhoneScheduledCallback, type PhoneTransportConfig, type PhoneTransportProfile, type PhoneTransportProvider, type PhoneTransportValidationResult, PhoneWebhookAuthError, type PhoneWebhookResult, type PlanBridgeWakeArgs, type PurchasedDomain, REALTIME_AUDIO_SAMPLE_RATE, REALTIME_MAX_AUDIO_FRAME_BASE64, REALTIME_TOOL_CALL_TIMEOUT_MS, REALTIME_TOOL_DEFINITIONS, RECALL_MEMORY_TOOL, REDACTED, RELAY_PRESETS, type RealtimeBridgePort, type RealtimeBridgeTranscriptEntry, type RealtimeInboundEvent, type RealtimeInstructionOptions, type RealtimeSessionConfigOptions, type RealtimeToolCall, type RealtimeToolDefinition, type RealtimeToolHandler, type RealtimeToolResult, type RealtimeTransportAdapter, type RealtimeTransportProvider, RealtimeVoiceBridge, type RealtimeVoiceBridgeOptions, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SCHEDULE_CALLBACK_TOOL, SEARCH_EMAIL_TOOL, SEARCH_SKILLS_TOOL, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type ScheduledCallbackRequest, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, type SendTelegramMessageOptions, type SendTelegramMessageResult, ServiceManager, type ServiceStatus, type SetWebhookOptions, type SetupConfig, SetupManager, type SetupResult, type Severity, type Skill, type SkillCategory, type SkillContext, type SkillExitStrategy, type SkillSummary, type SkillTactic, type SkillValidationError, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type StartPhoneCallOptions, type StartPhoneCallResult, type StartPhoneMissionInput, TELEGRAM_API_BASE, TELEGRAM_CHUNK_SIZE, TELEGRAM_MESSAGE_LIMIT, TELEGRAM_MIN_WEBHOOK_SECRET_LENGTH, TELEGRAM_OPERATOR_QUERY_TAG, TELEGRAM_STOP_WORDS, TELEGRAM_WEBHOOK_SECRET_RE, TELEPHONY_TRANSPORT_CAPABILITIES, TWILIO_MEDIA_SAMPLE_RATE, TWILIO_REALTIME_WS_PATH, TelegramApiError, type TelegramApiOptions, type TelegramBotInfo, type TelegramChatType, type TelegramConfig, TelegramManager, type TelegramMessage, type TelegramMode, type TelephonyTransportCapability, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type ToolExecutor, type TtsGenerateOptions, type TunnelConfig, TunnelManager, type TwilioConnectedMessage, type TwilioMarkMessage, type TwilioMediaMessage, type TwilioRealtimeInboundMessage, type TwilioRealtimeOutboundMessage, TwilioRealtimeTransport, type TwilioStartMessage, type TwilioStopMessage, type TwilioStreamTwiMLOptions, UnsafeApiUrlError, type UpdateMemoryInput, type ValidatedPhoneMissionStart, type VideoAction, type VideoEditOptions, type VideoTimelineEntry, type VideoUnderstandOptions, type VideoUnderstandResult, type VoiceCloneOptions, type VoiceProvider, type VoiceRuntimeConnection, WARNING_THRESHOLD, WEB_SEARCH_TOOL, WEB_SEARCH_UNTRUSTED_PREFIX, type WatcherOptions, type WebSearchOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildDefaultPersona, buildElksAudioMessage, buildElksByeMessage, buildElksHandshakeMessages, buildElksInterruptMessage, buildElksListeningMessage, buildElksSendingMessage, buildInboundSecurityAdvisory, buildOpenAIRealtimeUrl, buildPhoneTransportConfig, buildRealtimeInstructions, buildRealtimeSessionConfig, buildRealtimeToolGuidance, buildTwilioClearMessage, buildTwilioMarkMessage, buildTwilioMediaMessage, buildTwilioSayTwiML, buildTwilioSignature, buildTwilioStreamTwiML, callTelegramApi, classifyEmailRoute, classifyPhoneNumberRisk, classifyResumeError, clearMediaCapabilityCache, closeDatabase, composeBridgeWakePrompt, createRealtimeTransport, createTestDatabase, createToolExecutor, debug, debugWarn, deleteTelegramWebhook, detectBinary, ensureDataDir, escapeXml, extractEmailAddress, extractVerificationCode, flushTelemetry, forgetHostSession, formatOperatorQueryTelegramMessage, getDatabase, getDatetime, getMediaCapabilities, getOperatorEmail, getSmsProvider, getTelegramChat, getTelegramMe, getTelegramUpdates, getTelegramWebhookInfo, getVoiceProvider, hostSessionStoragePath, inferPhoneRegion, invalidateSkillCache, isInternalEmail, isLoopbackMailHost, isOperatorReplySender, isPhoneRegionAllowed, isSessionFresh, isTelegramChatAllowed, isTelegramStopCommand, isValidPhoneNumber, listSkills, listVoiceProviders, loadAgentPersona, loadHostSession, loadSkill, mapProviderSmsStatus, nextTelegramOffset, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, operatorQuerySubject, parseElksRealtimeMessage, parseEmail, parseGoogleVoiceSms, parseOperatorQueryReply, parseTelegramOperatorReply, parseTelegramUpdate, parseTwilioRealtimeMessage, personaPathFor, planBridgeWake, pollForOperatorAnswer, readAgentPersonaFile, recallMemory, recordToolCall, redactBotToken, redactObject, redactPhoneTransportConfig, redactSecret, redactSmsConfig, redactTelegramConfig, registerVoiceProvider, renderSkillAsPrompt, requireBinary, requireWhisperModel, resolveCallbackPolicy, resolveConfig, resolveExtensionPolicy, resolveTlsRejectUnauthorized, resolveVoiceRuntime, safeJoin, sanitizeEmail, saveAgentPersona, saveConfig, saveHostSession, saveUserSkill, scanOutboundEmail, scoreEmail, searchSkills, sendTelegramMessage, setOperatorEmail, setTelegramWebhook, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, splitTelegramMessage, startRelayBridge, stem, stripTelegramMarkdown, threadIdFor, tokenize, tryJoin, updateAgentPersonaFrontmatter, userSkillsDir, validateApiUrl, validatePhoneMissionPolicy, validatePhoneMissionStart, validatePhoneTransportProfile, validateSkill, validateTwilioSignature, webSearch };
package/dist/index.d.ts CHANGED
@@ -229,6 +229,31 @@ interface AgenticMailConfig {
229
229
  * `config.json`. Optional — no other feature depends on it.
230
230
  */
231
231
  openaiApiKey?: string;
232
+ /**
233
+ * v0.9.93 — voice-runtime provider keys. Each is a thin Bearer-token
234
+ * for a provider's realtime API; required only when that provider
235
+ * is selected as the runtime for a call. New providers register
236
+ * themselves in `packages/core/src/phone/voice-providers/` and
237
+ * declare which key field they consume — this record is the central
238
+ * pool. Read from per-provider env vars (e.g. `XAI_API_KEY`) at boot.
239
+ */
240
+ voiceProviderKeys?: Record<string, string>;
241
+ /**
242
+ * v0.9.95 — install-wide default voice CHARACTER per provider
243
+ * (e.g. `{ openai: 'cedar', grok: 'ara' }`). Optional — when a
244
+ * provider's slot is empty, the bridge falls back to the
245
+ * provider's own `defaultVoice`. Per-agent persona + per-call
246
+ * mission policy still beat this. The CLI's `agenticmail
247
+ * setup-voice` voice picker writes here.
248
+ */
249
+ voiceProviderVoices?: Record<string, string>;
250
+ /**
251
+ * v0.9.93 — default voice-runtime provider id for phone missions
252
+ * that don't pin one on their own policy. `'openai'` (the existing
253
+ * default) or any provider registered in `voice-providers/`.
254
+ * Read from `AGENTICMAIL_VOICE_RUNTIME` env var or `config.json`.
255
+ */
256
+ voiceRuntime?: string;
232
257
  masterKey: string;
233
258
  dataDir: string;
234
259
  }
@@ -2748,6 +2773,25 @@ interface OpenClawPhoneMissionPolicy {
2748
2773
  * {@link DEFAULT_CALLBACK_POLICY} when omitted.
2749
2774
  */
2750
2775
  callbackPolicy?: PhoneCallbackPolicy;
2776
+ /**
2777
+ * v0.9.95 — voice-runtime provider id ('openai', 'grok', any
2778
+ * future plugin). Per-call override that beats agent persona
2779
+ * frontmatter, install default, and bridge default. Optional.
2780
+ */
2781
+ voiceRuntime?: string;
2782
+ /**
2783
+ * v0.9.95 — model name override (e.g. `'gpt-realtime-mini'` for
2784
+ * cost-tuning, `'grok-voice-fast'` for latency). Optional.
2785
+ */
2786
+ voiceModel?: string;
2787
+ /**
2788
+ * v0.9.95 — voice CHARACTER override (e.g. `'cedar'`, `'ara'`, or
2789
+ * a custom-voice id from Grok). Optional. Validated against the
2790
+ * provider's catalogue at session-open time; unknown names against
2791
+ * a fixed-catalogue provider fall through to the provider default
2792
+ * with a log warning.
2793
+ */
2794
+ voice?: string;
2751
2795
  }
2752
2796
  interface StartPhoneMissionInput {
2753
2797
  to: string;
@@ -3824,6 +3868,161 @@ declare class RealtimeVoiceBridge {
3824
3868
  private safeSend;
3825
3869
  }
3826
3870
 
3871
+ /**
3872
+ * Voice-runtime provider plugin interface.
3873
+ *
3874
+ * Each backend (OpenAI Realtime, Grok Voice Agent, future Anthropic
3875
+ * realtime, Cartesia, ElevenLabs ConvAI, etc.) lives in its own file
3876
+ * under `packages/core/src/phone/voice-providers/` and registers
3877
+ * itself with the registry by calling {@link registerVoiceProvider}
3878
+ * at module load.
3879
+ *
3880
+ * Adding a new provider is meant to be a literal FILE DROP:
3881
+ *
3882
+ * 1. Create `voice-providers/<id>.ts` exporting a {@link VoiceProvider}.
3883
+ * 2. Add a single `import './<id>.js';` line to `voice-providers/index.ts`
3884
+ * so the side-effect registration runs.
3885
+ * 3. (Optional) document the env var the provider expects.
3886
+ *
3887
+ * No other file in the codebase needs to know about the new provider —
3888
+ * the realtime bridge looks providers up by id through the registry.
3889
+ *
3890
+ * Currently every supported provider is an OpenAI-Realtime-compatible
3891
+ * WebSocket. If a future provider diverges enough to need its own wire
3892
+ * protocol (custom event shape, gRPC, WebRTC SDP, etc.), the seam to
3893
+ * extend is here: add `buildSessionUpdate` / `parseInboundEvent` /
3894
+ * etc. hooks to {@link VoiceProvider}, then make `realtime-bridge.ts`
3895
+ * route through them instead of speaking OpenAI Realtime directly.
3896
+ */
3897
+ /**
3898
+ * One voice-runtime provider. Carries enough information for the
3899
+ * bridge to open the right WebSocket with the right auth + model.
3900
+ */
3901
+ interface VoiceProvider {
3902
+ /**
3903
+ * Stable identifier used in mission policy / config (`'openai'`,
3904
+ * `'grok'`, …). Keep lowercase, no spaces — this is what operators
3905
+ * type into `AGENTICMAIL_VOICE_RUNTIME=` or pass via mission policy.
3906
+ */
3907
+ id: string;
3908
+ /** Human-readable display name for logs + the web UI. */
3909
+ displayName: string;
3910
+ /** WebSocket base URL (the bridge appends `?model=…`). */
3911
+ websocketBaseUrl: string;
3912
+ /**
3913
+ * Default model when the caller doesn't pin one. Sent as the
3914
+ * `?model=…` query string AND echoed in the session.update.
3915
+ */
3916
+ defaultModel: string;
3917
+ /**
3918
+ * The env var the operator sets for this provider's API key
3919
+ * (`OPENAI_API_KEY`, `XAI_API_KEY`, …). The bootstrap / config
3920
+ * loader reads from this name and stores into
3921
+ * `AgenticMailConfig.voiceProviderKeys[<id>]`. Used in the
3922
+ * "you didn't set the key" error message too.
3923
+ */
3924
+ apiKeyEnvVar: string;
3925
+ /**
3926
+ * Optional fallback config-field name. When the provider's API key
3927
+ * has a long-standing dedicated config field (e.g. OpenAI's
3928
+ * `config.openaiApiKey`), this lets the resolver check that field
3929
+ * BEFORE looking in the generic `voiceProviderKeys` map — so
3930
+ * existing installs don't need to migrate. Leave undefined for
3931
+ * new providers.
3932
+ */
3933
+ apiKeyConfigField?: 'openaiApiKey';
3934
+ /**
3935
+ * Per-provider notes that surface in error / boot-log messages.
3936
+ * Optional — defaults are fine for the standard providers.
3937
+ */
3938
+ description?: string;
3939
+ /**
3940
+ * v0.9.95 — built-in voice catalogue. The names the operator can
3941
+ * pick when configuring a default voice for the agent or pinning
3942
+ * one per-call. Empty array means "use whatever defaultVoice we
3943
+ * ship" — used by providers like Grok that support arbitrary
3944
+ * voice ids (cloned voices) instead of a fixed list.
3945
+ */
3946
+ voices: string[];
3947
+ /**
3948
+ * v0.9.95 — default voice the bridge picks when neither the
3949
+ * mission policy nor the agent persona pins one. Per-provider:
3950
+ * OpenAI defaults to "marin"; Grok lets the provider pick.
3951
+ */
3952
+ defaultVoice: string;
3953
+ /**
3954
+ * v0.9.95 — true when the provider accepts arbitrary voice ids
3955
+ * beyond `voices` (e.g. Grok's Custom Voices API returns a
3956
+ * `voice_id` that plugs in here). Tells the CLI picker to show a
3957
+ * free-text input + a "paste a custom voice id" option after the
3958
+ * built-in list.
3959
+ */
3960
+ customVoicesSupported?: boolean;
3961
+ }
3962
+ /**
3963
+ * The bridge needs all the inputs needed to open a session in one
3964
+ * resolved struct — URL with model encoded, the API key, the source
3965
+ * for logging. {@link resolveVoiceRuntime} produces this.
3966
+ */
3967
+ interface VoiceRuntimeConnection {
3968
+ providerId: string;
3969
+ providerDisplayName: string;
3970
+ /** Full WebSocket URL including `?model=…`. */
3971
+ url: string;
3972
+ /** Resolved model name (also passed in the URL). */
3973
+ model: string;
3974
+ /** Bearer token for the `Authorization` header. */
3975
+ apiKey: string;
3976
+ /** Human-readable source of the key, for boot logs only (e.g. `"env XAI_API_KEY"`). */
3977
+ apiKeySource: string;
3978
+ /**
3979
+ * v0.9.95 — resolved voice name (sent in session.update under
3980
+ * `audio.output.voice`). Picked from (in order):
3981
+ * 1. caller-passed `options.voice` (mission policy)
3982
+ * 2. agent persona's `voice:` frontmatter
3983
+ * 3. install default in `config.voiceProviderVoices[<providerId>]`
3984
+ * 4. provider's `defaultVoice` (e.g. OpenAI "marin", Grok "ara")
3985
+ */
3986
+ voice: string;
3987
+ /** Human-readable source of the voice pick, for boot logs / audit. */
3988
+ voiceSource: string;
3989
+ }
3990
+
3991
+ /**
3992
+ * Voice-provider registry — drop-in plugin discovery.
3993
+ *
3994
+ * Providers register themselves at module-load time via
3995
+ * {@link registerVoiceProvider}. The barrel `voice-providers/index.ts`
3996
+ * imports each provider file for its side effect, populating this map
3997
+ * before any caller asks for one.
3998
+ *
3999
+ * No reflection, no filesystem scan, no decorator magic — adding a
4000
+ * provider is "create a file + add one import line". That gives us
4001
+ * both first-class TypeScript types AND the file-drop ergonomics.
4002
+ */
4003
+
4004
+ /**
4005
+ * Register a provider. Called once per file at module load. Throws on
4006
+ * duplicate id so an accidental copy-paste collision is loud, not
4007
+ * silently overriding.
4008
+ */
4009
+ declare function registerVoiceProvider(provider: VoiceProvider): void;
4010
+ /** All registered providers — for `agenticmail voice list` / web UI menus. */
4011
+ declare function listVoiceProviders(): VoiceProvider[];
4012
+ /** Look up a provider by id. Returns undefined for unknown ids. */
4013
+ declare function getVoiceProvider(id: string): VoiceProvider | undefined;
4014
+ /**
4015
+ * Resolve a provider id + the runtime's config / overrides into the
4016
+ * connection struct the bridge consumes (URL with model, api key,
4017
+ * source). Throws if the provider id is unknown or its API key isn't
4018
+ * configured — the realtime-ws layer turns the throw into a clear
4019
+ * startup error instead of a mid-call surprise.
4020
+ */
4021
+ declare function resolveVoiceRuntime(providerId: string | undefined, config: AgenticMailConfig, options?: {
4022
+ model?: string;
4023
+ voice?: string;
4024
+ }): VoiceRuntimeConnection;
4025
+
3827
4026
  type PhoneTransportProvider = '46elks' | 'twilio';
3828
4027
  /** Providers that support starting outbound call-control missions. */
3829
4028
  declare const PHONE_CALL_CONTROL_PROVIDERS: readonly PhoneTransportProvider[];
@@ -6235,11 +6434,16 @@ declare function buildDefaultPersona(agentName: string): string;
6235
6434
  /** Resolve the per-agent persona path on disk. */
6236
6435
  declare function personaPathFor(agentName: string): string;
6237
6436
  /**
6238
- * Load the persona for {@link agentName}. Auto-creates the file with
6239
- * {@link buildDefaultPersona} content on first read. Idempotent: a
6240
- * second call returns whatever's on disk. Never throws a permission
6241
- * error or filesystem quirk falls back to the in-memory default so the
6242
- * voice / email / telegram path is never crashed by a missing file.
6437
+ * Load the persona BODY for {@link agentName}. Auto-creates the file
6438
+ * with {@link buildDefaultPersona} content on first read. Idempotent.
6439
+ * Never throws a permission error or filesystem quirk falls back
6440
+ * to the in-memory default so the voice / email / telegram path is
6441
+ * never crashed by a missing file.
6442
+ *
6443
+ * v0.9.95 — if the file has YAML frontmatter (voice / voiceRuntime
6444
+ * keys, written by `agenticmail persona --voice <name>`), the
6445
+ * frontmatter is stripped from the returned string. Use
6446
+ * {@link readAgentPersonaFile} to get the parsed frontmatter too.
6243
6447
  */
6244
6448
  declare function loadAgentPersona(agentName: string): string;
6245
6449
  /**
@@ -6247,5 +6451,43 @@ declare function loadAgentPersona(agentName: string): string;
6247
6451
  * edit command. Returns the path written to.
6248
6452
  */
6249
6453
  declare function saveAgentPersona(agentName: string, content: string): string;
6454
+ /**
6455
+ * v0.9.95 — structured per-agent voice preferences. Stored as YAML
6456
+ * frontmatter at the top of the persona file:
6457
+ *
6458
+ * ---
6459
+ * voice: cedar
6460
+ * voiceRuntime: openai
6461
+ * ---
6462
+ * # Who you are
6463
+ * ...
6464
+ *
6465
+ * Both fields optional. Unknown keys are ignored on read. The CLI's
6466
+ * `agenticmail persona --voice <name>` writes here. The realtime
6467
+ * bridge consults this between the mission policy and the install
6468
+ * default so an agent can have a consistent voice across every call
6469
+ * without the caller pinning it on every invocation.
6470
+ */
6471
+ interface AgentPersonaFrontmatter {
6472
+ voice?: string;
6473
+ voiceRuntime?: string;
6474
+ }
6475
+ /**
6476
+ * Read frontmatter + body from the persona file. Best-effort; missing
6477
+ * file returns empty frontmatter + an auto-seeded body. Robust to:
6478
+ * - No frontmatter at all (legacy files written before 0.9.95).
6479
+ * - Frontmatter with leading whitespace / CRLF.
6480
+ * - Junk lines in the YAML block — we only pick the keys we know.
6481
+ */
6482
+ declare function readAgentPersonaFile(agentName: string): {
6483
+ frontmatter: AgentPersonaFrontmatter;
6484
+ body: string;
6485
+ };
6486
+ /**
6487
+ * Update one or more frontmatter keys on an agent's persona file.
6488
+ * Preserves the existing body. Auto-seeds the body if the file
6489
+ * didn't exist yet.
6490
+ */
6491
+ declare function updateAgentPersonaFrontmatter(agentName: string, patch: AgentPersonaFrontmatter): string;
6250
6492
 
6251
- export { AGENT_ROLES, AGENT_STATE_ROOT, ASK_OPERATOR_TOOL, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryEntry, type AgentMemoryFields, AgentMemoryManager, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type AudioAction, type AudioEditOptions, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeMailContext, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type BridgeWakeRoute, type CachedMessage, CloudflareClient, type CreateAgentOptions, type CreateMemoryInput, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_CALLBACK_POLICY, DEFAULT_EXTENSION_POLICY, DEFAULT_REALTIME_AUDIO_FORMAT, DEFAULT_REALTIME_MODEL, DEFAULT_REALTIME_VOICE, DEFAULT_SESSION_MAX_AGE_MS, DEFAULT_WEB_SEARCH_ENDPOINT, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, ELKS_REALTIME_AUDIO_FORMATS, ELKS_REALTIME_WS_PATH, END_CALL_TOOL, EXTEND_CALL_TIME_TOOL, type ElksRealtimeAudioFormat, type ElksRealtimeAudioMessage, type ElksRealtimeByeMessage, type ElksRealtimeHelloMessage, type ElksRealtimeInboundMessage, type ElksRealtimeOutboundMessage, ElksRealtimeTransport, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, GET_CALL_STATUS_TOOL, GET_DATETIME_TOOL, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type GetDatetimeOptions, type GetUpdatesOptions, type HostName, type HostSession, type HostSessionResumeMode, type ImageAction, type ImageEditOptions, type InboundEmail, type InboundSmsEvent, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, LOAD_SKILL_TOOL, type LinkAdvisory, type LocalSmtpConfig, MEMORY_CATEGORIES, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type MediaBinary, type MediaCapability, type MediaCapabilityReport, type MediaFileResult, type MediaInfoResult, MediaManager, type MediaManagerOptions, type MediaStreamInfo, type MemoryCategory, type MemoryImportance, type MemoryQueryOptions, type MemoryRecaller, MemorySearchIndex, type MemorySource, type MemoryStats, OPENAI_REALTIME_URL, OPERATOR_QUERY_POLL_INTERVAL_MS, OPERATOR_QUERY_SUBJECT_TAG, OPERATOR_QUERY_TIMEOUT_MS, OPERATOR_QUERY_TIMEOUT_SENTINEL, type OpenClawPhoneMissionPolicy, type OperatorQueryNotificationInput, type OperatorQueryPollOptions, type OperatorQueryUrgency, type OperatorReplyKind, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, PERSONA_FILENAME, PHONE_CALLBACK_MAX_DELAY_SECONDS, PHONE_CALLBACK_MIN_DELAY_SECONDS, PHONE_CALL_CONTROL_PROVIDERS, PHONE_MAX_CONCURRENT_MISSIONS, PHONE_MIN_WEBHOOK_SECRET_LENGTH, PHONE_MISSION_STATES, PHONE_RATE_LIMIT_PER_HOUR, PHONE_RATE_LIMIT_PER_MINUTE, PHONE_REGION_SCOPES, PHONE_SERVER_MAX_ATTEMPTS, PHONE_SERVER_MAX_CALLBACK_CHAIN, PHONE_SERVER_MAX_CALL_DURATION_SECONDS, PHONE_SERVER_MAX_COST_PER_MISSION, PHONE_SERVER_MAX_EXTENSION_REQUESTS_PER_CALL, PHONE_SERVER_MAX_EXTENSION_SECONDS_PER_REQUEST, PHONE_SERVER_MAX_TOTAL_EXTENSION_SECONDS, PHONE_TASK_MAX_LENGTH, type ParsedAttachment, type ParsedEmail, type ParsedOperatorReply, type ParsedSms, type ParsedTelegramMessage, PathTraversalError, type PhoneAlternativePolicy, type PhoneCallMission, type PhoneCallbackPolicy, type PhoneConfirmPolicy, type PhoneExtensionPolicy, PhoneManager, type PhoneMissionStartValidationResult, type PhoneMissionState, type PhoneMissionTranscriptEntry, type PhoneMissionValidationIssue, type PhoneMissionValidationResult, type PhoneNumberRisk, type PhoneOperatorQuery, PhoneRateLimitError, type PhoneRegionScope, type PhoneScheduledCallback, type PhoneTransportConfig, type PhoneTransportProfile, type PhoneTransportProvider, type PhoneTransportValidationResult, PhoneWebhookAuthError, type PhoneWebhookResult, type PlanBridgeWakeArgs, type PurchasedDomain, REALTIME_AUDIO_SAMPLE_RATE, REALTIME_MAX_AUDIO_FRAME_BASE64, REALTIME_TOOL_CALL_TIMEOUT_MS, REALTIME_TOOL_DEFINITIONS, RECALL_MEMORY_TOOL, REDACTED, RELAY_PRESETS, type RealtimeBridgePort, type RealtimeBridgeTranscriptEntry, type RealtimeInboundEvent, type RealtimeInstructionOptions, type RealtimeSessionConfigOptions, type RealtimeToolCall, type RealtimeToolDefinition, type RealtimeToolHandler, type RealtimeToolResult, type RealtimeTransportAdapter, type RealtimeTransportProvider, RealtimeVoiceBridge, type RealtimeVoiceBridgeOptions, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SCHEDULE_CALLBACK_TOOL, SEARCH_EMAIL_TOOL, SEARCH_SKILLS_TOOL, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type ScheduledCallbackRequest, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, type SendTelegramMessageOptions, type SendTelegramMessageResult, ServiceManager, type ServiceStatus, type SetWebhookOptions, type SetupConfig, SetupManager, type SetupResult, type Severity, type Skill, type SkillCategory, type SkillContext, type SkillExitStrategy, type SkillSummary, type SkillTactic, type SkillValidationError, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type StartPhoneCallOptions, type StartPhoneCallResult, type StartPhoneMissionInput, TELEGRAM_API_BASE, TELEGRAM_CHUNK_SIZE, TELEGRAM_MESSAGE_LIMIT, TELEGRAM_MIN_WEBHOOK_SECRET_LENGTH, TELEGRAM_OPERATOR_QUERY_TAG, TELEGRAM_STOP_WORDS, TELEGRAM_WEBHOOK_SECRET_RE, TELEPHONY_TRANSPORT_CAPABILITIES, TWILIO_MEDIA_SAMPLE_RATE, TWILIO_REALTIME_WS_PATH, TelegramApiError, type TelegramApiOptions, type TelegramBotInfo, type TelegramChatType, type TelegramConfig, TelegramManager, type TelegramMessage, type TelegramMode, type TelephonyTransportCapability, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type ToolExecutor, type TtsGenerateOptions, type TunnelConfig, TunnelManager, type TwilioConnectedMessage, type TwilioMarkMessage, type TwilioMediaMessage, type TwilioRealtimeInboundMessage, type TwilioRealtimeOutboundMessage, TwilioRealtimeTransport, type TwilioStartMessage, type TwilioStopMessage, type TwilioStreamTwiMLOptions, UnsafeApiUrlError, type UpdateMemoryInput, type ValidatedPhoneMissionStart, type VideoAction, type VideoEditOptions, type VideoTimelineEntry, type VideoUnderstandOptions, type VideoUnderstandResult, type VoiceCloneOptions, WARNING_THRESHOLD, WEB_SEARCH_TOOL, WEB_SEARCH_UNTRUSTED_PREFIX, type WatcherOptions, type WebSearchOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildDefaultPersona, buildElksAudioMessage, buildElksByeMessage, buildElksHandshakeMessages, buildElksInterruptMessage, buildElksListeningMessage, buildElksSendingMessage, buildInboundSecurityAdvisory, buildOpenAIRealtimeUrl, buildPhoneTransportConfig, buildRealtimeInstructions, buildRealtimeSessionConfig, buildRealtimeToolGuidance, buildTwilioClearMessage, buildTwilioMarkMessage, buildTwilioMediaMessage, buildTwilioSayTwiML, buildTwilioSignature, buildTwilioStreamTwiML, callTelegramApi, classifyEmailRoute, classifyPhoneNumberRisk, classifyResumeError, clearMediaCapabilityCache, closeDatabase, composeBridgeWakePrompt, createRealtimeTransport, createTestDatabase, createToolExecutor, debug, debugWarn, deleteTelegramWebhook, detectBinary, ensureDataDir, escapeXml, extractEmailAddress, extractVerificationCode, flushTelemetry, forgetHostSession, formatOperatorQueryTelegramMessage, getDatabase, getDatetime, getMediaCapabilities, getOperatorEmail, getSmsProvider, getTelegramChat, getTelegramMe, getTelegramUpdates, getTelegramWebhookInfo, hostSessionStoragePath, inferPhoneRegion, invalidateSkillCache, isInternalEmail, isLoopbackMailHost, isOperatorReplySender, isPhoneRegionAllowed, isSessionFresh, isTelegramChatAllowed, isTelegramStopCommand, isValidPhoneNumber, listSkills, loadAgentPersona, loadHostSession, loadSkill, mapProviderSmsStatus, nextTelegramOffset, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, operatorQuerySubject, parseElksRealtimeMessage, parseEmail, parseGoogleVoiceSms, parseOperatorQueryReply, parseTelegramOperatorReply, parseTelegramUpdate, parseTwilioRealtimeMessage, personaPathFor, planBridgeWake, pollForOperatorAnswer, recallMemory, recordToolCall, redactBotToken, redactObject, redactPhoneTransportConfig, redactSecret, redactSmsConfig, redactTelegramConfig, renderSkillAsPrompt, requireBinary, requireWhisperModel, resolveCallbackPolicy, resolveConfig, resolveExtensionPolicy, resolveTlsRejectUnauthorized, safeJoin, sanitizeEmail, saveAgentPersona, saveConfig, saveHostSession, saveUserSkill, scanOutboundEmail, scoreEmail, searchSkills, sendTelegramMessage, setOperatorEmail, setTelegramWebhook, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, splitTelegramMessage, startRelayBridge, stem, stripTelegramMarkdown, threadIdFor, tokenize, tryJoin, userSkillsDir, validateApiUrl, validatePhoneMissionPolicy, validatePhoneMissionStart, validatePhoneTransportProfile, validateSkill, validateTwilioSignature, webSearch };
6493
+ export { AGENT_ROLES, AGENT_STATE_ROOT, ASK_OPERATOR_TOOL, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryEntry, type AgentMemoryFields, AgentMemoryManager, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentPersonaFrontmatter, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type AudioAction, type AudioEditOptions, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeMailContext, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type BridgeWakeRoute, type CachedMessage, CloudflareClient, type CreateAgentOptions, type CreateMemoryInput, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_CALLBACK_POLICY, DEFAULT_EXTENSION_POLICY, DEFAULT_REALTIME_AUDIO_FORMAT, DEFAULT_REALTIME_MODEL, DEFAULT_REALTIME_VOICE, DEFAULT_SESSION_MAX_AGE_MS, DEFAULT_WEB_SEARCH_ENDPOINT, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, ELKS_REALTIME_AUDIO_FORMATS, ELKS_REALTIME_WS_PATH, END_CALL_TOOL, EXTEND_CALL_TIME_TOOL, type ElksRealtimeAudioFormat, type ElksRealtimeAudioMessage, type ElksRealtimeByeMessage, type ElksRealtimeHelloMessage, type ElksRealtimeInboundMessage, type ElksRealtimeOutboundMessage, ElksRealtimeTransport, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, GET_CALL_STATUS_TOOL, GET_DATETIME_TOOL, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type GetDatetimeOptions, type GetUpdatesOptions, type HostName, type HostSession, type HostSessionResumeMode, type ImageAction, type ImageEditOptions, type InboundEmail, type InboundSmsEvent, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, LOAD_SKILL_TOOL, type LinkAdvisory, type LocalSmtpConfig, MEMORY_CATEGORIES, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type MediaBinary, type MediaCapability, type MediaCapabilityReport, type MediaFileResult, type MediaInfoResult, MediaManager, type MediaManagerOptions, type MediaStreamInfo, type MemoryCategory, type MemoryImportance, type MemoryQueryOptions, type MemoryRecaller, MemorySearchIndex, type MemorySource, type MemoryStats, OPENAI_REALTIME_URL, OPERATOR_QUERY_POLL_INTERVAL_MS, OPERATOR_QUERY_SUBJECT_TAG, OPERATOR_QUERY_TIMEOUT_MS, OPERATOR_QUERY_TIMEOUT_SENTINEL, type OpenClawPhoneMissionPolicy, type OperatorQueryNotificationInput, type OperatorQueryPollOptions, type OperatorQueryUrgency, type OperatorReplyKind, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, PERSONA_FILENAME, PHONE_CALLBACK_MAX_DELAY_SECONDS, PHONE_CALLBACK_MIN_DELAY_SECONDS, PHONE_CALL_CONTROL_PROVIDERS, PHONE_MAX_CONCURRENT_MISSIONS, PHONE_MIN_WEBHOOK_SECRET_LENGTH, PHONE_MISSION_STATES, PHONE_RATE_LIMIT_PER_HOUR, PHONE_RATE_LIMIT_PER_MINUTE, PHONE_REGION_SCOPES, PHONE_SERVER_MAX_ATTEMPTS, PHONE_SERVER_MAX_CALLBACK_CHAIN, PHONE_SERVER_MAX_CALL_DURATION_SECONDS, PHONE_SERVER_MAX_COST_PER_MISSION, PHONE_SERVER_MAX_EXTENSION_REQUESTS_PER_CALL, PHONE_SERVER_MAX_EXTENSION_SECONDS_PER_REQUEST, PHONE_SERVER_MAX_TOTAL_EXTENSION_SECONDS, PHONE_TASK_MAX_LENGTH, type ParsedAttachment, type ParsedEmail, type ParsedOperatorReply, type ParsedSms, type ParsedTelegramMessage, PathTraversalError, type PhoneAlternativePolicy, type PhoneCallMission, type PhoneCallbackPolicy, type PhoneConfirmPolicy, type PhoneExtensionPolicy, PhoneManager, type PhoneMissionStartValidationResult, type PhoneMissionState, type PhoneMissionTranscriptEntry, type PhoneMissionValidationIssue, type PhoneMissionValidationResult, type PhoneNumberRisk, type PhoneOperatorQuery, PhoneRateLimitError, type PhoneRegionScope, type PhoneScheduledCallback, type PhoneTransportConfig, type PhoneTransportProfile, type PhoneTransportProvider, type PhoneTransportValidationResult, PhoneWebhookAuthError, type PhoneWebhookResult, type PlanBridgeWakeArgs, type PurchasedDomain, REALTIME_AUDIO_SAMPLE_RATE, REALTIME_MAX_AUDIO_FRAME_BASE64, REALTIME_TOOL_CALL_TIMEOUT_MS, REALTIME_TOOL_DEFINITIONS, RECALL_MEMORY_TOOL, REDACTED, RELAY_PRESETS, type RealtimeBridgePort, type RealtimeBridgeTranscriptEntry, type RealtimeInboundEvent, type RealtimeInstructionOptions, type RealtimeSessionConfigOptions, type RealtimeToolCall, type RealtimeToolDefinition, type RealtimeToolHandler, type RealtimeToolResult, type RealtimeTransportAdapter, type RealtimeTransportProvider, RealtimeVoiceBridge, type RealtimeVoiceBridgeOptions, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SCHEDULE_CALLBACK_TOOL, SEARCH_EMAIL_TOOL, SEARCH_SKILLS_TOOL, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type ScheduledCallbackRequest, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, type SendTelegramMessageOptions, type SendTelegramMessageResult, ServiceManager, type ServiceStatus, type SetWebhookOptions, type SetupConfig, SetupManager, type SetupResult, type Severity, type Skill, type SkillCategory, type SkillContext, type SkillExitStrategy, type SkillSummary, type SkillTactic, type SkillValidationError, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type StartPhoneCallOptions, type StartPhoneCallResult, type StartPhoneMissionInput, TELEGRAM_API_BASE, TELEGRAM_CHUNK_SIZE, TELEGRAM_MESSAGE_LIMIT, TELEGRAM_MIN_WEBHOOK_SECRET_LENGTH, TELEGRAM_OPERATOR_QUERY_TAG, TELEGRAM_STOP_WORDS, TELEGRAM_WEBHOOK_SECRET_RE, TELEPHONY_TRANSPORT_CAPABILITIES, TWILIO_MEDIA_SAMPLE_RATE, TWILIO_REALTIME_WS_PATH, TelegramApiError, type TelegramApiOptions, type TelegramBotInfo, type TelegramChatType, type TelegramConfig, TelegramManager, type TelegramMessage, type TelegramMode, type TelephonyTransportCapability, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type ToolExecutor, type TtsGenerateOptions, type TunnelConfig, TunnelManager, type TwilioConnectedMessage, type TwilioMarkMessage, type TwilioMediaMessage, type TwilioRealtimeInboundMessage, type TwilioRealtimeOutboundMessage, TwilioRealtimeTransport, type TwilioStartMessage, type TwilioStopMessage, type TwilioStreamTwiMLOptions, UnsafeApiUrlError, type UpdateMemoryInput, type ValidatedPhoneMissionStart, type VideoAction, type VideoEditOptions, type VideoTimelineEntry, type VideoUnderstandOptions, type VideoUnderstandResult, type VoiceCloneOptions, type VoiceProvider, type VoiceRuntimeConnection, WARNING_THRESHOLD, WEB_SEARCH_TOOL, WEB_SEARCH_UNTRUSTED_PREFIX, type WatcherOptions, type WebSearchOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildDefaultPersona, buildElksAudioMessage, buildElksByeMessage, buildElksHandshakeMessages, buildElksInterruptMessage, buildElksListeningMessage, buildElksSendingMessage, buildInboundSecurityAdvisory, buildOpenAIRealtimeUrl, buildPhoneTransportConfig, buildRealtimeInstructions, buildRealtimeSessionConfig, buildRealtimeToolGuidance, buildTwilioClearMessage, buildTwilioMarkMessage, buildTwilioMediaMessage, buildTwilioSayTwiML, buildTwilioSignature, buildTwilioStreamTwiML, callTelegramApi, classifyEmailRoute, classifyPhoneNumberRisk, classifyResumeError, clearMediaCapabilityCache, closeDatabase, composeBridgeWakePrompt, createRealtimeTransport, createTestDatabase, createToolExecutor, debug, debugWarn, deleteTelegramWebhook, detectBinary, ensureDataDir, escapeXml, extractEmailAddress, extractVerificationCode, flushTelemetry, forgetHostSession, formatOperatorQueryTelegramMessage, getDatabase, getDatetime, getMediaCapabilities, getOperatorEmail, getSmsProvider, getTelegramChat, getTelegramMe, getTelegramUpdates, getTelegramWebhookInfo, getVoiceProvider, hostSessionStoragePath, inferPhoneRegion, invalidateSkillCache, isInternalEmail, isLoopbackMailHost, isOperatorReplySender, isPhoneRegionAllowed, isSessionFresh, isTelegramChatAllowed, isTelegramStopCommand, isValidPhoneNumber, listSkills, listVoiceProviders, loadAgentPersona, loadHostSession, loadSkill, mapProviderSmsStatus, nextTelegramOffset, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, operatorQuerySubject, parseElksRealtimeMessage, parseEmail, parseGoogleVoiceSms, parseOperatorQueryReply, parseTelegramOperatorReply, parseTelegramUpdate, parseTwilioRealtimeMessage, personaPathFor, planBridgeWake, pollForOperatorAnswer, readAgentPersonaFile, recallMemory, recordToolCall, redactBotToken, redactObject, redactPhoneTransportConfig, redactSecret, redactSmsConfig, redactTelegramConfig, registerVoiceProvider, renderSkillAsPrompt, requireBinary, requireWhisperModel, resolveCallbackPolicy, resolveConfig, resolveExtensionPolicy, resolveTlsRejectUnauthorized, resolveVoiceRuntime, safeJoin, sanitizeEmail, saveAgentPersona, saveConfig, saveHostSession, saveUserSkill, scanOutboundEmail, scoreEmail, searchSkills, sendTelegramMessage, setOperatorEmail, setTelegramWebhook, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, splitTelegramMessage, startRelayBridge, stem, stripTelegramMarkdown, threadIdFor, tokenize, tryJoin, updateAgentPersonaFrontmatter, userSkillsDir, validateApiUrl, validatePhoneMissionPolicy, validatePhoneMissionStart, validatePhoneTransportProfile, validateSkill, validateTwilioSignature, webSearch };
package/dist/index.js CHANGED
@@ -920,6 +920,13 @@ function resolveConfig(overrides) {
920
920
  dataDir: env.AGENTICMAIL_DATA_DIR?.replace(/^~(?=\/|$)/, homedir()) ?? DEFAULT_CONFIG.dataDir
921
921
  };
922
922
  if (env.OPENAI_API_KEY) config.openaiApiKey = env.OPENAI_API_KEY;
923
+ if (env.XAI_API_KEY) {
924
+ config.voiceProviderKeys = config.voiceProviderKeys ?? {};
925
+ config.voiceProviderKeys.grok = env.XAI_API_KEY;
926
+ }
927
+ if (env.AGENTICMAIL_VOICE_RUNTIME && env.AGENTICMAIL_VOICE_RUNTIME.trim()) {
928
+ config.voiceRuntime = env.AGENTICMAIL_VOICE_RUNTIME.trim();
929
+ }
923
930
  const configPath = join(config.dataDir, "config.json");
924
931
  if (existsSync(configPath)) {
925
932
  try {
@@ -6129,7 +6136,14 @@ function validatePhoneMissionPolicy(policy) {
6129
6136
  // manager) can read a concrete value without juggling undefined.
6130
6137
  // Caller-omitted → DEFAULT_*. Caller-set → clamped to server caps.
6131
6138
  extensionPolicy: resolveExtensionPolicy(policy.extensionPolicy),
6132
- callbackPolicy: resolveCallbackPolicy(policy.callbackPolicy)
6139
+ callbackPolicy: resolveCallbackPolicy(policy.callbackPolicy),
6140
+ // v0.9.95 — voice-runtime overrides. Pass-through; the registry
6141
+ // validates the provider id and voice-against-catalogue at
6142
+ // session-open time so we don't have to import the registry
6143
+ // here (would create a cycle with realtime-bridge.ts).
6144
+ voiceRuntime: typeof policy.voiceRuntime === "string" && policy.voiceRuntime.trim() ? policy.voiceRuntime.trim() : void 0,
6145
+ voiceModel: typeof policy.voiceModel === "string" && policy.voiceModel.trim() ? policy.voiceModel.trim() : void 0,
6146
+ voice: typeof policy.voice === "string" && policy.voice.trim() ? policy.voice.trim() : void 0
6133
6147
  },
6134
6148
  issues: []
6135
6149
  };
@@ -10530,6 +10544,140 @@ function withTimeout(promise, ms) {
10530
10544
  return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
10531
10545
  }
10532
10546
 
10547
+ // src/phone/voice-providers/registry.ts
10548
+ var PROVIDERS2 = /* @__PURE__ */ new Map();
10549
+ function registerVoiceProvider(provider) {
10550
+ if (PROVIDERS2.has(provider.id)) {
10551
+ throw new Error(`Voice provider "${provider.id}" registered twice \u2014 id collision.`);
10552
+ }
10553
+ PROVIDERS2.set(provider.id, provider);
10554
+ }
10555
+ function listVoiceProviders() {
10556
+ return Array.from(PROVIDERS2.values());
10557
+ }
10558
+ function getVoiceProvider(id) {
10559
+ return PROVIDERS2.get(id);
10560
+ }
10561
+ function resolveVoiceRuntime(providerId, config, options = {}) {
10562
+ const id = (providerId || "openai").trim() || "openai";
10563
+ const provider = PROVIDERS2.get(id);
10564
+ if (!provider) {
10565
+ const known = Array.from(PROVIDERS2.keys()).join(", ") || "(none registered)";
10566
+ throw new Error(
10567
+ `Unknown voice runtime "${id}". Known providers: ${known}. Add a new one by dropping a file into packages/core/src/phone/voice-providers/.`
10568
+ );
10569
+ }
10570
+ let apiKey = "";
10571
+ let apiKeySource = "";
10572
+ if (provider.apiKeyConfigField) {
10573
+ const legacy = config[provider.apiKeyConfigField];
10574
+ if (legacy && legacy.trim()) {
10575
+ apiKey = legacy.trim();
10576
+ apiKeySource = `config.${provider.apiKeyConfigField}`;
10577
+ }
10578
+ }
10579
+ if (!apiKey) {
10580
+ const fromMap = config.voiceProviderKeys?.[provider.id];
10581
+ if (fromMap && fromMap.trim()) {
10582
+ apiKey = fromMap.trim();
10583
+ apiKeySource = `config.voiceProviderKeys.${provider.id}`;
10584
+ }
10585
+ }
10586
+ if (!apiKey) {
10587
+ const fromEnv = process.env[provider.apiKeyEnvVar];
10588
+ if (fromEnv && fromEnv.trim()) {
10589
+ apiKey = fromEnv.trim();
10590
+ apiKeySource = `env ${provider.apiKeyEnvVar}`;
10591
+ }
10592
+ }
10593
+ if (!apiKey) {
10594
+ throw new Error(
10595
+ `Voice provider "${provider.id}" (${provider.displayName}) selected, but no API key is configured. Set ${provider.apiKeyEnvVar} in your environment or save it to ~/.agenticmail/config.json under voiceProviderKeys.${provider.id}.`
10596
+ );
10597
+ }
10598
+ const model = options.model && options.model.trim() || provider.defaultModel;
10599
+ const url = `${provider.websocketBaseUrl}?model=${encodeURIComponent(model)}`;
10600
+ let voice = "";
10601
+ let voiceSource = "";
10602
+ const requested = (options.voice || "").trim();
10603
+ if (requested) {
10604
+ if (provider.voices.includes(requested) || provider.customVoicesSupported) {
10605
+ voice = requested;
10606
+ voiceSource = "mission policy";
10607
+ } else {
10608
+ console.warn(
10609
+ `[voice-providers] Voice "${requested}" is not in ${provider.id}'s catalogue (${provider.voices.join(", ")}). Falling through to ${provider.defaultVoice}.`
10610
+ );
10611
+ }
10612
+ }
10613
+ if (!voice) {
10614
+ const installDefault = config.voiceProviderVoices?.[provider.id];
10615
+ if (installDefault && installDefault.trim()) {
10616
+ const v = installDefault.trim();
10617
+ if (provider.voices.includes(v) || provider.customVoicesSupported) {
10618
+ voice = v;
10619
+ voiceSource = `config.voiceProviderVoices.${provider.id}`;
10620
+ }
10621
+ }
10622
+ }
10623
+ if (!voice) {
10624
+ voice = provider.defaultVoice;
10625
+ voiceSource = `${provider.id} default`;
10626
+ }
10627
+ return {
10628
+ providerId: provider.id,
10629
+ providerDisplayName: provider.displayName,
10630
+ url,
10631
+ model,
10632
+ apiKey,
10633
+ apiKeySource,
10634
+ voice,
10635
+ voiceSource
10636
+ };
10637
+ }
10638
+
10639
+ // src/phone/voice-providers/openai.ts
10640
+ registerVoiceProvider({
10641
+ id: "openai",
10642
+ displayName: "OpenAI Realtime (gpt-realtime)",
10643
+ websocketBaseUrl: "wss://api.openai.com/v1/realtime",
10644
+ defaultModel: "gpt-realtime",
10645
+ apiKeyEnvVar: "OPENAI_API_KEY",
10646
+ // Legacy: the original config.json schema used a dedicated
10647
+ // `openaiApiKey` field for this key. The resolver checks that field
10648
+ // before the generic voiceProviderKeys map so existing installs
10649
+ // continue to work without migration.
10650
+ apiKeyConfigField: "openaiApiKey",
10651
+ description: "OpenAI Realtime (gpt-realtime). Default voice runtime; supports linear PCM @ 24 kHz (46elks) and G.711 \xB5-law @ 8 kHz (Twilio) without transcoding.",
10652
+ // v0.9.95 — voice catalogue. Names match what the Realtime session
10653
+ // accepts under `audio.output.voice`. `marin` and `cedar` are the
10654
+ // GA gpt-realtime additions; the rest are the legacy roster carried
10655
+ // forward from gpt-4o-realtime-preview.
10656
+ voices: ["alloy", "ash", "ballad", "cedar", "coral", "echo", "marin", "sage", "shimmer", "verse"],
10657
+ defaultVoice: "marin"
10658
+ });
10659
+
10660
+ // src/phone/voice-providers/grok.ts
10661
+ registerVoiceProvider({
10662
+ id: "grok",
10663
+ displayName: "xAI Grok Voice Agent",
10664
+ websocketBaseUrl: "wss://api.x.ai/v1/realtime",
10665
+ defaultModel: "grok-voice-latest",
10666
+ apiKeyEnvVar: "XAI_API_KEY",
10667
+ description: 'xAI Grok Voice Agent \u2014 OpenAI-Realtime-compatible WebSocket protocol; select via mission policy.voiceRuntime="grok" or env AGENTICMAIL_VOICE_RUNTIME=grok.',
10668
+ // v0.9.95 — voice catalogue. xAI ships 5 expressive built-in
10669
+ // voices for the realtime Voice Agent. Three are explicitly named
10670
+ // in the announcement (Ara, Eve, Leo); the other two are
10671
+ // browsable in console.x.ai's Voice Library along with the
10672
+ // broader 80+ voice catalogue. The full live list is at
10673
+ // GET /v1/tts/voices. We don't try to enumerate all 80+ here —
10674
+ // customVoicesSupported lets operators paste any voice_id from
10675
+ // their console.
10676
+ voices: ["ara", "eve", "leo"],
10677
+ defaultVoice: "ara",
10678
+ customVoicesSupported: true
10679
+ });
10680
+
10533
10681
  // src/telemetry.ts
10534
10682
  import { randomUUID as randomUUID2 } from "crypto";
10535
10683
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
@@ -14679,8 +14827,19 @@ function loadAgentPersona(agentName) {
14679
14827
  const path2 = personaPathFor(agentName);
14680
14828
  try {
14681
14829
  if (existsSync14(path2)) {
14682
- const content = readFileSync10(path2, "utf-8").trim();
14683
- if (content) return content;
14830
+ const raw = readFileSync10(path2, "utf-8");
14831
+ if (raw.trim()) {
14832
+ const text = raw.replace(/\r\n/g, "\n");
14833
+ if (text.startsWith("---\n")) {
14834
+ const close = text.indexOf("\n---", 4);
14835
+ if (close > 0) {
14836
+ let cursor = close + 4;
14837
+ while (cursor < text.length && (text[cursor] === "\n" || text[cursor] === "\r")) cursor++;
14838
+ return text.slice(cursor).trim();
14839
+ }
14840
+ }
14841
+ return text.trim();
14842
+ }
14684
14843
  }
14685
14844
  } catch {
14686
14845
  }
@@ -14700,6 +14859,61 @@ function saveAgentPersona(agentName, content) {
14700
14859
  writeFileSync11(path2, content.trim() + "\n", { mode: 420 });
14701
14860
  return path2;
14702
14861
  }
14862
+ function readAgentPersonaFile(agentName) {
14863
+ const path2 = personaPathFor(agentName);
14864
+ let raw = "";
14865
+ try {
14866
+ if (existsSync14(path2)) raw = readFileSync10(path2, "utf-8");
14867
+ } catch {
14868
+ }
14869
+ if (!raw.trim()) {
14870
+ return { frontmatter: {}, body: loadAgentPersona(agentName) };
14871
+ }
14872
+ const text = raw.replace(/\r\n/g, "\n");
14873
+ if (!text.startsWith("---\n")) {
14874
+ return { frontmatter: {}, body: text.trim() };
14875
+ }
14876
+ const close = text.indexOf("\n---", 4);
14877
+ if (close < 0) {
14878
+ return { frontmatter: {}, body: text.trim() };
14879
+ }
14880
+ const yamlBlock = text.slice(4, close);
14881
+ const bodyStart = close + 4;
14882
+ let cursor = bodyStart;
14883
+ while (cursor < text.length && (text[cursor] === "\n" || text[cursor] === "\r")) cursor++;
14884
+ const body = text.slice(cursor).trim();
14885
+ const frontmatter = {};
14886
+ for (const line of yamlBlock.split("\n")) {
14887
+ const m = /^\s*([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*?)\s*$/.exec(line);
14888
+ if (!m) continue;
14889
+ const key = m[1];
14890
+ const value = m[2].replace(/^["']|["']$/g, "");
14891
+ if (key === "voice") frontmatter.voice = value;
14892
+ else if (key === "voiceRuntime") frontmatter.voiceRuntime = value;
14893
+ }
14894
+ return { frontmatter, body };
14895
+ }
14896
+ function updateAgentPersonaFrontmatter(agentName, patch) {
14897
+ const { frontmatter, body } = readAgentPersonaFile(agentName);
14898
+ const merged = { ...frontmatter };
14899
+ if (patch.voice !== void 0) merged.voice = patch.voice;
14900
+ if (patch.voiceRuntime !== void 0) merged.voiceRuntime = patch.voiceRuntime;
14901
+ const lines = [];
14902
+ if (merged.voice && merged.voice.trim()) lines.push(`voice: ${merged.voice.trim()}`);
14903
+ if (merged.voiceRuntime && merged.voiceRuntime.trim()) lines.push(`voiceRuntime: ${merged.voiceRuntime.trim()}`);
14904
+ const path2 = personaPathFor(agentName);
14905
+ const dir2 = path2.substring(0, path2.lastIndexOf("/"));
14906
+ if (!existsSync14(dir2)) mkdirSync12(dir2, { recursive: true });
14907
+ const content = lines.length > 0 ? `---
14908
+ ${lines.join("\n")}
14909
+ ---
14910
+
14911
+ ${body}
14912
+ ` : `${body}
14913
+ `;
14914
+ writeFileSync11(path2, content, { mode: 420 });
14915
+ return path2;
14916
+ }
14703
14917
  export {
14704
14918
  AGENT_ROLES,
14705
14919
  AGENT_STATE_ROOT,
@@ -14859,6 +15073,7 @@ export {
14859
15073
  getTelegramMe,
14860
15074
  getTelegramUpdates,
14861
15075
  getTelegramWebhookInfo,
15076
+ getVoiceProvider,
14862
15077
  hostSessionStoragePath,
14863
15078
  inferPhoneRegion,
14864
15079
  invalidateSkillCache,
@@ -14871,6 +15086,7 @@ export {
14871
15086
  isTelegramStopCommand,
14872
15087
  isValidPhoneNumber,
14873
15088
  listSkills,
15089
+ listVoiceProviders,
14874
15090
  loadAgentPersona,
14875
15091
  loadHostSession,
14876
15092
  loadSkill,
@@ -14891,6 +15107,7 @@ export {
14891
15107
  personaPathFor,
14892
15108
  planBridgeWake,
14893
15109
  pollForOperatorAnswer,
15110
+ readAgentPersonaFile,
14894
15111
  recallMemory,
14895
15112
  recordToolCall,
14896
15113
  redactBotToken,
@@ -14899,6 +15116,7 @@ export {
14899
15116
  redactSecret,
14900
15117
  redactSmsConfig,
14901
15118
  redactTelegramConfig,
15119
+ registerVoiceProvider,
14902
15120
  renderSkillAsPrompt,
14903
15121
  requireBinary,
14904
15122
  requireWhisperModel,
@@ -14906,6 +15124,7 @@ export {
14906
15124
  resolveConfig,
14907
15125
  resolveExtensionPolicy,
14908
15126
  resolveTlsRejectUnauthorized,
15127
+ resolveVoiceRuntime,
14909
15128
  safeJoin,
14910
15129
  sanitizeEmail,
14911
15130
  saveAgentPersona,
@@ -14927,6 +15146,7 @@ export {
14927
15146
  threadIdFor,
14928
15147
  tokenize,
14929
15148
  tryJoin,
15149
+ updateAgentPersonaFrontmatter,
14930
15150
  userSkillsDir,
14931
15151
  validateApiUrl,
14932
15152
  validatePhoneMissionPolicy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/core",
3
- "version": "0.9.38",
3
+ "version": "0.9.40",
4
4
  "description": "Core SDK for AgenticMail — email, SMS, and phone call-control for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",