open-agents-ai 0.51.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 +124 -0
  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();
@@ -27416,6 +27454,8 @@ with summary "no_reply" to silently skip without responding.
27416
27454
  toolPolicyConfig;
27417
27455
  /** Command handler for admin DM slash commands (wired from interactive.ts) */
27418
27456
  commandHandler = null;
27457
+ /** Callback to get active call session URL (wired from interactive.ts) */
27458
+ callUrlGetter = null;
27419
27459
  /** Media cache — fileUniqueId → cache entry */
27420
27460
  mediaCache = /* @__PURE__ */ new Map();
27421
27461
  /** Media cache directory */
@@ -27449,6 +27489,10 @@ with summary "no_reply" to silently skip without responding.
27449
27489
  setCommandHandler(handler) {
27450
27490
  this.commandHandler = handler;
27451
27491
  }
27492
+ /** Register callback to get active call URL for /call button forwarding */
27493
+ setCallUrlGetter(getter) {
27494
+ this.callUrlGetter = getter;
27495
+ }
27452
27496
  /** Register event handler for sub-agent activity (waterfall view) */
27453
27497
  setOnSubAgentEvent(handler) {
27454
27498
  this.onSubAgentEvent = handler;
@@ -27597,6 +27641,17 @@ with summary "no_reply" to silently skip without responding.
27597
27641
  const isAdmin = this.isAdminUser(msg);
27598
27642
  const toolContext = this.resolveToolContext(msg, isAdmin);
27599
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
+ }
27600
27655
  if (isAdminDM && msg.text.startsWith("/") && this.commandHandler) {
27601
27656
  const cmdName = msg.text.split(/\s+/)[0].slice(1).toLowerCase();
27602
27657
  if (cmdName !== "start") {
@@ -28004,6 +28059,26 @@ Telegram admin: @${msg.username}` : `Telegram ${isGroup ? "group" : "public"} ch
28004
28059
  }
28005
28060
  }
28006
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
+ }
28007
28082
  /**
28008
28083
  * Send a voice message (WAV/OGG) to a Telegram chat using multipart form-data.
28009
28084
  * Telegram requires OGG/Opus for sendVoice, but also accepts sendAudio with WAV.
@@ -30705,6 +30780,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
30705
30780
  telegramBridge.setVoiceEngine(voiceEngine);
30706
30781
  telegramBridge.voiceEnabled = true;
30707
30782
  }
30783
+ telegramBridge.setCallUrlGetter(() => voiceSession?.tunnelUrl ?? null);
30708
30784
  telegramBridge.setCommandHandler(async (input) => {
30709
30785
  const captured = [];
30710
30786
  const origWrite = process.stdout.write;
@@ -30867,6 +30943,54 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
30867
30943
  }
30868
30944
  return text ? `Stopped. Last transcript: "${text}"` : "Stopped listening.";
30869
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
+ },
30870
30994
  getEmojis: () => getEmojisEnabled(),
30871
30995
  setEmojis: (enabled) => setEmojisEnabled(enabled),
30872
30996
  getColors: () => getColorsEnabled(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.51.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",