open-agents-ai 0.187.583 → 0.187.584
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/README.md +15 -4
- package/dist/index.js +310 -15
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3365,7 +3365,7 @@ All settings commands accept `--local` to save to project `.oa/settings.json` in
|
|
|
3365
3365
|
|
|
3366
3366
|
`/platforms menu` opens a keyboard-driven connectivity surface for platform status and onboarding. The same surface is available through `/gateway menu`.
|
|
3367
3367
|
|
|
3368
|
-
Supported adapter IDs are `telegram`, `discord`, `slack`, `matrix`, and `webhook`. Telegram is wired into the existing bridge controls, so the menu can save the bot token, save the admin user ID, start the bridge, stop it, and show status. The other platform adapters expose the shared connector configuration surface: enable/disable, token environment variable, base URL or webhook URL, default target/channel/room, polling vs webhook mode, redacted config display, and status/health reporting.
|
|
3368
|
+
Supported adapter IDs are `telegram`, `discord`, `slack`, `matrix`, and `webhook`. Telegram is wired into the existing bridge controls, so the menu can save the bot token, save the admin user ID, set the interaction mode, start the bridge, stop it, and show status. The other platform adapters expose the shared connector configuration surface: enable/disable, token environment variable, base URL or webhook URL, default target/channel/room, polling vs webhook mode, redacted config display, and status/health reporting.
|
|
3369
3369
|
|
|
3370
3370
|
Use environment variables for credentials:
|
|
3371
3371
|
|
|
@@ -3416,7 +3416,7 @@ The steering sub-agent uses the same model and backend as the main agent with `m
|
|
|
3416
3416
|
|
|
3417
3417
|
<div align="right"><a href="#top">back to top</a></div>
|
|
3418
3418
|
|
|
3419
|
-
Connect the agent to a Telegram bot.
|
|
3419
|
+
Connect the agent to a Telegram bot. Telegram can run in auto, chat, or action mode: conversational messages get rapid streamed replies in chat mode, while codebase/file/run requests use dedicated action sub-agents that are visible in the terminal waterfall alongside other agent activity.
|
|
3420
3420
|
|
|
3421
3421
|
```bash
|
|
3422
3422
|
/telegram --key <token> # Save bot token (persisted to .oa/settings.json)
|
|
@@ -3424,6 +3424,7 @@ Connect the agent to a Telegram bot. Each incoming message spawns a dedicated su
|
|
|
3424
3424
|
/telegram # Toggle bridge on/off (uses saved key)
|
|
3425
3425
|
/telegram status # Show connection status + active sub-agents
|
|
3426
3426
|
/telegram stop # Disconnect and kill all sub-agents
|
|
3427
|
+
/telegram mode auto|chat|action # Set interaction routing profile
|
|
3427
3428
|
/telegram auth # Show a one-time TUI-only admin auth code
|
|
3428
3429
|
/telegram auth cancel # Cancel the pending admin auth code
|
|
3429
3430
|
/telegram bot <username> <text> # Send bot-to-bot message by @username
|
|
@@ -3438,7 +3439,17 @@ Connect the agent to a Telegram bot. Each incoming message spawns a dedicated su
|
|
|
3438
3439
|
/telegram delete-reactions <chat> --user <id> # Delete recent reactions
|
|
3439
3440
|
```
|
|
3440
3441
|
|
|
3441
|
-
The bot token
|
|
3442
|
+
The bot token, admin ID, and interaction mode are persisted to settings, so you only need to set them once. After that, bare `/telegram` toggles the bridge on and off like a service watchdog.
|
|
3443
|
+
|
|
3444
|
+
### Telegram Interaction Modes
|
|
3445
|
+
|
|
3446
|
+
Use `/telegram mode auto|chat|action` to control how inbound Telegram messages are routed:
|
|
3447
|
+
|
|
3448
|
+
- **auto** — short greetings, quick questions, playful messages, and conversational turns use fast streamed chat replies; explicit codebase/file/command/run/test requests use action sub-agents.
|
|
3449
|
+
- **chat** — every non-command message gets a direct quick-chat completion with no tool loop. This is best for rapid back-and-forth conversation.
|
|
3450
|
+
- **action** — every non-command message runs through the Telegram sub-agent path with the configured tool policy.
|
|
3451
|
+
|
|
3452
|
+
Telegram quick-chat views and action sub-agent views both appear as blue plane-labeled items in the systems header. Clicking one swaps the main scrollable window to that Telegram-only content buffer; press Esc to return to the primary view.
|
|
3442
3453
|
|
|
3443
3454
|
### Telegram Admin Authentication
|
|
3444
3455
|
|
|
@@ -3462,7 +3473,7 @@ The Telegram bridge handles modern Bot API traffic directly:
|
|
|
3462
3473
|
- **Direct-message channels** — direct-message channel/topic metadata is preserved on normalized Telegram messages for routing and future adapter logic.
|
|
3463
3474
|
- **Drafts and profile chat reads** — `/telegram draft` can set or clear message drafts, and `/telegram personal <user_id> <limit>` fetches recent messages from a user's profile personal chat.
|
|
3464
3475
|
- **Reactions/admin helpers** — `/telegram delete-reaction` removes a message reaction, `/telegram delete-reactions` removes recent reactions by a user/chat, and `/telegram admins <chat> [--bots]` can include bot administrators.
|
|
3465
|
-
- **TUI
|
|
3476
|
+
- **TUI Telegram visibility** — Telegram quick-chat and action conversations register as blue plane-labeled views in the systems panel; clicking a view swaps the main scrollable window to that Telegram-only buffer, and the footer shows an active plane indicator while the bridge or Telegram work is active.
|
|
3466
3477
|
|
|
3467
3478
|
### Admin Slash Command Passthrough
|
|
3468
3479
|
|
package/dist/index.js
CHANGED
|
@@ -549063,6 +549063,7 @@ var init_command_registry = __esm({
|
|
|
549063
549063
|
["/telegram", "Toggle Telegram bridge on/off (uses saved key)"],
|
|
549064
549064
|
["/telegram status", "Show Telegram bridge status"],
|
|
549065
549065
|
["/telegram stop", "Disconnect Telegram bridge"],
|
|
549066
|
+
["/telegram mode auto|chat|action", "Set Telegram interaction routing: auto, fast chat, or action sub-agent"],
|
|
549066
549067
|
["/telegram auth", "Show a TUI-only one-time code for Telegram admin authentication"],
|
|
549067
549068
|
["/telegram auth cancel", "Cancel the pending Telegram admin authentication code"],
|
|
549068
549069
|
["/telegram bot <username> <text>", "Send a Bot API bot-to-bot message by @username"],
|
|
@@ -558482,6 +558483,8 @@ var init_status_bar = __esm({
|
|
|
558482
558483
|
zones.push({ w: 2, render: () => "" });
|
|
558483
558484
|
const voiceLabel = this._voiceActive ? ` ${this._voiceModelId || "voice"} ` : " voice ";
|
|
558484
558485
|
zones.push({ w: voiceLabel.length + 2, render: () => "" });
|
|
558486
|
+
const telegramLabel = this._telegramStatus.activeSubAgents > 0 ? ` ✈ tg ${this._telegramStatus.activeSubAgents} ` : " ✈ tg ";
|
|
558487
|
+
zones.push({ w: telegramLabel.length + 2, render: () => "" });
|
|
558485
558488
|
zones.push({ w: 9, render: () => "" });
|
|
558486
558489
|
let pages = [];
|
|
558487
558490
|
let cur = [];
|
|
@@ -559608,7 +559611,7 @@ var init_status_bar = __esm({
|
|
|
559608
559611
|
* If currently on the "systems" panel, re-render to show updated agent list. */
|
|
559609
559612
|
renderAgentTabs() {
|
|
559610
559613
|
if (!this.active) return;
|
|
559611
|
-
if (this.currentHeaderPanel
|
|
559614
|
+
if (String(this.currentHeaderPanel).startsWith("sys-")) {
|
|
559612
559615
|
this.refreshHeaderContent();
|
|
559613
559616
|
}
|
|
559614
559617
|
}
|
|
@@ -579313,6 +579316,7 @@ sleep 1
|
|
|
579313
579316
|
case "telegram":
|
|
579314
579317
|
case "tg": {
|
|
579315
579318
|
const parts = arg ? arg.split(/\s+/) : [];
|
|
579319
|
+
const isLocal = hasLocal;
|
|
579316
579320
|
if (parts[0] === "stop" || parts[0] === "off") {
|
|
579317
579321
|
if (ctx3.isTelegramActive?.()) {
|
|
579318
579322
|
ctx3.telegramStop?.();
|
|
@@ -579325,6 +579329,23 @@ sleep 1
|
|
|
579325
579329
|
ctx3.telegramStatus?.();
|
|
579326
579330
|
return "handled";
|
|
579327
579331
|
}
|
|
579332
|
+
if (parts[0] === "mode" || parts[0] === "profile") {
|
|
579333
|
+
const requested = parts.slice(1).find((part) => !part.startsWith("--"));
|
|
579334
|
+
if (!requested) {
|
|
579335
|
+
const current = ctx3.getTelegramSettings?.()?.mode ?? "auto";
|
|
579336
|
+
renderInfo(`Telegram interaction mode: ${c3.bold(current)}`);
|
|
579337
|
+
renderInfo("Modes: auto (chat vs action), chat (fast replies), action (sub-agent tool loop).");
|
|
579338
|
+
return "handled";
|
|
579339
|
+
}
|
|
579340
|
+
if (requested !== "auto" && requested !== "chat" && requested !== "action") {
|
|
579341
|
+
renderWarning("Usage: /telegram mode auto|chat|action [--local]");
|
|
579342
|
+
return "handled";
|
|
579343
|
+
}
|
|
579344
|
+
ctx3.saveTelegramSettings?.({ mode: requested, local: isLocal });
|
|
579345
|
+
ctx3.telegramSetInteractionMode?.(requested);
|
|
579346
|
+
renderInfo(`Telegram interaction mode set to ${c3.bold(requested)}${isLocal ? " (project)" : " (global)"}.`);
|
|
579347
|
+
return "handled";
|
|
579348
|
+
}
|
|
579328
579349
|
if (parts[0] === "auth" || parts[0] === "authenticate") {
|
|
579329
579350
|
if (parts[1] === "cancel") {
|
|
579330
579351
|
const cancelled = ctx3.telegramCancelAdminAuth?.() ?? false;
|
|
@@ -579500,7 +579521,6 @@ sleep 1
|
|
|
579500
579521
|
}
|
|
579501
579522
|
return "handled";
|
|
579502
579523
|
}
|
|
579503
|
-
const isLocal = parts.includes("--local");
|
|
579504
579524
|
const keyIdx = parts.indexOf("--key");
|
|
579505
579525
|
const adminIdx = parts.indexOf("--admin");
|
|
579506
579526
|
if (keyIdx !== -1 || adminIdx !== -1) {
|
|
@@ -579570,6 +579590,7 @@ sleep 1
|
|
|
579570
579590
|
renderInfo(" /telegram Toggle on/off");
|
|
579571
579591
|
renderInfo(" /telegram stop Stop bridge");
|
|
579572
579592
|
renderInfo(" /telegram status Show status");
|
|
579593
|
+
renderInfo(" /telegram mode auto|chat|action Set interaction routing profile");
|
|
579573
579594
|
renderInfo(" /telegram auth Show one-time admin auth code");
|
|
579574
579595
|
renderInfo(" /telegram auth cancel Cancel pending admin auth code");
|
|
579575
579596
|
renderInfo(" /telegram bot <username> <text> Send bot-to-bot message");
|
|
@@ -580072,6 +580093,15 @@ async function showConfigEditor(ctx3) {
|
|
|
580072
580093
|
value: String(merged.telegramAdmin ?? "[not set]"),
|
|
580073
580094
|
detail: String(merged.telegramAdmin ?? "[not set]")
|
|
580074
580095
|
},
|
|
580096
|
+
{
|
|
580097
|
+
key: "telegramMode",
|
|
580098
|
+
label: "telegramMode",
|
|
580099
|
+
kind: "enum",
|
|
580100
|
+
value: String(merged.telegramMode ?? "auto"),
|
|
580101
|
+
detail: String(merged.telegramMode ?? "auto"),
|
|
580102
|
+
options: ["auto", "chat", "action"],
|
|
580103
|
+
settingsKey: "telegramMode"
|
|
580104
|
+
},
|
|
580075
580105
|
// -- Actions --
|
|
580076
580106
|
{
|
|
580077
580107
|
key: "__h_actions__",
|
|
@@ -580337,6 +580367,7 @@ async function showPlatformOnboardingMenu(ctx3, id) {
|
|
|
580337
580367
|
{ key: "telegram-status", label: "Status", detail: "Show bridge status" },
|
|
580338
580368
|
{ key: "telegram-token", label: "Set bot token", detail: "Saved to existing Telegram settings" },
|
|
580339
580369
|
{ key: "telegram-admin", label: "Set admin user id", detail: "Restricts remote admin controls" },
|
|
580370
|
+
{ key: "telegram-mode", label: "Interaction mode", detail: ctx3.getTelegramSettings?.()?.mode ?? "auto" },
|
|
580340
580371
|
{ key: "telegram-start", label: "Start bridge", detail: "Uses saved token" },
|
|
580341
580372
|
{ key: "telegram-stop", label: "Stop bridge", detail: "Disconnect long polling" }
|
|
580342
580373
|
] : [
|
|
@@ -580415,7 +580446,13 @@ async function showPlatformOnboardingMenu(ctx3, id) {
|
|
|
580415
580446
|
if (!result.confirmed || !result.key) return;
|
|
580416
580447
|
if (id === "telegram") {
|
|
580417
580448
|
if (result.key === "telegram-status") ctx3.telegramStatus?.();
|
|
580418
|
-
else if (result.key === "telegram-
|
|
580449
|
+
else if (result.key === "telegram-mode") {
|
|
580450
|
+
const current2 = ctx3.getTelegramSettings?.()?.mode ?? "auto";
|
|
580451
|
+
const next = current2 === "auto" ? "chat" : current2 === "chat" ? "action" : "auto";
|
|
580452
|
+
ctx3.saveTelegramSettings?.({ mode: next });
|
|
580453
|
+
ctx3.telegramSetInteractionMode?.(next);
|
|
580454
|
+
renderInfo(`Telegram interaction mode set to ${next}.`);
|
|
580455
|
+
} else if (result.key === "telegram-start") {
|
|
580419
580456
|
const settings = ctx3.getTelegramSettings?.() ?? {};
|
|
580420
580457
|
if (!settings.key) renderWarning("No Telegram bot token configured.");
|
|
580421
580458
|
else await ctx3.telegramStart?.(settings.key, settings.admin);
|
|
@@ -590857,6 +590894,28 @@ import { mkdirSync as mkdirSync58, existsSync as existsSync100, unlinkSync as un
|
|
|
590857
590894
|
import { join as join117, resolve as resolve35, basename as basename21 } from "node:path";
|
|
590858
590895
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
590859
590896
|
import { createHash as createHash18, randomInt } from "node:crypto";
|
|
590897
|
+
function classifyTelegramInteraction(text, mode = "auto", options2 = {}) {
|
|
590898
|
+
if (mode === "chat") return "chat";
|
|
590899
|
+
if (mode === "action") return "action";
|
|
590900
|
+
const trimmed = text.trim();
|
|
590901
|
+
if (!trimmed) return "chat";
|
|
590902
|
+
if (options2.isSlashCommand || trimmed.startsWith("/")) return "action";
|
|
590903
|
+
if (options2.hasMedia) return "action";
|
|
590904
|
+
const hasAction = TELEGRAM_ACTION_INTENT_RE.test(trimmed);
|
|
590905
|
+
const hasCodebaseContext = TELEGRAM_CODEBASE_CONTEXT_RE.test(trimmed) || TELEGRAM_COMMANDISH_RE.test(trimmed) || /[`'"]?[\w./-]+\.(?:ts|tsx|js|jsx|mjs|cjs|json|md|py|rs|go|java|css|scss|html|yml|yaml|toml|sh|sql)[`'"]?/i.test(trimmed);
|
|
590906
|
+
if (hasAction && hasCodebaseContext) return "action";
|
|
590907
|
+
if (/^(please\s+|pls\s+|can you\s+|could you\s+|we need to\s+|i need you to\s+)?(fix|implement|patch|edit|refactor|run|test|build|install|debug|commit|deploy|publish)\b/i.test(trimmed)) {
|
|
590908
|
+
return "action";
|
|
590909
|
+
}
|
|
590910
|
+
if (TELEGRAM_COMMANDISH_RE.test(trimmed)) return "action";
|
|
590911
|
+
if (trimmed.includes("```") && hasAction) return "action";
|
|
590912
|
+
if (trimmed.length > 360 && hasAction) return "action";
|
|
590913
|
+
if (/\b(write|draft)\b.*\b(poem|story|joke|email|message|caption|song|haiku)\b/i.test(trimmed)) return "chat";
|
|
590914
|
+
if (TELEGRAM_CHAT_INTENT_RE.test(trimmed)) return "chat";
|
|
590915
|
+
if (/[??]\s*$/.test(trimmed) && !hasAction) return "chat";
|
|
590916
|
+
if (trimmed.length <= 180 && !hasCodebaseContext && !hasAction) return "chat";
|
|
590917
|
+
return hasAction ? "action" : "chat";
|
|
590918
|
+
}
|
|
590860
590919
|
function convertMarkdownToTelegramHTML(md) {
|
|
590861
590920
|
let html = md;
|
|
590862
590921
|
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -591160,11 +591219,13 @@ function mimeForPath(path11, fallbackKind) {
|
|
|
591160
591219
|
if (fallbackKind === "video") return "video/mp4";
|
|
591161
591220
|
return "application/octet-stream";
|
|
591162
591221
|
}
|
|
591163
|
-
function renderTelegramStart(botUsername, adminId) {
|
|
591222
|
+
function renderTelegramStart(botUsername, adminId, mode = "auto") {
|
|
591164
591223
|
process.stdout.write(`
|
|
591165
591224
|
${c3.cyan("✈")} ${c3.bold("Telegram Bridge")} connected as @${botUsername}
|
|
591166
591225
|
`);
|
|
591167
|
-
process.stdout.write(` ${c3.dim(
|
|
591226
|
+
process.stdout.write(` ${c3.dim(`Interaction mode: ${mode}`)}
|
|
591227
|
+
`);
|
|
591228
|
+
process.stdout.write(` ${c3.dim("Auto mode uses quick chat for conversational turns and sub-agents for action requests")}
|
|
591168
591229
|
`);
|
|
591169
591230
|
if (adminId) {
|
|
591170
591231
|
process.stdout.write(` ${c3.dim(`Admin: ${adminId} (full memory + tools)`)}
|
|
@@ -591178,17 +591239,19 @@ function renderTelegramStart(botUsername, adminId) {
|
|
|
591178
591239
|
|
|
591179
591240
|
`);
|
|
591180
591241
|
}
|
|
591181
|
-
function renderTelegramStatus(active, botUsername, adminId, activeSubAgents) {
|
|
591242
|
+
function renderTelegramStatus(active, botUsername, adminId, activeSubAgents, mode = "auto") {
|
|
591182
591243
|
if (active) {
|
|
591183
591244
|
process.stdout.write(`
|
|
591184
591245
|
${c3.green("●")} Telegram bridge: ${c3.bold("ACTIVE")} (@${botUsername ?? "?"})
|
|
591246
|
+
`);
|
|
591247
|
+
process.stdout.write(` Mode: ${mode}
|
|
591185
591248
|
`);
|
|
591186
591249
|
if (adminId) {
|
|
591187
591250
|
process.stdout.write(` Admin: ${adminId}
|
|
591188
591251
|
`);
|
|
591189
591252
|
}
|
|
591190
591253
|
if (activeSubAgents && activeSubAgents > 0) {
|
|
591191
|
-
process.stdout.write(` Active
|
|
591254
|
+
process.stdout.write(` Active Telegram work: ${activeSubAgents}
|
|
591192
591255
|
`);
|
|
591193
591256
|
}
|
|
591194
591257
|
process.stdout.write(` ${c3.dim("Use /telegram to toggle off")}
|
|
@@ -591246,7 +591309,7 @@ function renderTelegramSubAgentError(username, error) {
|
|
|
591246
591309
|
process.stdout.write(` ${c3.dim("⎿")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
|
|
591247
591310
|
`);
|
|
591248
591311
|
}
|
|
591249
|
-
var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, GROUP_REPLY_DISCRETION_PROMPT, MEDIA_CACHE_TTL_MS, TelegramBridge;
|
|
591312
|
+
var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, TELEGRAM_ACTION_INTENT_RE, TELEGRAM_CODEBASE_CONTEXT_RE, TELEGRAM_COMMANDISH_RE, TELEGRAM_CHAT_INTENT_RE, MEDIA_CACHE_TTL_MS, TelegramBridge;
|
|
591250
591313
|
var init_telegram_bridge = __esm({
|
|
591251
591314
|
"packages/cli/src/tui/telegram-bridge.ts"() {
|
|
591252
591315
|
"use strict";
|
|
@@ -591306,6 +591369,20 @@ REPLY DISCRETION: You are in a group chat. Only respond if:
|
|
|
591306
591369
|
If the message is casual group chatter not directed at you, use task_complete
|
|
591307
591370
|
with summary "no_reply" to silently skip without responding.
|
|
591308
591371
|
`.trim();
|
|
591372
|
+
TELEGRAM_CHAT_MODE_PROMPT = `
|
|
591373
|
+
You are Open Agents replying in Telegram quick-chat mode.
|
|
591374
|
+
|
|
591375
|
+
Rules:
|
|
591376
|
+
1. Reply directly to the Telegram user, conversationally and concisely.
|
|
591377
|
+
2. Do not inspect, summarize, or expose local files, paths, secrets, tool output, or runtime internals.
|
|
591378
|
+
3. Do not claim that you changed code, ran commands, or checked the workspace in quick-chat mode.
|
|
591379
|
+
4. If the user asks for codebase action while quick-chat mode is forced, tell them to switch Telegram to action mode or send a concrete action request in auto mode.
|
|
591380
|
+
5. For ordinary chat, status questions, greetings, quick explanations, and playful messages, answer immediately without tool-use narration.
|
|
591381
|
+
`.trim();
|
|
591382
|
+
TELEGRAM_ACTION_INTENT_RE = /\b(implement|fix|patch|edit|write|create|delete|remove|refactor|run|execute|test|build|install|debug|investigate|diagnose|validate|commit|push|pull|merge|rebase|deploy|publish|read|open|inspect|grep|search|find|list|apply|change|update)\b/i;
|
|
591383
|
+
TELEGRAM_CODEBASE_CONTEXT_RE = /\b(repo|repository|codebase|workspace|working directory|file|files|folder|directory|src|source|test|tests|package|pnpm|npm|node|git|branch|commit|pr|pull request|issue|shell|terminal|cli|command|function|class|component|endpoint|api)\b/i;
|
|
591384
|
+
TELEGRAM_COMMANDISH_RE = /(^|\s)(pnpm|npm|node|git|rg|grep|sed|cat|ls|cd|mkdir|rm|mv|cp|curl|docker|pytest|vitest|tsc)\b/i;
|
|
591385
|
+
TELEGRAM_CHAT_INTENT_RE = /\b(hi|hello|hey|thanks|thank you|lol|haha|joke|how are you|what's up|whats up|can you hear|are you there|explain|what is|what are|why|how does|tell me|opinion|quick question)\b/i;
|
|
591309
591386
|
MEDIA_CACHE_TTL_MS = 30 * 60 * 1e3;
|
|
591310
591387
|
TelegramBridge = class {
|
|
591311
591388
|
constructor(botToken, onMessage, agentConfig, repoRoot, toolPolicyConfig) {
|
|
@@ -591327,6 +591404,7 @@ with summary "no_reply" to silently skip without responding.
|
|
|
591327
591404
|
active: false,
|
|
591328
591405
|
botUsername: "",
|
|
591329
591406
|
supportsGuestQueries: false,
|
|
591407
|
+
interactionMode: "auto",
|
|
591330
591408
|
startedAt: "",
|
|
591331
591409
|
messagesReceived: 0,
|
|
591332
591410
|
messagesSent: 0,
|
|
@@ -591336,6 +591414,12 @@ with summary "no_reply" to silently skip without responding.
|
|
|
591336
591414
|
adminUserId = null;
|
|
591337
591415
|
/** Active sub-agents by chat/guest session key */
|
|
591338
591416
|
subAgents = /* @__PURE__ */ new Map();
|
|
591417
|
+
/** Active direct chat completions, counted with Telegram activity in the TUI */
|
|
591418
|
+
activeChatViews = /* @__PURE__ */ new Set();
|
|
591419
|
+
/** Lightweight chat history by chat/guest session key */
|
|
591420
|
+
chatHistory = /* @__PURE__ */ new Map();
|
|
591421
|
+
/** Telegram interaction routing profile */
|
|
591422
|
+
interactionMode = "auto";
|
|
591339
591423
|
/** Event handler for forwarding sub-agent events to parent TUI */
|
|
591340
591424
|
onSubAgentEvent = null;
|
|
591341
591425
|
/** Tool policy config — user overrides from config */
|
|
@@ -591375,6 +591459,13 @@ with summary "no_reply" to silently skip without responding.
|
|
|
591375
591459
|
setAdmin(userId) {
|
|
591376
591460
|
this.adminUserId = userId;
|
|
591377
591461
|
}
|
|
591462
|
+
setInteractionMode(mode) {
|
|
591463
|
+
this.interactionMode = mode;
|
|
591464
|
+
this.state.interactionMode = mode;
|
|
591465
|
+
}
|
|
591466
|
+
getInteractionMode() {
|
|
591467
|
+
return this.interactionMode;
|
|
591468
|
+
}
|
|
591378
591469
|
/** Update tool policy config at runtime (e.g., from /disable command) */
|
|
591379
591470
|
setToolPolicyConfig(config) {
|
|
591380
591471
|
this.toolPolicyConfig = config;
|
|
@@ -591418,7 +591509,7 @@ with summary "no_reply" to silently skip without responding.
|
|
|
591418
591509
|
return this.polling;
|
|
591419
591510
|
}
|
|
591420
591511
|
get stats() {
|
|
591421
|
-
return { ...this.state, activeSubAgents: this.
|
|
591512
|
+
return { ...this.state, activeSubAgents: this.activeTelegramInteractionCount() };
|
|
591422
591513
|
}
|
|
591423
591514
|
get botUsername() {
|
|
591424
591515
|
return this.state.botUsername;
|
|
@@ -591476,9 +591567,28 @@ with summary "no_reply" to silently skip without responding.
|
|
|
591476
591567
|
if (msg.guestQueryId) return `guest:${msg.guestQueryId}`;
|
|
591477
591568
|
return `chat:${String(msg.chatId)}`;
|
|
591478
591569
|
}
|
|
591570
|
+
activeTelegramInteractionCount() {
|
|
591571
|
+
return this.subAgents.size + this.activeChatViews.size;
|
|
591572
|
+
}
|
|
591573
|
+
refreshActiveTelegramInteractionCount() {
|
|
591574
|
+
this.state.activeSubAgents = this.activeTelegramInteractionCount();
|
|
591575
|
+
}
|
|
591479
591576
|
canUseChatActions(msg) {
|
|
591480
591577
|
return !msg.guestQueryId && (typeof msg.chatId === "number" || String(msg.chatId).startsWith("@"));
|
|
591481
591578
|
}
|
|
591579
|
+
recordChatHistory(sessionKey, entry) {
|
|
591580
|
+
const existing = this.chatHistory.get(sessionKey) ?? [];
|
|
591581
|
+
existing.push(entry);
|
|
591582
|
+
if (existing.length > 16) existing.splice(0, existing.length - 16);
|
|
591583
|
+
this.chatHistory.set(sessionKey, existing);
|
|
591584
|
+
}
|
|
591585
|
+
shouldFastChatReplyInGroup(msg) {
|
|
591586
|
+
if (msg.chatType === "private" || msg.guestQueryId) return true;
|
|
591587
|
+
const lower = msg.text.toLowerCase();
|
|
591588
|
+
const botMention = this.state.botUsername ? lower.includes(`@${this.state.botUsername.toLowerCase()}`) : false;
|
|
591589
|
+
if (botMention || msg.replyToMessageId) return true;
|
|
591590
|
+
return /[??]/.test(msg.text) || /\b(help|question|how|what|why|when|where|can you|could you|explain)\b/i.test(msg.text);
|
|
591591
|
+
}
|
|
591482
591592
|
async handleAdminAuthCommand(msg) {
|
|
591483
591593
|
const normalized = msg.text.trim();
|
|
591484
591594
|
const lower = normalized.toLowerCase();
|
|
@@ -591542,6 +591652,7 @@ with summary "no_reply" to silently skip without responding.
|
|
|
591542
591652
|
active: true,
|
|
591543
591653
|
botUsername: me.result?.username ?? "unknown",
|
|
591544
591654
|
supportsGuestQueries: Boolean(me.result?.supports_guest_queries),
|
|
591655
|
+
interactionMode: this.interactionMode,
|
|
591545
591656
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
591546
591657
|
messagesReceived: 0,
|
|
591547
591658
|
messagesSent: 0,
|
|
@@ -591571,6 +591682,8 @@ with summary "no_reply" to silently skip without responding.
|
|
|
591571
591682
|
if (agent.typingInterval) clearInterval(agent.typingInterval);
|
|
591572
591683
|
}
|
|
591573
591684
|
this.subAgents.clear();
|
|
591685
|
+
this.activeChatViews.clear();
|
|
591686
|
+
this.refreshActiveTelegramInteractionCount();
|
|
591574
591687
|
}
|
|
591575
591688
|
// ── Typing indicator ──────────────────────────────────────────────────
|
|
591576
591689
|
/** Start sending "typing" indicator every 4 seconds */
|
|
@@ -591761,6 +591874,14 @@ Join: ${newUrl}`);
|
|
|
591761
591874
|
}
|
|
591762
591875
|
return;
|
|
591763
591876
|
}
|
|
591877
|
+
const route = classifyTelegramInteraction(msg.text, this.interactionMode, {
|
|
591878
|
+
hasMedia: !!msg.media || !!msg.poll || !!msg.livePhoto,
|
|
591879
|
+
isSlashCommand: msg.text.trim().startsWith("/")
|
|
591880
|
+
});
|
|
591881
|
+
if (route === "chat") {
|
|
591882
|
+
await this.handleTelegramChatCompletion(msg, toolContext);
|
|
591883
|
+
return;
|
|
591884
|
+
}
|
|
591764
591885
|
const subAgent = {
|
|
591765
591886
|
chatId: msg.chatId,
|
|
591766
591887
|
username: msg.username,
|
|
@@ -591776,7 +591897,7 @@ Join: ${newUrl}`);
|
|
|
591776
591897
|
pendingMessages: []
|
|
591777
591898
|
};
|
|
591778
591899
|
this.subAgents.set(sessionKey, subAgent);
|
|
591779
|
-
this.
|
|
591900
|
+
this.refreshActiveTelegramInteractionCount();
|
|
591780
591901
|
this.subAgentViewCallbacks?.onRegister(
|
|
591781
591902
|
subAgent.viewId,
|
|
591782
591903
|
`✈ @${msg.username || "telegram"}`,
|
|
@@ -591846,10 +591967,173 @@ Join: ${newUrl}`);
|
|
|
591846
591967
|
}
|
|
591847
591968
|
} finally {
|
|
591848
591969
|
this.subAgents.delete(sessionKey);
|
|
591849
|
-
this.
|
|
591970
|
+
this.refreshActiveTelegramInteractionCount();
|
|
591850
591971
|
this.subAgentViewCallbacks?.onComplete(subAgent.viewId);
|
|
591851
591972
|
}
|
|
591852
591973
|
}
|
|
591974
|
+
/** Fast Telegram chat path: direct streamed completion, no tool loop. */
|
|
591975
|
+
async handleTelegramChatCompletion(msg, toolContext) {
|
|
591976
|
+
if (!this.shouldFastChatReplyInGroup(msg)) {
|
|
591977
|
+
this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, "chat-mode discretion: skipped group chatter"));
|
|
591978
|
+
return;
|
|
591979
|
+
}
|
|
591980
|
+
const sessionKey = this.sessionKeyForMessage(msg);
|
|
591981
|
+
const viewId = `${this.viewIdForMessage(msg)}-chat`;
|
|
591982
|
+
let typingInterval = null;
|
|
591983
|
+
let liveMessageId = null;
|
|
591984
|
+
let accumulated = "";
|
|
591985
|
+
let lastEditMs = 0;
|
|
591986
|
+
let lastViewWriteMs = 0;
|
|
591987
|
+
const progressLines = [`💬 Quick chat mode (${this.interactionMode})`];
|
|
591988
|
+
this.activeChatViews.add(viewId);
|
|
591989
|
+
this.refreshActiveTelegramInteractionCount();
|
|
591990
|
+
this.subAgentViewCallbacks?.onRegister(
|
|
591991
|
+
viewId,
|
|
591992
|
+
`✈ @${msg.username || "telegram"}`,
|
|
591993
|
+
`Telegram quick chat: ${msg.text.slice(0, 160)}`
|
|
591994
|
+
);
|
|
591995
|
+
this.subAgentViewCallbacks?.onWrite(viewId, `✈ Telegram quick chat from @${msg.username}: ${msg.text}`);
|
|
591996
|
+
this.subAgentViewCallbacks?.onWrite(viewId, `route: chat (${this.interactionMode})`);
|
|
591997
|
+
this.subAgentViewCallbacks?.onStatus(viewId, "running");
|
|
591998
|
+
if (this.canUseChatActions(msg)) {
|
|
591999
|
+
typingInterval = this.startTypingIndicator(msg.chatId);
|
|
592000
|
+
}
|
|
592001
|
+
this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, `chat-mode fast reply (${this.interactionMode})`));
|
|
592002
|
+
try {
|
|
592003
|
+
if (!msg.guestQueryId) {
|
|
592004
|
+
liveMessageId = await this.sendLiveMessage(
|
|
592005
|
+
msg.chatId,
|
|
592006
|
+
renderTelegramLiveProgressHTML(progressLines, ""),
|
|
592007
|
+
msg.chatType !== "private" ? msg.messageId : void 0
|
|
592008
|
+
);
|
|
592009
|
+
}
|
|
592010
|
+
const mediaContext = msg.media || msg.livePhoto ? "Attachment received. Quick-chat mode does not inspect media; use action mode for media analysis." : "";
|
|
592011
|
+
const finalText = await this.runTelegramChatCompletion(
|
|
592012
|
+
msg,
|
|
592013
|
+
toolContext,
|
|
592014
|
+
mediaContext,
|
|
592015
|
+
(nextText) => {
|
|
592016
|
+
accumulated = nextText;
|
|
592017
|
+
const now = Date.now();
|
|
592018
|
+
if (now - lastViewWriteMs > 900) {
|
|
592019
|
+
lastViewWriteMs = now;
|
|
592020
|
+
this.subAgentViewCallbacks?.onWrite(viewId, `stream: ${sanitizeTelegramProgressText(nextText, 180)}`);
|
|
592021
|
+
}
|
|
592022
|
+
if (liveMessageId && !msg.guestQueryId && now - lastEditMs > 900) {
|
|
592023
|
+
lastEditMs = now;
|
|
592024
|
+
this.editLiveMessage(
|
|
592025
|
+
msg.chatId,
|
|
592026
|
+
liveMessageId,
|
|
592027
|
+
renderTelegramLiveProgressHTML(progressLines, accumulated)
|
|
592028
|
+
).catch(() => {
|
|
592029
|
+
});
|
|
592030
|
+
}
|
|
592031
|
+
}
|
|
592032
|
+
);
|
|
592033
|
+
if (typingInterval) {
|
|
592034
|
+
clearInterval(typingInterval);
|
|
592035
|
+
typingInterval = null;
|
|
592036
|
+
}
|
|
592037
|
+
const cleaned = stripTelegramHiddenThinking(finalText || accumulated).trim() || "I heard you.";
|
|
592038
|
+
this.recordChatHistory(sessionKey, { role: "user", text: msg.text });
|
|
592039
|
+
this.recordChatHistory(sessionKey, { role: "assistant", text: cleaned });
|
|
592040
|
+
const finalHtml = convertMarkdownToTelegramHTML(cleaned);
|
|
592041
|
+
if (liveMessageId && !msg.guestQueryId) {
|
|
592042
|
+
await this.editLiveMessage(msg.chatId, liveMessageId, finalHtml);
|
|
592043
|
+
} else {
|
|
592044
|
+
await this.replyToTelegramMessage(msg, finalHtml, {
|
|
592045
|
+
html: true,
|
|
592046
|
+
replyToMessageId: msg.chatType !== "private" ? msg.messageId : void 0
|
|
592047
|
+
});
|
|
592048
|
+
}
|
|
592049
|
+
this.subAgentViewCallbacks?.onWrite(viewId, `completed: ${cleaned}`);
|
|
592050
|
+
this.subAgentViewCallbacks?.onStatus(viewId, "completed");
|
|
592051
|
+
} catch (err) {
|
|
592052
|
+
if (typingInterval) {
|
|
592053
|
+
clearInterval(typingInterval);
|
|
592054
|
+
typingInterval = null;
|
|
592055
|
+
}
|
|
592056
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
592057
|
+
this.tuiWrite(() => renderTelegramSubAgentError(msg.username, errMsg));
|
|
592058
|
+
this.subAgentViewCallbacks?.onWrite(viewId, `error: ${errMsg}`);
|
|
592059
|
+
this.subAgentViewCallbacks?.onStatus(viewId, "failed");
|
|
592060
|
+
if (liveMessageId && !msg.guestQueryId) {
|
|
592061
|
+
await this.editLiveMessage(msg.chatId, liveMessageId, `❌ Error: ${escapeTelegramHTML(errMsg)}`).catch(() => {
|
|
592062
|
+
});
|
|
592063
|
+
} else {
|
|
592064
|
+
await this.replyToTelegramMessage(msg, "Sorry, I couldn't process that quick chat message.").catch(() => {
|
|
592065
|
+
});
|
|
592066
|
+
}
|
|
592067
|
+
} finally {
|
|
592068
|
+
this.activeChatViews.delete(viewId);
|
|
592069
|
+
this.refreshActiveTelegramInteractionCount();
|
|
592070
|
+
this.subAgentViewCallbacks?.onComplete(viewId);
|
|
592071
|
+
}
|
|
592072
|
+
}
|
|
592073
|
+
buildTelegramChatMessages(msg, toolContext, mediaContext = "") {
|
|
592074
|
+
const isAdminDM = toolContext === "telegram-admin-dm";
|
|
592075
|
+
const isAdminGroup = toolContext === "telegram-admin-group";
|
|
592076
|
+
const isGroup = msg.chatType !== "private";
|
|
592077
|
+
const sessionKey = this.sessionKeyForMessage(msg);
|
|
592078
|
+
const history = this.chatHistory.get(sessionKey) ?? [];
|
|
592079
|
+
const safety = isAdminDM ? "Admin private DM. The user is trusted, but quick-chat mode still has no tool access." : isAdminGroup ? ADMIN_GROUP_PROMPT : TELEGRAM_SAFETY_PROMPT;
|
|
592080
|
+
const groupHint = isGroup ? `Telegram group: ${msg.chatTitle || "unknown"}. Keep the reply short and relevant.` : "Telegram private chat.";
|
|
592081
|
+
const messages2 = [
|
|
592082
|
+
{
|
|
592083
|
+
role: "system",
|
|
592084
|
+
content: `${TELEGRAM_CHAT_MODE_PROMPT}
|
|
592085
|
+
|
|
592086
|
+
${safety}
|
|
592087
|
+
|
|
592088
|
+
${groupHint}`
|
|
592089
|
+
},
|
|
592090
|
+
...history.slice(-8).map((entry) => ({
|
|
592091
|
+
role: entry.role,
|
|
592092
|
+
content: entry.text
|
|
592093
|
+
}))
|
|
592094
|
+
];
|
|
592095
|
+
const chatLabel = isGroup ? ` in group "${msg.chatTitle || "unknown"}"` : "";
|
|
592096
|
+
messages2.push({
|
|
592097
|
+
role: "user",
|
|
592098
|
+
content: `Telegram message from @${msg.username}${chatLabel}:
|
|
592099
|
+
${msg.text}${mediaContext ? `
|
|
592100
|
+
|
|
592101
|
+
${mediaContext}` : ""}`
|
|
592102
|
+
});
|
|
592103
|
+
return messages2;
|
|
592104
|
+
}
|
|
592105
|
+
async runTelegramChatCompletion(msg, toolContext, mediaContext, onToken) {
|
|
592106
|
+
const config = this.agentConfig;
|
|
592107
|
+
const backend = new OllamaAgenticBackend(
|
|
592108
|
+
config.backendUrl,
|
|
592109
|
+
config.model,
|
|
592110
|
+
config.apiKey
|
|
592111
|
+
);
|
|
592112
|
+
const request = {
|
|
592113
|
+
messages: this.buildTelegramChatMessages(msg, toolContext, mediaContext),
|
|
592114
|
+
tools: [],
|
|
592115
|
+
temperature: 0.4,
|
|
592116
|
+
maxTokens: 700,
|
|
592117
|
+
timeoutMs: Math.min(config.timeoutMs ?? 3e4, 3e4),
|
|
592118
|
+
think: false
|
|
592119
|
+
};
|
|
592120
|
+
let accumulated = "";
|
|
592121
|
+
const streamable = backend;
|
|
592122
|
+
if (typeof streamable.chatCompletionStream === "function") {
|
|
592123
|
+
for await (const chunk of streamable.chatCompletionStream(request)) {
|
|
592124
|
+
if (chunk.type === "content" && !chunk.thinking && chunk.content) {
|
|
592125
|
+
accumulated += chunk.content;
|
|
592126
|
+
onToken(accumulated);
|
|
592127
|
+
}
|
|
592128
|
+
}
|
|
592129
|
+
}
|
|
592130
|
+
if (!accumulated.trim()) {
|
|
592131
|
+
const result = await backend.chatCompletion(request);
|
|
592132
|
+
accumulated = result.choices[0]?.message?.content ?? "";
|
|
592133
|
+
if (accumulated) onToken(accumulated);
|
|
592134
|
+
}
|
|
592135
|
+
return stripTelegramHiddenThinking(accumulated).trim();
|
|
592136
|
+
}
|
|
591853
592137
|
/** Run a sub-agent for a Telegram message */
|
|
591854
592138
|
async runSubAgent(msg, subAgent, mediaContext = "") {
|
|
591855
592139
|
const config = this.agentConfig;
|
|
@@ -622073,6 +622357,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
622073
622357
|
currentConfig,
|
|
622074
622358
|
repoRoot
|
|
622075
622359
|
);
|
|
622360
|
+
telegramBridge.setInteractionMode(savedSettings.telegramMode ?? "auto");
|
|
622076
622361
|
if (adminId) {
|
|
622077
622362
|
telegramBridge.setAdmin(adminId);
|
|
622078
622363
|
}
|
|
@@ -622185,7 +622470,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
622185
622470
|
);
|
|
622186
622471
|
});
|
|
622187
622472
|
writeContent(
|
|
622188
|
-
() => renderTelegramStart(telegramBridge.botUsername, adminId)
|
|
622473
|
+
() => renderTelegramStart(telegramBridge.botUsername, adminId, savedSettings.telegramMode ?? "auto")
|
|
622189
622474
|
);
|
|
622190
622475
|
showPrompt();
|
|
622191
622476
|
},
|
|
@@ -622204,7 +622489,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
622204
622489
|
getTelegramSettings() {
|
|
622205
622490
|
return {
|
|
622206
622491
|
key: savedSettings.telegramKey,
|
|
622207
|
-
admin: savedSettings.telegramAdmin
|
|
622492
|
+
admin: savedSettings.telegramAdmin,
|
|
622493
|
+
mode: savedSettings.telegramMode
|
|
622208
622494
|
};
|
|
622209
622495
|
},
|
|
622210
622496
|
saveTelegramSettings(settings) {
|
|
@@ -622214,9 +622500,13 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
622214
622500
|
if (settings.admin !== void 0) {
|
|
622215
622501
|
savedSettings.telegramAdmin = settings.admin;
|
|
622216
622502
|
}
|
|
622503
|
+
if (settings.mode !== void 0) {
|
|
622504
|
+
savedSettings.telegramMode = settings.mode;
|
|
622505
|
+
}
|
|
622217
622506
|
const payload = {
|
|
622218
622507
|
...settings.key !== void 0 ? { telegramKey: settings.key } : {},
|
|
622219
|
-
...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {}
|
|
622508
|
+
...settings.admin !== void 0 ? { telegramAdmin: settings.admin } : {},
|
|
622509
|
+
...settings.mode !== void 0 ? { telegramMode: settings.mode } : {}
|
|
622220
622510
|
};
|
|
622221
622511
|
if (settings.local) {
|
|
622222
622512
|
saveProjectSettings(repoRoot, payload);
|
|
@@ -622224,6 +622514,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
622224
622514
|
saveGlobalSettings(payload);
|
|
622225
622515
|
}
|
|
622226
622516
|
},
|
|
622517
|
+
telegramSetInteractionMode(mode) {
|
|
622518
|
+
savedSettings.telegramMode = mode;
|
|
622519
|
+
telegramBridge?.setInteractionMode(mode);
|
|
622520
|
+
},
|
|
622227
622521
|
telegramStatus() {
|
|
622228
622522
|
const active = telegramBridge?.isActive ?? false;
|
|
622229
622523
|
const botUser = active ? telegramBridge?.botUsername : void 0;
|
|
@@ -622233,7 +622527,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
622233
622527
|
active,
|
|
622234
622528
|
botUser,
|
|
622235
622529
|
savedSettings.telegramAdmin,
|
|
622236
|
-
subAgents
|
|
622530
|
+
subAgents,
|
|
622531
|
+
savedSettings.telegramMode ?? "auto"
|
|
622237
622532
|
)
|
|
622238
622533
|
);
|
|
622239
622534
|
},
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.187.
|
|
3
|
+
"version": "0.187.584",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "open-agents-ai",
|
|
9
|
-
"version": "0.187.
|
|
9
|
+
"version": "0.187.584",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "CC-BY-NC-4.0",
|
|
12
12
|
"dependencies": {
|
package/package.json
CHANGED