open-agents-ai 0.38.0 → 0.38.1

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 +11 -4
  2. package/dist/index.js +125 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -61,7 +61,7 @@ An autonomous multi-turn tool-calling agent that reads your code, makes changes,
61
61
  - **Self-learning** — auto-fetches docs from the web when encountering unfamiliar APIs
62
62
  - **Seamless `/update`** — in-place update and reload with automatic context save/restore
63
63
  - **Blessed mode** — `/full-send-bless` infinite warm loop keeps model weights in VRAM, auto-cycles tasks, never exits until you say stop
64
- - **Telegram bridge** — `/telegram <token>` public ingress/egress with mandatory safety filter for untrusted input
64
+ - **Telegram bridge** — `/telegram --key <token> --admin <userid>` public ingress/egress with admin filter and mandatory safety filter; bare `/telegram` toggles the service watchdog
65
65
  - **Task control** — `/pause` (gentle halt at turn boundary), `/stop` (immediate kill), `/resume` to continue
66
66
  - **Model-tier awareness** — dynamic tool sets, prompt complexity, and context limits scale with model size (small/medium/large)
67
67
 
@@ -275,10 +275,17 @@ When blessed mode is active:
275
275
  Connect the agent to a Telegram bot for public-facing message handling. Messages received from Telegram are processed with a mandatory safety filter that warns the agent it is talking to the general public.
276
276
 
277
277
  ```bash
278
- /telegram <bot-token> # Connect to Telegram bot (get token from @BotFather)
279
- /telegram stop # Disconnect
278
+ /telegram --key <token> # Save bot token (persisted to .oa/settings.json)
279
+ /telegram --admin <userid> # Set admin filter — only this user can interact
280
+ /telegram # Toggle bridge on/off (uses saved key)
281
+ /telegram status # Show connection status
282
+ /telegram stop # Disconnect
280
283
  ```
281
284
 
285
+ The bot token and admin ID are persisted to project settings, so you only need to set them once. After that, bare `/telegram` toggles the bridge on and off like a service watchdog.
286
+
287
+ **Admin filter** — when `--admin` is set, only messages from that user ID (numeric Telegram ID or username) are processed. All other messages are silently ignored. This lets you lock down the bot to a single operator.
288
+
282
289
  **Safety filter** — every Telegram-sourced task is wrapped with strict safety instructions:
283
290
  - Never share private information, API keys, file paths, or system internals
284
291
  - Never execute destructive commands based on Telegram input
@@ -288,7 +295,7 @@ Connect the agent to a Telegram bot for public-facing message handling. Messages
288
295
 
289
296
  **Egress** — when a task completes that originated from Telegram, the agent's summary is automatically sent back to the originating chat. Long responses are truncated to Telegram's 4096-character limit.
290
297
 
291
- **Combined with blessed mode** — `/full-send-bless` + `/telegram <token>` creates a persistent, always-on agent that processes Telegram messages around the clock while keeping the model warm.
298
+ **Combined with blessed mode** — `/full-send-bless` + `/telegram` creates a persistent, always-on agent that processes Telegram messages around the clock while keeping the model warm.
292
299
 
293
300
  ## Listen Mode — Live Bidirectional Audio
294
301
 
