@rymi/mcp 2.8.1 → 2.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -1
  2. package/dist/index.js +228 -75
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -104,6 +104,10 @@ Run the server on your own machine — it speaks **stdio** by default and talks
104
104
  <td><code>create_call</code> · <code>batch_call</code> · <code>end_call</code> · <code>add_call_participant</code></td>
105
105
  </tr>
106
106
  <tr>
107
+ <td><b>Task calls</b></td>
108
+ <td><code>place_task_call</code> · <code>wait_for_call</code></td>
109
+ </tr>
110
+ <tr>
107
111
  <td><b>Numbers</b></td>
108
112
  <td><code>list_numbers</code> · <code>register_number</code> · <code>attach_number</code> · <code>remove_number</code></td>
109
113
  </tr>
@@ -151,7 +155,7 @@ Run the server on your own machine — it speaks **stdio** by default and talks
151
155
 
152
156
  ### ⚠️ Gated tools
153
157
 
154
- `create_call` and `batch_call` place **real, billable** outbound calls; `publish_agent` flips an agent **live** to end users.
158
+ `place_task_call`, `create_call` and `batch_call` place **real, billable** outbound calls; `publish_agent` flips an agent **live** to end users.
155
159
 
156
160
  What restrains them depends on which server you're talking to.
157
161
 
