open-agents-ai 0.50.0 → 0.52.0

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 (2) hide show
  1. package/dist/index.js +162 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17284,6 +17284,8 @@ function renderSlashHelp() {
17284
17284
  ["/listen manual", "Manual mode \u2014 press Enter to submit (turns off auto)"],
17285
17285
  ["/listen auto", "Auto-submit after 3s silence (blinking \u25CF indicator)"],
17286
17286
  ["/listen stop", "Stop listening"],
17287
+ ["/call", "Start voice call session (cloudflared tunnel + PCM frontend)"],
17288
+ ["/call stop", "Stop active call session"],
17287
17289
  ["/cost", "Show session token cost breakdown"],
17288
17290
  ["/evaluate", "Evaluate last completed task (LLM quality scoring)"],
17289
17291
  ["/task-type", "Set task type (code, document, analysis, plan, general, auto)"],
@@ -17672,6 +17674,7 @@ var init_render = __esm({
17672
17674
  "/update",
17673
17675
  "/voice",
17674
17676
  "/listen",
17677
+ "/call",
17675
17678
  "/stream",
17676
17679
  "/verbose",
17677
17680
  "/dream",
@@ -20711,6 +20714,41 @@ async function handleSlashCommand(input, ctx) {
20711
20714
  renderInfo(msg);
20712
20715
  return "handled";
20713
20716
  }
20717
+ case "call": {
20718
+ if (!ctx.callStart) {
20719
+ renderWarning("Call mode not available in this context.");
20720
+ return "handled";
20721
+ }
20722
+ if (arg === "stop" || arg === "off") {
20723
+ if (ctx.isCallActive?.()) {
20724
+ await ctx.callStop?.();
20725
+ renderInfo("Call session ended.");
20726
+ } else {
20727
+ renderWarning("No active call session.");
20728
+ }
20729
+ return "handled";
20730
+ }
20731
+ if (ctx.isCallActive?.()) {
20732
+ const url = ctx.getCallUrl?.();
20733
+ if (url) {
20734
+ renderInfo(`Call session active: ${c2.bold(url)}`);
20735
+ } else {
20736
+ renderWarning("Call session active but no URL available yet.");
20737
+ }
20738
+ return "handled";
20739
+ }
20740
+ try {
20741
+ const url = await ctx.callStart();
20742
+ if (url) {
20743
+ renderInfo(`Call session started: ${c2.bold(url)}`);
20744
+ } else {
20745
+ renderWarning("Failed to start call session.");
20746
+ }
20747
+ } catch (err) {
20748
+ renderError(`Call error: ${err instanceof Error ? err.message : String(err)}`);
20749
+ }
20750
+ return "handled";
20751
+ }
20714
20752
  case "bruteforce":
20715
20753
  case "brute": {
20716
20754
  const isOn = ctx.bruteForceToggle();
@@ -20941,27 +20979,36 @@ async function handleSlashCommand(input, ctx) {
20941
20979
  ctx.telegramStatus?.();
20942
20980
  return "handled";
20943
20981
  }
20982
+ const isLocal = parts.includes("--local");
20944
20983
  const keyIdx = parts.indexOf("--key");
20945
- if (keyIdx !== -1) {
20946
- const token = parts[keyIdx + 1];
20947
- if (!token) {
20948
- renderWarning("Usage: /telegram --key <bot-token>");
20949
- renderInfo("Get a bot token from @BotFather on Telegram.");
20950
- return "handled";
20951
- }
20952
- ctx.saveTelegramSettings?.({ key: token });
20953
- renderInfo(`Telegram bot token saved (${token.slice(0, 6)}...). Use /telegram to start.`);
20954
- return "handled";
20955
- }
20956
20984
  const adminIdx = parts.indexOf("--admin");
20957
- if (adminIdx !== -1) {
20958
- const userId = parts[adminIdx + 1];
20959
- if (!userId) {
20960
- renderWarning("Usage: /telegram --admin <user-id-or-username>");
20961
- return "handled";
20962
- }
20963
- ctx.saveTelegramSettings?.({ admin: userId });
20964
- renderInfo(`Telegram admin set to ${c2.bold(userId)}. Only this user's messages will be processed.`);
20985
+ if (keyIdx !== -1 || adminIdx !== -1) {
20986
+ const settings = { local: isLocal };
20987
+ const scope = isLocal ? "project" : "global";
20988
+ if (keyIdx !== -1) {
20989
+ const token = parts[keyIdx + 1];
20990
+ if (!token || token.startsWith("--")) {
20991
+ renderWarning("Usage: /telegram --key <bot-token> [--admin <id>] [--local]");
20992
+ renderInfo("Get a bot token from @BotFather on Telegram.");
20993
+ return "handled";
20994
+ }
20995
+ settings.key = token;
20996
+ }
20997
+ if (adminIdx !== -1) {
20998
+ const userId = parts[adminIdx + 1];
20999
+ if (!userId || userId.startsWith("--")) {
21000
+ renderWarning("Usage: /telegram --admin <user-id-or-username> [--key <token>] [--local]");
21001
+ return "handled";
21002
+ }
21003
+ settings.admin = userId;
21004
+ }
21005
+ ctx.saveTelegramSettings?.(settings);
21006
+ if (settings.key)
21007
+ renderInfo(`Telegram bot token saved to ${scope} settings (${settings.key.slice(0, 6)}...).`);
21008
+ if (settings.admin)
21009
+ renderInfo(`Telegram admin set to ${c2.bold(settings.admin)} (${scope}).`);
21010
+ if (!ctx.isTelegramActive?.() && settings.key)
21011
+ renderInfo("Use /telegram to start.");
20965
21012
  return "handled";
20966
21013
  }
20967
21014
  if (!arg) {
@@ -20985,11 +21032,12 @@ async function handleSlashCommand(input, ctx) {
20985
21032
  }
20986
21033
  renderWarning(`Unknown argument: "${arg}"`);
20987
21034
  renderInfo("Usage:");
20988
- renderInfo(" /telegram --key <token> Save bot token");
20989
- renderInfo(" /telegram --admin <id> Set admin user filter");
21035
+ renderInfo(" /telegram --key <token> Save bot token (global)");
21036
+ renderInfo(" /telegram --admin <id> Set admin filter (global)");
20990
21037
  renderInfo(" /telegram Toggle on/off");
20991
21038
  renderInfo(" /telegram stop Stop bridge");
20992
21039
  renderInfo(" /telegram status Show status");
21040
+ renderInfo(" Add --local to scope settings to this project only");
20993
21041
  return "handled";
20994
21042
  }
20995
21043
  default: {
@@ -27406,6 +27454,8 @@ with summary "no_reply" to silently skip without responding.
27406
27454
  toolPolicyConfig;
27407
27455
  /** Command handler for admin DM slash commands (wired from interactive.ts) */
27408
27456
  commandHandler = null;
27457
+ /** Callback to get active call session URL (wired from interactive.ts) */
27458
+ callUrlGetter = null;
27409
27459
  /** Media cache — fileUniqueId → cache entry */
27410
27460
  mediaCache = /* @__PURE__ */ new Map();
27411
27461
  /** Media cache directory */
@@ -27439,6 +27489,10 @@ with summary "no_reply" to silently skip without responding.
27439
27489
  setCommandHandler(handler) {
27440
27490
  this.commandHandler = handler;
27441
27491
  }
27492
+ /** Register callback to get active call URL for /call button forwarding */
27493
+ setCallUrlGetter(getter) {
27494
+ this.callUrlGetter = getter;
27495
+ }
27442
27496
  /** Register event handler for sub-agent activity (waterfall view) */
27443
27497
  setOnSubAgentEvent(handler) {
27444
27498
  this.onSubAgentEvent = handler;
@@ -27587,6 +27641,17 @@ with summary "no_reply" to silently skip without responding.
27587
27641
  const isAdmin = this.isAdminUser(msg);
27588
27642
  const toolContext = this.resolveToolContext(msg, isAdmin);
27589
27643
  const isAdminDM = toolContext === "telegram-admin-dm";
27644
+ if (msg.text.trim().toLowerCase() === "/call" && this.callUrlGetter) {
27645
+ const callUrl = this.callUrlGetter();
27646
+ if (callUrl) {
27647
+ await this.sendCallButton(msg.chatId, callUrl);
27648
+ return;
27649
+ }
27650
+ if (!isAdmin) {
27651
+ await this.sendMessage(msg.chatId, "No active call session. Ask the admin to start one with /call.");
27652
+ return;
27653
+ }
27654
+ }
27590
27655
  if (isAdminDM && msg.text.startsWith("/") && this.commandHandler) {
27591
27656
  const cmdName = msg.text.split(/\s+/)[0].slice(1).toLowerCase();
27592
27657
  if (cmdName !== "start") {
@@ -27994,6 +28059,26 @@ Telegram admin: @${msg.username}` : `Telegram ${isGroup ? "group" : "public"} ch
27994
28059
  }
27995
28060
  }
27996
28061
  }
28062
+ /** Send an inline keyboard button to start a voice call session */
28063
+ async sendCallButton(chatId, url) {
28064
+ try {
28065
+ const result = await this.apiCall("sendMessage", {
28066
+ chat_id: chatId,
28067
+ text: "\u{1F4DE} <b>Voice Call Session</b>\n\nTap the button below to join the live voice call with the agent.",
28068
+ parse_mode: "HTML",
28069
+ reply_markup: {
28070
+ inline_keyboard: [[
28071
+ { text: "\u{1F50A} Start Call", url }
28072
+ ]]
28073
+ }
28074
+ });
28075
+ this.state.messagesSent++;
28076
+ return result.result?.message_id ?? null;
28077
+ } catch (err) {
28078
+ renderWarning(`Failed to send call button: ${err instanceof Error ? err.message : String(err)}`);
28079
+ return null;
28080
+ }
28081
+ }
27997
28082
  /**
27998
28083
  * Send a voice message (WAV/OGG) to a Telegram chat using multipart form-data.
27999
28084
  * Telegram requires OGG/Opus for sendVoice, but also accepts sendAudio with WAV.
@@ -30695,6 +30780,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
30695
30780
  telegramBridge.setVoiceEngine(voiceEngine);
30696
30781
  telegramBridge.voiceEnabled = true;
30697
30782
  }
30783
+ telegramBridge.setCallUrlGetter(() => voiceSession?.tunnelUrl ?? null);
30698
30784
  telegramBridge.setCommandHandler(async (input) => {
30699
30785
  const captured = [];
30700
30786
  const origWrite = process.stdout.write;
@@ -30753,10 +30839,15 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
30753
30839
  if (settings.admin !== void 0) {
30754
30840
  savedSettings.telegramAdmin = settings.admin;
30755
30841
  }
30756
- saveProjectSettings(repoRoot, {
30842
+ const payload = {
30757
30843
  ...settings.key !== void 0 ? { telegramKey: settings.key } : {},
30758
30844
  ...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {}
30759
- });
30845
+ };
30846
+ if (settings.local) {
30847
+ saveProjectSettings(repoRoot, payload);
30848
+ } else {
30849
+ saveGlobalSettings(payload);
30850
+ }
30760
30851
  },
30761
30852
  telegramStatus() {
30762
30853
  const active = telegramBridge?.isActive ?? false;
@@ -30852,6 +30943,54 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
30852
30943
  }
30853
30944
  return text ? `Stopped. Last transcript: "${text}"` : "Stopped listening.";
30854
30945
  },
30946
+ // Voice call session — standalone cloudflared tunnel for /call
30947
+ async callStart() {
30948
+ if (voiceSession?.isActive) {
30949
+ return voiceSession.tunnelUrl;
30950
+ }
30951
+ voiceSession = new VoiceSession();
30952
+ voiceSession.on("userConnected", (id, username) => {
30953
+ writeContent(() => renderVoiceSessionUser("connected", username));
30954
+ });
30955
+ voiceSession.on("userDisconnected", (id) => {
30956
+ writeContent(() => renderVoiceSessionUser("disconnected", id));
30957
+ });
30958
+ try {
30959
+ const tunnelUrl = await voiceSession.start();
30960
+ writeContent(() => renderVoiceSessionStart(tunnelUrl));
30961
+ if (telegramBridge?.isActive && savedSettings.telegramAdmin) {
30962
+ const adminChatId = parseInt(savedSettings.telegramAdmin, 10);
30963
+ if (!isNaN(adminChatId)) {
30964
+ telegramBridge.sendCallButton(adminChatId, tunnelUrl).catch(() => {
30965
+ });
30966
+ }
30967
+ }
30968
+ return tunnelUrl;
30969
+ } catch (err) {
30970
+ writeContent(() => renderWarning(`Voice session failed: ${err instanceof Error ? err.message : String(err)}`));
30971
+ voiceSession = null;
30972
+ return null;
30973
+ }
30974
+ },
30975
+ async callStop() {
30976
+ if (voiceSession?.isActive) {
30977
+ const runtime = voiceSession.runtime;
30978
+ await voiceSession.stop();
30979
+ writeContent(() => renderVoiceSessionStop(runtime));
30980
+ voiceSession = null;
30981
+ }
30982
+ },
30983
+ isCallActive() {
30984
+ return voiceSession?.isActive ?? false;
30985
+ },
30986
+ getCallUrl() {
30987
+ return voiceSession?.tunnelUrl ?? null;
30988
+ },
30989
+ async sendCallButton(chatId, url) {
30990
+ if (telegramBridge?.isActive) {
30991
+ await telegramBridge.sendCallButton(chatId, url);
30992
+ }
30993
+ },
30855
30994
  getEmojis: () => getEmojisEnabled(),
30856
30995
  setEmojis: (enabled) => setEmojisEnabled(enabled),
30857
30996
  getColors: () => getColorsEnabled(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.50.0",
3
+ "version": "0.52.0",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",