package/dist/index.js CHANGED
@@ -16920,7 +16920,10 @@ function renderSlashHelp() {
16920
16920
  ["/<skill-name> [args]", "Invoke an AIWG skill directly"],
16921
16921
  ["/full-send-bless", "Infinite warm loop \u2014 keeps model in VRAM, auto-cycles tasks"],
16922
16922
  ["/bless stop", "End blessed mode"],
16923
- ["/telegram <token>", "Connect Telegram bot (ingress/egress with safety filter)"],
16923
+ ["/telegram --key <token>", "Save Telegram bot token (persisted to settings)"],
16924
+ ["/telegram --admin <userid>", "Set admin user filter (only this user can interact)"],
16925
+ ["/telegram", "Toggle Telegram bridge on/off (uses saved key)"],
16926
+ ["/telegram status", "Show Telegram bridge status"],
16924
16927
  ["/telegram stop", "Disconnect Telegram bridge"],
16925
16928
  ["/style", "Show current response style"],
16926
16929
  ["/style <preset>", "Set style: concise, balanced, verbose, pedagogical"],
@@ -19645,7 +19648,8 @@ async function handleSlashCommand(input, ctx) {
19645
19648
  }
19646
19649
  case "telegram":
19647
19650
  case "tg": {
19648
- if (arg === "stop" || arg === "off") {
19651
+ const parts = arg ? arg.split(/\s+/) : [];
19652
+ if (parts[0] === "stop" || parts[0] === "off") {
19649
19653
  if (ctx.isTelegramActive?.()) {
19650
19654
  ctx.telegramStop?.();
19651
19655
  } else {
@@ -19653,20 +19657,59 @@ async function handleSlashCommand(input, ctx) {
19653
19657
  }
19654
19658
  return "handled";
19655
19659
  }
19656
- if (ctx.isTelegramActive?.()) {
19657
- renderWarning("Telegram bridge already active. Use /telegram stop to disconnect first.");
19660
+ if (parts[0] === "status") {
19661
+ ctx.telegramStatus?.();
19658
19662
  return "handled";
19659
19663
  }
19660
- if (!arg) {
19661
- renderWarning("Usage: /telegram <bot-token> or /telegram stop");
19662
- renderInfo("Get a bot token from @BotFather on Telegram.");
19664
+ const keyIdx = parts.indexOf("--key");
19665
+ if (keyIdx !== -1) {
19666
+ const token = parts[keyIdx + 1];
19667
+ if (!token) {
19668
+ renderWarning("Usage: /telegram --key <bot-token>");
19669
+ renderInfo("Get a bot token from @BotFather on Telegram.");
19670
+ return "handled";
19671
+ }
19672
+ ctx.saveTelegramSettings?.({ key: token });
19673
+ renderInfo(`Telegram bot token saved (${token.slice(0, 6)}...). Use /telegram to start.`);
19663
19674
  return "handled";
19664
19675
  }
19665
- try {
19666
- await ctx.telegramStart?.(arg);
19667
- } catch (err) {
19668
- renderError(`Telegram error: ${err instanceof Error ? err.message : String(err)}`);
19676
+ const adminIdx = parts.indexOf("--admin");
19677
+ if (adminIdx !== -1) {
19678
+ const userId = parts[adminIdx + 1];
19679
+ if (!userId) {
19680
+ renderWarning("Usage: /telegram --admin <user-id-or-username>");
19681
+ return "handled";
19682
+ }
19683
+ ctx.saveTelegramSettings?.({ admin: userId });
19684
+ renderInfo(`Telegram admin set to ${c2.bold(userId)}. Only this user's messages will be processed.`);
19685
+ return "handled";
19669
19686
  }
19687
+ if (!arg) {
19688
+ if (ctx.isTelegramActive?.()) {
19689
+ ctx.telegramStop?.();
19690
+ return "handled";
19691
+ }
19692
+ const settings = ctx.getTelegramSettings?.() ?? {};
19693
+ if (!settings.key) {
19694
+ renderWarning("No Telegram bot token configured.");
19695
+ renderInfo("Set one first: /telegram --key <bot-token>");
19696
+ renderInfo("Get a token from @BotFather on Telegram.");
19697
+ return "handled";
19698
+ }
19699
+ try {
19700
+ await ctx.telegramStart?.(settings.key, settings.admin);
19701
+ } catch (err) {
19702
+ renderError(`Telegram error: ${err instanceof Error ? err.message : String(err)}`);
19703
+ }
19704
+ return "handled";
19705
+ }
19706
+ renderWarning(`Unknown argument: "${arg}"`);
19707
+ renderInfo("Usage:");
19708
+ renderInfo(" /telegram --key <token> Save bot token");
19709
+ renderInfo(" /telegram --admin <id> Set admin user filter");
19710
+ renderInfo(" /telegram Toggle on/off");
19711
+ renderInfo(" /telegram stop Stop bridge");
19712
+ renderInfo(" /telegram status Show status");
19670
19713
  return "handled";
19671
19714
  }
19672
19715
  default: {
@@ -23535,18 +23578,43 @@ var init_bless_engine = __esm({
23535
23578
  });
23536
23579
 
23537
23580
  // packages/cli/dist/tui/telegram-bridge.js
23538
- function renderTelegramStart(botUsername) {
23581
+ function renderTelegramStart(botUsername, adminId) {
23539
23582
  process.stdout.write(`
23540
23583
  ${c2.cyan("\u2708")} ${c2.bold("Telegram Bridge")} connected as @${botUsername}
23541
23584
  `);
23542
- process.stdout.write(` ${c2.dim("Receiving messages from Telegram")}
23585
+ if (adminId) {
23586
+ process.stdout.write(` ${c2.dim(`Admin filter: only user ${adminId}`)}
23543
23587
  `);
23588
+ }
23544
23589
  process.stdout.write(` ${c2.dim("Safety filter: ACTIVE \u2014 public channel mode")}
23545
23590
  `);
23546
- process.stdout.write(` ${c2.dim("Use /telegram stop to disconnect")}
23591
+ process.stdout.write(` ${c2.dim("Use /telegram to toggle off, or /telegram stop")}
23547
23592
 
23548
23593
  `);
23549
23594
  }
23595
+ function renderTelegramStatus(active, botUsername, adminId) {
23596
+ if (active) {
23597
+ process.stdout.write(`
23598
+ ${c2.green("\u25CF")} Telegram bridge: ${c2.bold("ACTIVE")} (@${botUsername ?? "?"})
23599
+ `);
23600
+ if (adminId) {
23601
+ process.stdout.write(` Admin: ${adminId}
23602
+ `);
23603
+ }
23604
+ process.stdout.write(` ${c2.dim("Use /telegram to toggle off")}
23605
+
23606
+ `);
23607
+ } else {
23608
+ process.stdout.write(`
23609
+ ${c2.dim("\u25CB")} Telegram bridge: ${c2.bold("INACTIVE")}
23610
+ `);
23611
+ process.stdout.write(` ${c2.dim("Use /telegram --key <token> to set bot token")}
23612
+ `);
23613
+ process.stdout.write(` ${c2.dim("Use /telegram to toggle on (after key is set)")}
23614
+
23615
+ `);
23616
+ }
23617
+ }
23550
23618
  function renderTelegramStop(state) {
23551
23619
  process.stdout.write(`
23552
23620
  ${c2.yellow("\u2708")} ${c2.bold("Telegram Bridge")} disconnected
@@ -23599,10 +23667,16 @@ prioritize safety and privacy over helpfulness. When in doubt, decline politely.
23599
23667
  messagesReceived: 0,
23600
23668
  messagesSent: 0
23601
23669
  };
23670
+ /** Admin user ID — if set, only messages from this user are processed */
23671
+ adminUserId = null;
23602
23672
  constructor(botToken, onMessage) {
23603
23673
  this.botToken = botToken;
23604
23674
  this.onMessage = onMessage;
23605
23675
  }
23676
+ /** Set admin user ID filter. Only messages from this user will be processed. */
23677
+ setAdmin(userId) {
23678
+ this.adminUserId = userId;
23679
+ }
23606
23680
  get isActive() {
23607
23681
  return this.polling;
23608
23682
  }
@@ -23673,11 +23747,19 @@ prioritize safety and privacy over helpfulness. When in doubt, decline politely.
23673
23747
  for (const update of result.result) {
23674
23748
  this.lastUpdateId = update.update_id;
23675
23749
  if (update.message?.text) {
23750
+ const fromId = String(update.message.from?.id ?? "");
23751
+ const fromUser = update.message.from?.username ?? "";
23752
+ if (this.adminUserId) {
23753
+ const isAdmin = fromId === this.adminUserId || fromUser === this.adminUserId;
23754
+ if (!isAdmin) {
23755
+ continue;
23756
+ }
23757
+ }
23676
23758
  this.state.messagesReceived++;
23677
23759
  this.onMessage({
23678
23760
  chatId: update.message.chat.id,
23679
23761
  text: update.message.text,
23680
- username: update.message.from?.username ?? "unknown",
23762
+ username: fromUser || "unknown",
23681
23763
  firstName: update.message.from?.first_name,
23682
23764
  messageId: update.message.message_id
23683
23765
  });
@@ -25803,7 +25885,7 @@ async function startInteractive(config, repoPath) {
25803
25885
  return blessEngine?.isActive ?? false;
25804
25886
  },
25805
25887
  // Telegram bridge
25806
- async telegramStart(token) {
25888
+ async telegramStart(token, adminId) {
25807
25889
  telegramBridge = new TelegramBridge(token, (msg) => {
25808
25890
  writeContent(() => renderTelegramMessage(msg.username, msg.text));
25809
25891
  if (!activeTask) {
@@ -25825,8 +25907,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
25825
25907
  showPrompt();
25826
25908
  }
25827
25909
  });
25910
+ if (adminId) {
25911
+ telegramBridge.setAdmin(adminId);
25912
+ }
25828
25913
  await telegramBridge.start();
25829
- writeContent(() => renderTelegramStart(telegramBridge.botUsername));
25914
+ writeContent(() => renderTelegramStart(telegramBridge.botUsername, adminId));
25830
25915
  showPrompt();
25831
25916
  },
25832
25917
  telegramStop() {
@@ -25840,6 +25925,29 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
25840
25925
  isTelegramActive() {
25841
25926
  return telegramBridge?.isActive ?? false;
25842
25927
  },
25928
+ getTelegramSettings() {
25929
+ return {
25930
+ key: savedSettings.telegramKey,
25931
+ admin: savedSettings.telegramAdmin
25932
+ };
25933
+ },
25934
+ saveTelegramSettings(settings) {
25935
+ if (settings.key !== void 0) {
25936
+ savedSettings.telegramKey = settings.key;
25937
+ }
25938
+ if (settings.admin !== void 0) {
25939
+ savedSettings.telegramAdmin = settings.admin;
25940
+ }
25941
+ saveProjectSettings(repoRoot, {
25942
+ ...settings.key !== void 0 ? { telegramKey: settings.key } : {},
25943
+ ...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {}
25944
+ });
25945
+ },
25946
+ telegramStatus() {
25947
+ const active = telegramBridge?.isActive ?? false;
25948
+ const botUser = active ? telegramBridge?.botUsername : void 0;
25949
+ writeContent(() => renderTelegramStatus(active, botUser, savedSettings.telegramAdmin));
25950
+ },
25843
25951
  // Listen mode (transcribe-cli integration)
25844
25952
  async listenToggle() {
25845
25953
  const engine = getListenEngine();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.38.0",
3
+ "version": "0.38.1",
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",