package/dist/index.js CHANGED
@@ -153,6 +153,7 @@ var init_studioCatalog = __esm({
153
153
  "gpt-realtime-2.1-mini",
154
154
  "gpt-live-1",
155
155
  "gemini-3.8-live",
156
+ "gemini-3.8-live-extended-thinking",
156
157
  "grok-voice-3.0",
157
158
  "grok-voice-think-fast-2.0",
158
159
  "ultravox-v0.7",
@@ -176,10 +177,6 @@ var init_studioCatalog = __esm({
176
177
  "gemini-2.5-flash-native-audio-preview-12-2025",
177
178
  "nova-sonic-1",
178
179
  "nova-sonic-2",
179
- "llama-3.1-8b-instant",
180
- "llama-3.3-70b-versatile",
181
- "meta-llama/llama-4-scout-17b-16e-instruct",
182
- "qwen/qwen3-32b",
183
180
  "openai/gpt-oss-20b",
184
181
  "openai/gpt-oss-120b",
185
182
  "openai/gpt-oss-safeguard-20b",
@@ -263,6 +260,7 @@ var init_studioCatalog = __esm({
263
260
  "gemini-3.1-flash-tts-preview",
264
261
  "tts-1",
265
262
  "bulbul:v3",
263
+ "bulbul:v4-flash",
266
264
  "gpt-4o-mini-tts",
267
265
  "eleven_multilingual_v2",
268
266
  // Additional TTS
@@ -1218,6 +1216,80 @@ var init_variables = __esm({
1218
1216
  }
1219
1217
  });
1220
1218
 
1219
+ // ../shared-types/dist/safeUrl.js
1220
+ var safeUrl_exports = {};
1221
+ __export(safeUrl_exports, {
1222
+ isSafeAudioUrl: () => isSafeAudioUrl,
1223
+ isSafeUrl: () => isSafeUrl
1224
+ });
1225
+ function unmapIpv4(v6) {
1226
+ const dotted = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(v6);
1227
+ if (dotted)
1228
+ return dotted[1];
1229
+ const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(v6);
1230
+ if (!hex)
1231
+ return null;
1232
+ const high = parseInt(hex[1], 16);
1233
+ const low = parseInt(hex[2], 16);
1234
+ return [high >> 8, high & 255, low >> 8, low & 255].join(".");
1235
+ }
1236
+ function isBlockedHost(host) {
1237
+ const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
1238
+ if (bare === "localhost" || bare === "0.0.0.0" || bare === "::")
1239
+ return true;
1240
+ if (bare.endsWith(".local") || bare.endsWith(".internal") || bare.endsWith(".localhost"))
1241
+ return true;
1242
+ if (/^127\./.test(bare))
1243
+ return true;
1244
+ if (/^10\./.test(bare))
1245
+ return true;
1246
+ if (/^192\.168\./.test(bare))
1247
+ return true;
1248
+ if (/^172\.(1[6-9]|2\d|3[01])\./.test(bare))
1249
+ return true;
1250
+ if (/^169\.254\./.test(bare))
1251
+ return true;
1252
+ const v6 = bare.toLowerCase();
1253
+ if (v6 === "::1")
1254
+ return true;
1255
+ if (/^f[cd][0-9a-f]{2}:/.test(v6))
1256
+ return true;
1257
+ if (/^fe[89ab][0-9a-f]:/.test(v6))
1258
+ return true;
1259
+ const mapped = unmapIpv4(v6);
1260
+ if (mapped)
1261
+ return isBlockedHost(mapped);
1262
+ return false;
1263
+ }
1264
+ function isSafeUrl(raw, options = {}) {
1265
+ const protocols = options.protocols ?? ["http:", "https:"];
1266
+ let url;
1267
+ try {
1268
+ url = new URL(raw);
1269
+ } catch {
1270
+ return false;
1271
+ }
1272
+ if (!protocols.includes(url.protocol))
1273
+ return false;
1274
+ return !isBlockedHost(url.hostname.toLowerCase());
1275
+ }
1276
+ function isSafeAudioUrl(raw) {
1277
+ if (!isSafeUrl(raw, { protocols: ["https:"] }))
1278
+ return false;
1279
+ try {
1280
+ const parsed = new URL(raw.trim());
1281
+ const path = parsed.pathname.toLowerCase();
1282
+ return path.endsWith(".wav") || path.endsWith(".mp3");
1283
+ } catch {
1284
+ return false;
1285
+ }
1286
+ }
1287
+ var init_safeUrl = __esm({
1288
+ "../shared-types/dist/safeUrl.js"() {
1289
+ "use strict";
1290
+ }
1291
+ });
1292
+
1221
1293
  // ../../node_modules/.pnpm/dlv@1.1.3/node_modules/dlv/dist/dlv.es.js
1222
1294
  function dlv_es_default(t, e, l, n, r) {
1223
1295
  for (e = e.split ? e.split(".") : e, n = 0; n < e.length; n++) t = t ? t[e[n]] : r;
@@ -1407,9 +1479,10 @@ var AGENT_CONFIG_SCHEMA_VERSION, CHANGE_SET_VALIDATORS, DERIVED_SERIALIZE_RULES,
1407
1479
  var init_agentConfigSchema = __esm({
1408
1480
  "../shared-types/dist/agentConfigSchema.js"() {
1409
1481
  "use strict";
1482
+ init_safeUrl();
1410
1483
  init_dlv_es();
1411
1484
  init_dist();
1412
- AGENT_CONFIG_SCHEMA_VERSION = "2026-05-20";
1485
+ AGENT_CONFIG_SCHEMA_VERSION = "2026-10-23";
1413
1486
  CHANGE_SET_VALIDATORS = [
1414
1487
  // Catalog validation for enum-catalog model + language fields.
1415
1488
  // Skips silently when no catalogs are wired (legacy callers / tests).
@@ -1453,6 +1526,21 @@ var init_agentConfigSchema = __esm({
1453
1526
  }
1454
1527
  }
1455
1528
  return errors.length === 0 ? { valid: true } : { valid: false, errors };
1529
+ },
1530
+ (cfg, changes) => {
1531
+ if (!changes.some((c) => c.key === "background_sound" || c.key === "background_sound_url"))
1532
+ return { valid: true };
1533
+ const errors = [];
1534
+ const url = cfg.background_sound_url;
1535
+ const mode = cfg.background_sound;
1536
+ const hasUrl = typeof url === "string" && url.trim() !== "";
1537
+ if (hasUrl && (url.length > 512 || !isSafeAudioUrl(url))) {
1538
+ errors.push({ field: "background_sound_url", error: "Use a public https:// link to a .wav or .mp3 file (up to 512 characters)." });
1539
+ }
1540
+ if (mode === "custom" && !hasUrl) {
1541
+ errors.push({ field: "background_sound_url", error: "Custom background sound needs a link to a .wav or .mp3 file." });
1542
+ }
1543
+ return errors.length === 0 ? { valid: true } : { valid: false, errors };
1456
1544
  }
1457
1545
  ];
1458
1546
  DERIVED_SERIALIZE_RULES = [
@@ -1718,6 +1806,7 @@ var init_agentConfigSchema = __esm({
1718
1806
  // ── Features ────────────────────────────────────────────────────────────
1719
1807
  { key: "transcriptionEnabled", label: "Transcription", description: "Generate text transcripts of calls.", default: true, type: { kind: "boolean" }, apiPath: "features.transcription_enabled", section: "features", editableIn: "both" },
1720
1808
  { key: "requireConsent", label: "Require Consent", description: "Ask caller for recording consent before proceeding.", default: true, type: { kind: "boolean" }, apiPath: "features.require_consent", section: "features", editableIn: "both" },
1809
+ { key: "taskCallsEnabled", label: "Allow Task Calls", description: "Outside AI agents (MCP/API) can place one-off calls through this agent.", default: false, type: { kind: "boolean" }, apiPath: "features.task_calls_enabled", section: "features", editableIn: "both" },
1721
1810
  { key: "postCallSummaryEnabled", label: "Post-Call Summary", description: "Generate an AI summary after each call.", default: true, type: { kind: "boolean" }, apiPath: "post_call.summary.enabled", section: "features", editableIn: "both" },
1722
1811
  { key: "postCallSummaryPrompt", label: "Summary Prompt", description: "Custom prompt for post-call summary generation.", default: "Summarize the call in a few sentences: the caller's main reason for calling, what was discussed or resolved, and any agreed next steps.", type: { kind: "string", maxLength: 2e3 }, apiPath: "post_call.summary.prompt", section: "features", editableIn: "both" },
1723
1812
  { key: "postCallExtractionEnabled", label: "Post-Call Extraction", description: "Extract structured data from call transcripts.", default: false, type: { kind: "boolean" }, apiPath: "post_call.structured_extraction.prompt", section: "features", editableIn: "both" },
@@ -1773,7 +1862,10 @@ var init_agentConfigSchema = __esm({
1773
1862
  { key: "stt_keywords", label: "STT Keywords", description: "Comma-separated keywords to boost STT accuracy.", default: "", type: { kind: "string", maxLength: 1e3 }, apiPath: "advanced.stt_keywords", section: "advanced", editableIn: "both" },
1774
1863
  { key: "pronunciations", label: "Pronunciations", aliases: ["pronunciation", "pronounce", "say as", "how to say"], description: "Words the agent should say a specific way: each word with how to say it, plus optional IPA where the voice engine reads it.", default: [], type: { kind: "list-object", itemShape: [{ key: "word", type: { kind: "string", maxLength: 64 }, required: true }, { key: "say_as", type: { kind: "string", maxLength: 128 }, required: true }, { key: "ipa", type: { kind: "string", maxLength: 128 }, required: false }] }, apiPath: "advanced.pronunciations", section: "advanced", editableIn: "both" },
1775
1864
  { key: "stt_background_denoising", label: "Background Denoising", aliases: ["denoise", "noise suppression", "background noise"], description: "Server-side noise suppression on the inbound audio (Deepgram smart-format).", default: true, type: { kind: "boolean" }, apiPath: "advanced.stt_background_denoising", section: "advanced", editableIn: "both" },
1776
- { key: "noise_cancellation_krisp", label: "Krisp Noise Cancellation", aliases: ["krisp", "noise cancellation", "inbound denoising", "background voice"], description: "Transport-level inbound noise + background-voice cancellation (LiveKit Krisp). On by default for cloud calls; turn off to feed the raw caller audio to the recognizer.", default: true, type: { kind: "boolean" }, apiPath: "advanced.noise_cancellation_krisp", section: "advanced", editableIn: "both" },
1865
+ { key: "voice_focus", label: "Voice Focus", aliases: ["krisp", "noise cancellation", "inbound denoising", "background voice", "voice focus", "noise_cancellation_krisp"], description: "Transport-level inbound speech isolation (RNNoise). Suppresses steady room noise before VAD and speech recognition; off by default.", default: "off", type: { kind: "enum", options: [{ value: "off", label: "Off" }, { value: "standard", label: "Standard" }] }, apiPath: "advanced.voice_focus", section: "advanced", editableIn: "both" },
1866
+ { key: "background_sound", label: "Background Sound", aliases: ["ambience", "background audio", "office sound", "call ambience"], description: "Continuous low-volume ambient audio mixed under the agent voice on the outbound call track (`off`, built-in `office`, or `custom` URL).", default: "off", type: { kind: "enum", options: [{ value: "off", label: "Off" }, { value: "office", label: "Office" }, { value: "custom", label: "Custom URL" }] }, apiPath: "advanced.background_sound", section: "advanced", editableIn: "both" },
1867
+ { key: "background_sound_volume", label: "Background Sound Volume", aliases: ["ambience volume", "office sound volume"], description: "Base linear gain for outbound background sound (0.00 to 0.40, default 0.15). Ducks automatically by 8 dB while the agent speaks.", default: 0.15, type: { kind: "number", min: 0, max: 0.4 }, apiPath: "advanced.background_sound_volume", section: "advanced", editableIn: "both" },
1868
+ { key: "background_sound_url", label: "Background Sound URL", aliases: ["ambience url", "custom background sound url"], description: "Public HTTPS URL to a mono/stereo .wav or .mp3 file (3\u2013120 s, max 15 MB) used when background_sound is set to custom.", default: "", type: { kind: "string", maxLength: 512 }, apiPath: "advanced.background_sound_url", section: "advanced", editableIn: "both" },
1777
1869
  { key: "stt_eot_confidence", label: "End-of-Turn Confidence", description: "Deepgram Flux: how sure the model must be before deciding you finished speaking.", default: 0.7, type: { kind: "number", min: 0.5, max: 0.9 }, apiPath: "advanced.stt_eot_confidence", section: "advanced", editableIn: "both" },
1778
1870
  { key: "stt_eot_timeout_ms", label: "End-of-Turn Timeout", description: "Deepgram Flux: max silence before end-of-turn is forced.", default: 5e3, type: { kind: "number", min: 500, max: 1e4, unit: "ms" }, apiPath: "advanced.stt_eot_timeout_ms", section: "advanced", editableIn: "both" },
1779
1871
  { key: "stt_min_turn_silence_ms", label: "Min Turn Silence", description: "AssemblyAI: minimum silence before detecting end-of-turn when confident.", default: 160, type: { kind: "number", min: 0, max: 3e3, unit: "ms" }, apiPath: "advanced.stt_min_turn_silence_ms", section: "advanced", editableIn: "both" },
@@ -2151,6 +2243,8 @@ var init_flatDiff = __esm({
2151
2243
  "features.transcription_enabled": "transcriptionEnabled",
2152
2244
  "features.requireConsent": "requireConsent",
2153
2245
  "features.require_consent": "requireConsent",
2246
+ "features.taskCallsEnabled": "taskCallsEnabled",
2247
+ "features.task_calls_enabled": "taskCallsEnabled",
2154
2248
  "features.postCallSummaryEnabled": "postCallSummaryEnabled",
2155
2249
  "features.post_call_summary_enabled": "postCallSummaryEnabled",
2156
2250
  "features.postCallExtractionEnabled": "postCallExtractionEnabled",
@@ -2174,6 +2268,12 @@ var init_flatDiff = __esm({
2174
2268
  "stt.stt_keywords": "stt_keywords",
2175
2269
  "stt.background_denoising": "stt_background_denoising",
2176
2270
  "stt.stt_background_denoising": "stt_background_denoising",
2271
+ "noise_cancellation_krisp": "voice_focus",
2272
+ "advanced.noise_cancellation_krisp": "voice_focus",
2273
+ "stt.voice_focus": "voice_focus",
2274
+ "tts.background_sound": "background_sound",
2275
+ "tts.background_sound_volume": "background_sound_volume",
2276
+ "tts.background_sound_url": "background_sound_url",
2177
2277
  "stt.eot_confidence": "stt_eot_confidence",
2178
2278
  "stt.stt_eot_confidence": "stt_eot_confidence",
2179
2279
  "stt.eot_timeout_ms": "stt_eot_timeout_ms",
@@ -2310,6 +2410,8 @@ var init_pricing = __esm({
2310
2410
  "gpt-realtime-2.1": 0.096,
2311
2411
  "gpt-realtime-2.1-mini": 0.03,
2312
2412
  "gemini-3.8-live": 0.035,
2413
+ "gemini-3.8-live-extended-thinking": 0.035,
2414
+ // provisional: same list rates, thinking tokens extra
2313
2415
  "gpt-live-1": 0.06,
2314
2416
  // 0.05 voice layer + ~0.01 gpt-5.4-mini delegation backend
2315
2417
  "grok-voice-think-fast-2.0": 0.08,
@@ -2446,6 +2548,7 @@ var init_pricing = __esm({
2446
2548
  "inworld-tts-1": 5e-3,
2447
2549
  "speech-1.6": 0.015,
2448
2550
  "s2.1-pro": 0.020833,
2551
+ "drama-3": 0.069444,
2449
2552
  "polly-neural": 0.012,
2450
2553
  "sonic-3.5": 0.037594,
2451
2554
  "sonic-3.6": 0.037594,
@@ -2466,7 +2569,9 @@ var init_pricing = __esm({
2466
2569
  "PlayDialogTurbo": 0.06,
2467
2570
  "speech-02-turbo": 0.024,
2468
2571
  "speech-02-hd": 0.04,
2469
- "bulbul:v3": 0.02842105
2572
+ "bulbul:v3": 0.02842105,
2573
+ // ₹2.00/1K chars PAYG, WebSocket (sarvam.ai/api-pricing, 2026-10-17) at ₹95/$, 900 chars/min.
2574
+ "bulbul:v4-flash": 0.01894737
2470
2575
  };
2471
2576
  }
2472
2577
  });
@@ -2488,68 +2593,6 @@ var init_knowledge = __esm({
2488
2593
  }
2489
2594
  });
2490
2595
 
2491
- // ../shared-types/dist/safeUrl.js
2492
- var safeUrl_exports = {};
2493
- __export(safeUrl_exports, {
2494
- isSafeUrl: () => isSafeUrl
2495
- });
2496
- function unmapIpv4(v6) {
2497
- const dotted = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(v6);
2498
- if (dotted)
2499
- return dotted[1];
2500
- const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(v6);
2501
- if (!hex)
2502
- return null;
2503
- const high = parseInt(hex[1], 16);
2504
- const low = parseInt(hex[2], 16);
2505
- return [high >> 8, high & 255, low >> 8, low & 255].join(".");
2506
- }
2507
- function isBlockedHost(host) {
2508
- const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
2509
- if (bare === "localhost" || bare === "0.0.0.0" || bare === "::")
2510
- return true;
2511
- if (bare.endsWith(".local") || bare.endsWith(".internal") || bare.endsWith(".localhost"))
2512
- return true;
2513
- if (/^127\./.test(bare))
2514
- return true;
2515
- if (/^10\./.test(bare))
2516
- return true;
2517
- if (/^192\.168\./.test(bare))
2518
- return true;
2519
- if (/^172\.(1[6-9]|2\d|3[01])\./.test(bare))
2520
- return true;
2521
- if (/^169\.254\./.test(bare))
2522
- return true;
2523
- const v6 = bare.toLowerCase();
2524
- if (v6 === "::1")
2525
- return true;
2526
- if (/^f[cd][0-9a-f]{2}:/.test(v6))
2527
- return true;
2528
- if (/^fe[89ab][0-9a-f]:/.test(v6))
2529
- return true;
2530
- const mapped = unmapIpv4(v6);
2531
- if (mapped)
2532
- return isBlockedHost(mapped);
2533
- return false;
2534
- }
2535
- function isSafeUrl(raw, options = {}) {
2536
- const protocols = options.protocols ?? ["http:", "https:"];
2537
- let url;
2538
- try {
2539
- url = new URL(raw);
2540
- } catch {
2541
- return false;
2542
- }
2543
- if (!protocols.includes(url.protocol))
2544
- return false;
2545
- return !isBlockedHost(url.hostname.toLowerCase());
2546
- }
2547
- var init_safeUrl = __esm({
2548
- "../shared-types/dist/safeUrl.js"() {
2549
- "use strict";
2550
- }
2551
- });
2552
-
2553
2596
  // ../shared-types/dist/agentTools.js
2554
2597
  var agentTools_exports = {};
2555
2598
  __export(agentTools_exports, {
@@ -3736,6 +3779,20 @@ var init_sttCapabilities = __esm({
3736
3779
  function resolveManagedPricePerMin(sku) {
3737
3780
  return sku.pricePerMin ?? 0;
3738
3781
  }
3782
+ function skuAllowsCountry(sku, country, strict) {
3783
+ const allowed = sku.allowedCountries;
3784
+ if (!allowed || allowed.length === 0)
3785
+ return true;
3786
+ if (!country)
3787
+ return !strict;
3788
+ return allowed.includes(country.toUpperCase());
3789
+ }
3790
+ function skuFeaturedInCountry(sku, country) {
3791
+ const featured = sku.featuredCountries;
3792
+ if (!featured || featured.length === 0)
3793
+ return true;
3794
+ return !!country && featured.includes(country.toUpperCase());
3795
+ }
3739
3796
  var init_managedAgentSkus = __esm({
3740
3797
  "../shared-types/dist/managedAgentSkus.js"() {
3741
3798
  "use strict";
@@ -7011,6 +7068,7 @@ __export(dist_exports, {
7011
7068
  isE164: () => isE164,
7012
7069
  isKnownSessionEndReason: () => isKnownSessionEndReason,
7013
7070
  isRealtimeModel: () => isRealtimeModel,
7071
+ isSafeAudioUrl: () => isSafeAudioUrl,
7014
7072
  isSafeUrl: () => isSafeUrl,
7015
7073
  isSupportedCountry: () => isSupportedCountry2,
7016
7074
  isSupportedPhoneNumber: () => isSupportedPhoneNumber,
@@ -7047,6 +7105,8 @@ __export(dist_exports, {
7047
7105
  sanitizeTranscriptText: () => sanitizeTranscriptText,
7048
7106
  scalarContextKeys: () => scalarContextKeys,
7049
7107
  serializeSection: () => serializeSection,
7108
+ skuAllowsCountry: () => skuAllowsCountry,
7109
+ skuFeaturedInCountry: () => skuFeaturedInCountry,
7050
7110
  stackCostPerMin: () => stackCostPerMin,
7051
7111
  supportedCountry: () => supportedCountry,
7052
7112
  toSupportedE164: () => toSupportedE164,
@@ -8255,7 +8315,7 @@ var require_dist = __commonJS({
8255
8315
  ];
8256
8316
  var import_zod11 = require("zod");
8257
8317
  var import_agentTools = (init_agentTools(), __toCommonJS(agentTools_exports));
8258
- var import_safeUrl2 = (init_safeUrl(), __toCommonJS(safeUrl_exports));
8318
+ var import_safeUrl3 = (init_safeUrl(), __toCommonJS(safeUrl_exports));
8259
8319
  var DEFAULT_NAME = "call_webhook";
8260
8320
  var METHODS = ["GET", "POST", "PUT", "PATCH"];
8261
8321
  var FIELD_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
@@ -8293,7 +8353,7 @@ var require_dist = __commonJS({
8293
8353
  const url = typeof s.url === "string" ? s.url.trim() : "";
8294
8354
  if (!url) errors.push("url is required.");
8295
8355
  else if (!/^https:\/\//i.test(url)) errors.push("url must be https \u2014 the request carries caller details.");
8296
- else if (!(0, import_safeUrl2.isSafeUrl)(url, { protocols: ["https:"] })) errors.push("url must be a valid public host; private and loopback addresses are refused at call time.");
8356
+ else if (!(0, import_safeUrl3.isSafeUrl)(url, { protocols: ["https:"] })) errors.push("url must be a valid public host; private and loopback addresses are refused at call time.");
8297
8357
  if (!METHODS.includes(s.method)) errors.push(`method must be one of ${METHODS.join(", ")}.`);
8298
8358
  if (!Number.isInteger(b.timeout_ms) || b.timeout_ms < 1e3 || b.timeout_ms > 1e4) errors.push("timeout_ms must be an integer from 1000 to 10000.");
8299
8359
  const seen = /* @__PURE__ */ new Set();
@@ -8768,6 +8828,98 @@ var require_dist = __commonJS({
8768
8828
  run: (client, { account_id, user_id }) => client.accounts.members.remove(account_id, user_id)
8769
8829
  })
8770
8830
  ];
8831
+ var import_zod16 = require("zod");
8832
+ var returnField = import_zod16.z.object({
8833
+ name: import_zod16.z.string().regex(/^[a-z][a-z0-9_]{0,39}$/).describe('snake_case field name, e.g. "booked"'),
8834
+ description: import_zod16.z.string().min(1).max(300).describe("What the value means, for the extractor"),
8835
+ type: import_zod16.z.enum(["string", "number", "boolean"]).optional().describe('Default "string"')
8836
+ });
8837
+ var IN_FLIGHT = /* @__PURE__ */ new Set(["queued", "initiated", "ringing", "in_progress", "active", "pending", "dialing"]);
8838
+ var INTEL_DONE = /* @__PURE__ */ new Set(["completed", "partial", "skipped", "failed"]);
8839
+ function isCallDone(call) {
8840
+ if (!call.status || IN_FLIGHT.has(call.status)) return false;
8841
+ if (call.status !== "completed") return true;
8842
+ return INTEL_DONE.has(call.intelligence?.status ?? "");
8843
+ }
8844
+ var POLL_MS = 3e3;
8845
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8846
+ var taskCallTools = [
8847
+ defineTool({
8848
+ name: "place_task_call",
8849
+ description: "WARNING: places a real, billable phone call. Places a real, billable call through an agent with task calls enabled. Use list_agents to find one (features.task_calls_enabled: true), then call wait_for_call with the returned call_id to get the results.",
8850
+ scope: "agent",
8851
+ risk: "sensitive",
8852
+ routes: ["POST /v1/agents/:id/task-calls"],
8853
+ keyScopes: ["calls:write"],
8854
+ input: {
8855
+ agent_id: import_zod16.z.string().describe("Agent with task calls enabled; find one with list_agents (features.task_calls_enabled: true)"),
8856
+ to: import_zod16.z.string().regex(/^\+[1-9]\d{6,14}$/).describe("Number to call, strict E.164, e.g. +919812345678"),
8857
+ on_behalf_of: import_zod16.z.string().min(1).max(120).describe("Business or person the call is for; spoken in the opener"),
8858
+ instructions: import_zod16.z.string().min(1).max(4e3).describe("What the call must do, in plain words"),
8859
+ return_fields: import_zod16.z.array(returnField).min(1).max(20).refine((fs) => new Set(fs.map((f) => f.name)).size === fs.length, "return_fields names must be unique").describe("Fields to extract from the call and return via wait_for_call"),
8860
+ opening_line: import_zod16.z.string().min(1).max(300).optional().describe("First line spoken; defaults to the agent opener or an AI disclosure"),
8861
+ from_number: import_zod16.z.string().optional().describe("Caller ID registered to the workspace; omit to use the agent's attached number")
8862
+ },
8863
+ run: async (client, p) => {
8864
+ const res = await client.agents.placeTaskCall(p.agent_id, {
8865
+ to: p.to,
8866
+ on_behalf_of: p.on_behalf_of,
8867
+ instructions: p.instructions,
8868
+ return_fields: p.return_fields,
8869
+ ...p.opening_line ? { opening_line: p.opening_line } : {},
8870
+ ...p.from_number ? { from_number: p.from_number } : {}
8871
+ });
8872
+ return {
8873
+ call_id: res?.call_id ?? res?.id,
8874
+ agent_id: res?.agent_id ?? p.agent_id,
8875
+ status: res?.status,
8876
+ next: "Call wait_for_call with this call_id to get the summary and fields."
8877
+ };
8878
+ }
8879
+ }),
8880
+ defineTool({
8881
+ name: "wait_for_call",
8882
+ description: "Wait (up to timeout_s, default 90) for a call to finish and its post-call results to be ready, then return status, summary and the extracted fields. Returns done:false if still running \u2014 just call it again.",
8883
+ scope: "agent",
8884
+ risk: "read",
8885
+ routes: ["GET /v1/calls/:id"],
8886
+ keyScopes: ["calls:read"],
8887
+ input: {
8888
+ call_id: import_zod16.z.string().describe("The call ID from place_task_call or create_call"),
8889
+ timeout_s: import_zod16.z.number().int().min(5).max(240).optional().describe("Max seconds to wait (5\u2013240, default 90)")
8890
+ },
8891
+ run: async (client, { call_id, timeout_s }) => {
8892
+ const deadline = Date.now() + (timeout_s ?? 90) * 1e3;
8893
+ let call = null;
8894
+ for (; ; ) {
8895
+ try {
8896
+ call = await client.calls.retrieve(call_id);
8897
+ } catch {
8898
+ }
8899
+ if (call && isCallDone(call)) {
8900
+ return {
8901
+ done: true,
8902
+ call_id,
8903
+ status: call.status,
8904
+ end_reason: call.end_reason ?? null,
8905
+ duration_seconds: call.bill_duration ?? call.duration_seconds ?? null,
8906
+ summary: call.intelligence?.summary ?? null,
8907
+ fields: call.intelligence?.structured_data ?? null,
8908
+ intelligence_status: call.intelligence?.status ?? null
8909
+ };
8910
+ }
8911
+ if (Date.now() + POLL_MS > deadline) break;
8912
+ await sleep(POLL_MS);
8913
+ }
8914
+ return {
8915
+ done: false,
8916
+ call_id,
8917
+ status: call?.status ?? "unknown",
8918
+ note: "Still running \u2014 call wait_for_call again with the same call_id."
8919
+ };
8920
+ }
8921
+ })
8922
+ ];
8771
8923
  var import_agentTools2 = (init_agentTools(), __toCommonJS(agentTools_exports));
8772
8924
  var REDACTED = "[redacted]";
8773
8925
  function redactToolSecrets(value) {
@@ -8810,15 +8962,16 @@ var require_dist = __commonJS({
8810
8962
  ...publish(shareLinkTools),
8811
8963
  ...publish(workspaceTools),
8812
8964
  ...publish(complianceTools),
8813
- ...publish(accountTools)
8965
+ ...publish(accountTools),
8966
+ ...publish(taskCallTools)
8814
8967
  ];
8815
8968
  var names = /* @__PURE__ */ new Set();
8816
8969
  for (const tool of opsToolCatalog2) {
8817
8970
  if (names.has(tool.name)) throw new Error(`Duplicate ops tool name: ${tool.name}`);
8818
8971
  names.add(tool.name);
8819
8972
  }
8820
- var import_zod16 = require("zod");
8821
- var WORKSPACE = import_zod16.z.string().optional().describe("Act in this workspace, e.g. one of your client workspaces (id from list_workspaces). Default: the workspace this connection uses.");
8973
+ var import_zod17 = require("zod");
8974
+ var WORKSPACE = import_zod17.z.string().optional().describe("Act in this workspace, e.g. one of your client workspaces (id from list_workspaces). Default: the workspace this connection uses.");
8822
8975
  function withWorkspaceParam2(tool) {
8823
8976
  return {
8824
8977
  ...tool,
@@ -8858,7 +9011,7 @@ function withToolErrors(handler) {
8858
9011
  // package.json
8859
9012
  var package_default = {
8860
9013
  name: "@rymi/mcp",
8861
- version: "2.8.1",
9014
+ version: "2.8.2",
8862
9015
  description: "Rymi MCP server \u2014 manage AI voice agents via Model Context Protocol",
8863
9016
  license: "MIT",
8864
9017
  author: "Rymi AI <engineering@rymi.live>",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rymi/mcp",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
4
4
  "description": "Rymi MCP server — manage AI voice agents via Model Context Protocol",
5
5
  "license": "MIT",
6
6
  "author": "Rymi AI <engineering@rymi.live>",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@modelcontextprotocol/sdk": "^1.11.1",
47
- "@rymi/node": "^2.7.1",
47
+ "@rymi/node": "^2.7.2",
48
48
  "zod": "^3.23.0"
49
49
  },
50
50
  "devDependencies": {