@openacp/cli 0.2.23 → 0.2.25
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/dist/autostart-YBYXQA77.js +18 -0
- package/dist/autostart-YBYXQA77.js.map +1 -0
- package/dist/{setup-XQBEZZQB.js → chunk-4BN7NSKB.js} +140 -9
- package/dist/chunk-4BN7NSKB.js.map +1 -0
- package/dist/chunk-CQMS5U7Z.js +63 -0
- package/dist/chunk-CQMS5U7Z.js.map +1 -0
- package/dist/{chunk-XOVJLTEC.js → chunk-FGXG3H3F.js} +14 -58
- package/dist/chunk-FGXG3H3F.js.map +1 -0
- package/dist/{chunk-ZATQZUJT.js → chunk-MNJDYDGH.js} +9 -1
- package/dist/{chunk-ZATQZUJT.js.map → chunk-MNJDYDGH.js.map} +1 -1
- package/dist/chunk-PQRVTUNH.js +145 -0
- package/dist/chunk-PQRVTUNH.js.map +1 -0
- package/dist/chunk-QWUJIKTX.js +527 -0
- package/dist/chunk-QWUJIKTX.js.map +1 -0
- package/dist/{chunk-EIBLQU3H.js → chunk-S5MPFOR3.js} +632 -136
- package/dist/chunk-S5MPFOR3.js.map +1 -0
- package/dist/chunk-S6O7SM6A.js +129 -0
- package/dist/chunk-S6O7SM6A.js.map +1 -0
- package/dist/chunk-WXS6ONOD.js +103 -0
- package/dist/chunk-WXS6ONOD.js.map +1 -0
- package/dist/cli.js +354 -4
- package/dist/cli.js.map +1 -1
- package/dist/config-2XALNLAA.js +14 -0
- package/dist/config-2XALNLAA.js.map +1 -0
- package/dist/config-editor-56B6YU7B.js +11 -0
- package/dist/config-editor-56B6YU7B.js.map +1 -0
- package/dist/daemon-3E5OMLT3.js +29 -0
- package/dist/daemon-3E5OMLT3.js.map +1 -0
- package/dist/index.d.ts +99 -18
- package/dist/index.js +35 -6
- package/dist/install-cloudflared-57NRTI4E.js +8 -0
- package/dist/install-cloudflared-57NRTI4E.js.map +1 -0
- package/dist/{main-VJUX7RUY.js → main-TGYT34IJ.js} +43 -11
- package/dist/main-TGYT34IJ.js.map +1 -0
- package/dist/setup-FTNJACSC.js +27 -0
- package/dist/setup-FTNJACSC.js.map +1 -0
- package/dist/{tunnel-service-I6NUMBT4.js → tunnel-service-I6WM6USB.js} +10 -10
- package/dist/tunnel-service-I6WM6USB.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-EIBLQU3H.js.map +0 -1
- package/dist/chunk-XOVJLTEC.js.map +0 -1
- package/dist/main-VJUX7RUY.js.map +0 -1
- package/dist/setup-XQBEZZQB.js.map +0 -1
- package/dist/tunnel-service-I6NUMBT4.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createChildLogger,
|
|
3
3
|
createSessionLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MNJDYDGH.js";
|
|
5
5
|
|
|
6
6
|
// src/core/streams.ts
|
|
7
7
|
function nodeToWebWritable(nodeStream) {
|
|
@@ -54,33 +54,35 @@ import { randomUUID } from "crypto";
|
|
|
54
54
|
import { ClientSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
|
|
55
55
|
var log = createChildLogger({ module: "agent-instance" });
|
|
56
56
|
function resolveAgentCommand(cmd) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"index.js"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
57
|
+
const searchRoots = [process.cwd()];
|
|
58
|
+
const ownDir = path.resolve(import.meta.dirname, "..", "..");
|
|
59
|
+
if (ownDir !== process.cwd()) {
|
|
60
|
+
searchRoots.push(ownDir);
|
|
61
|
+
}
|
|
62
|
+
for (const root of searchRoots) {
|
|
63
|
+
const packageDirs = [
|
|
64
|
+
path.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
|
|
65
|
+
path.resolve(root, "node_modules", cmd, "dist", "index.js")
|
|
66
|
+
];
|
|
67
|
+
for (const jsPath of packageDirs) {
|
|
68
|
+
if (fs.existsSync(jsPath)) {
|
|
69
|
+
return { command: process.execPath, args: [jsPath] };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const root of searchRoots) {
|
|
74
|
+
const localBin = path.resolve(root, "node_modules", ".bin", cmd);
|
|
75
|
+
if (fs.existsSync(localBin)) {
|
|
76
|
+
const content = fs.readFileSync(localBin, "utf-8");
|
|
77
|
+
if (content.startsWith("#!/usr/bin/env node")) {
|
|
78
|
+
return { command: process.execPath, args: [localBin] };
|
|
79
|
+
}
|
|
80
|
+
const match = content.match(/"([^"]+\.js)"/);
|
|
81
|
+
if (match) {
|
|
82
|
+
const target = path.resolve(path.dirname(localBin), match[1]);
|
|
83
|
+
if (fs.existsSync(target)) {
|
|
84
|
+
return { command: process.execPath, args: [target] };
|
|
85
|
+
}
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
}
|
|
@@ -580,6 +582,7 @@ var Session = class {
|
|
|
580
582
|
try {
|
|
581
583
|
const start = Date.now();
|
|
582
584
|
await this.agentInstance.prompt('Reply with only "ready".');
|
|
585
|
+
this.status = "active";
|
|
583
586
|
this.log.info({ durationMs: Date.now() - start }, "Warm-up complete");
|
|
584
587
|
} catch (err) {
|
|
585
588
|
this.log.error({ err }, "Warm-up failed");
|
|
@@ -1259,6 +1262,187 @@ var ChannelAdapter = class {
|
|
|
1259
1262
|
}
|
|
1260
1263
|
};
|
|
1261
1264
|
|
|
1265
|
+
// src/core/api-server.ts
|
|
1266
|
+
import * as http from "http";
|
|
1267
|
+
import * as fs3 from "fs";
|
|
1268
|
+
import * as path4 from "path";
|
|
1269
|
+
import * as os2 from "os";
|
|
1270
|
+
var log4 = createChildLogger({ module: "api-server" });
|
|
1271
|
+
var DEFAULT_PORT_FILE = path4.join(os2.homedir(), ".openacp", "api.port");
|
|
1272
|
+
var ApiServer = class {
|
|
1273
|
+
constructor(core, config, portFilePath) {
|
|
1274
|
+
this.core = core;
|
|
1275
|
+
this.config = config;
|
|
1276
|
+
this.portFilePath = portFilePath ?? DEFAULT_PORT_FILE;
|
|
1277
|
+
}
|
|
1278
|
+
server = null;
|
|
1279
|
+
actualPort = 0;
|
|
1280
|
+
portFilePath;
|
|
1281
|
+
async start() {
|
|
1282
|
+
this.server = http.createServer((req, res) => this.handleRequest(req, res));
|
|
1283
|
+
await new Promise((resolve, reject) => {
|
|
1284
|
+
this.server.on("error", (err) => {
|
|
1285
|
+
if (err.code === "EADDRINUSE") {
|
|
1286
|
+
log4.warn({ port: this.config.port }, "API port in use, continuing without API server");
|
|
1287
|
+
this.server = null;
|
|
1288
|
+
resolve();
|
|
1289
|
+
} else {
|
|
1290
|
+
reject(err);
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
this.server.listen(this.config.port, this.config.host, () => {
|
|
1294
|
+
const addr = this.server.address();
|
|
1295
|
+
if (addr && typeof addr === "object") {
|
|
1296
|
+
this.actualPort = addr.port;
|
|
1297
|
+
}
|
|
1298
|
+
this.writePortFile();
|
|
1299
|
+
log4.info({ host: this.config.host, port: this.actualPort }, "API server listening");
|
|
1300
|
+
resolve();
|
|
1301
|
+
});
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
async stop() {
|
|
1305
|
+
this.removePortFile();
|
|
1306
|
+
if (this.server) {
|
|
1307
|
+
await new Promise((resolve) => {
|
|
1308
|
+
this.server.close(() => resolve());
|
|
1309
|
+
});
|
|
1310
|
+
this.server = null;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
getPort() {
|
|
1314
|
+
return this.actualPort;
|
|
1315
|
+
}
|
|
1316
|
+
writePortFile() {
|
|
1317
|
+
const dir = path4.dirname(this.portFilePath);
|
|
1318
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
1319
|
+
fs3.writeFileSync(this.portFilePath, String(this.actualPort));
|
|
1320
|
+
}
|
|
1321
|
+
removePortFile() {
|
|
1322
|
+
try {
|
|
1323
|
+
fs3.unlinkSync(this.portFilePath);
|
|
1324
|
+
} catch {
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
async handleRequest(req, res) {
|
|
1328
|
+
const method = req.method?.toUpperCase();
|
|
1329
|
+
const url = req.url || "";
|
|
1330
|
+
try {
|
|
1331
|
+
if (method === "POST" && url === "/api/sessions") {
|
|
1332
|
+
await this.handleCreateSession(req, res);
|
|
1333
|
+
} else if (method === "DELETE" && url.match(/^\/api\/sessions\/(.+)$/)) {
|
|
1334
|
+
const sessionId = url.match(/^\/api\/sessions\/(.+)$/)[1];
|
|
1335
|
+
await this.handleCancelSession(sessionId, res);
|
|
1336
|
+
} else if (method === "GET" && url === "/api/sessions") {
|
|
1337
|
+
await this.handleListSessions(res);
|
|
1338
|
+
} else if (method === "GET" && url === "/api/agents") {
|
|
1339
|
+
await this.handleListAgents(res);
|
|
1340
|
+
} else {
|
|
1341
|
+
this.sendJson(res, 404, { error: "Not found" });
|
|
1342
|
+
}
|
|
1343
|
+
} catch (err) {
|
|
1344
|
+
log4.error({ err }, "API request error");
|
|
1345
|
+
this.sendJson(res, 500, { error: "Internal server error" });
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
async handleCreateSession(req, res) {
|
|
1349
|
+
const body = await this.readBody(req);
|
|
1350
|
+
let agent;
|
|
1351
|
+
let workspace;
|
|
1352
|
+
if (body) {
|
|
1353
|
+
try {
|
|
1354
|
+
const parsed = JSON.parse(body);
|
|
1355
|
+
agent = parsed.agent;
|
|
1356
|
+
workspace = parsed.workspace;
|
|
1357
|
+
} catch {
|
|
1358
|
+
this.sendJson(res, 400, { error: "Invalid JSON body" });
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
const config = this.core.configManager.get();
|
|
1363
|
+
const activeSessions = this.core.sessionManager.listSessions().filter((s) => s.status === "active" || s.status === "initializing");
|
|
1364
|
+
if (activeSessions.length >= config.security.maxConcurrentSessions) {
|
|
1365
|
+
this.sendJson(res, 429, {
|
|
1366
|
+
error: `Max concurrent sessions (${config.security.maxConcurrentSessions}) reached. Cancel a session first.`
|
|
1367
|
+
});
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
const [adapterId, adapter] = this.core.adapters.entries().next().value ?? [null, null];
|
|
1371
|
+
const channelId = adapterId ?? "api";
|
|
1372
|
+
const session = await this.core.handleNewSession(channelId, agent, workspace);
|
|
1373
|
+
if (adapter) {
|
|
1374
|
+
try {
|
|
1375
|
+
const threadId = await adapter.createSessionThread(session.id, `\u{1F504} ${session.agentName} \u2014 New Session`);
|
|
1376
|
+
session.threadId = threadId;
|
|
1377
|
+
this.core.wireSessionEvents(session, adapter);
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
log4.warn({ err, sessionId: session.id }, "Failed to create session thread on adapter, running headless");
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
if (!adapter) {
|
|
1383
|
+
session.agentInstance.onPermissionRequest = async (request) => {
|
|
1384
|
+
const allowOption = request.options.find((o) => o.isAllow);
|
|
1385
|
+
log4.debug({ sessionId: session.id, permissionId: request.id, option: allowOption?.id }, "Auto-approving permission for API session");
|
|
1386
|
+
return allowOption?.id ?? request.options[0]?.id ?? "";
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
session.warmup().catch((err) => log4.warn({ err, sessionId: session.id }, "API session warmup failed"));
|
|
1390
|
+
this.sendJson(res, 200, {
|
|
1391
|
+
sessionId: session.id,
|
|
1392
|
+
agent: session.agentName,
|
|
1393
|
+
status: session.status,
|
|
1394
|
+
workspace: session.workingDirectory
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
async handleCancelSession(sessionId, res) {
|
|
1398
|
+
const session = this.core.sessionManager.getSession(sessionId);
|
|
1399
|
+
if (!session) {
|
|
1400
|
+
this.sendJson(res, 404, { error: `Session "${sessionId}" not found` });
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
await session.cancel();
|
|
1404
|
+
this.sendJson(res, 200, { ok: true });
|
|
1405
|
+
}
|
|
1406
|
+
async handleListSessions(res) {
|
|
1407
|
+
const sessions = this.core.sessionManager.listSessions();
|
|
1408
|
+
this.sendJson(res, 200, {
|
|
1409
|
+
sessions: sessions.map((s) => ({
|
|
1410
|
+
id: s.id,
|
|
1411
|
+
agent: s.agentName,
|
|
1412
|
+
status: s.status,
|
|
1413
|
+
name: s.name ?? null,
|
|
1414
|
+
workspace: s.workingDirectory
|
|
1415
|
+
}))
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
async handleListAgents(res) {
|
|
1419
|
+
const agents = this.core.agentManager.getAvailableAgents();
|
|
1420
|
+
const defaultAgent = this.core.configManager.get().defaultAgent;
|
|
1421
|
+
this.sendJson(res, 200, {
|
|
1422
|
+
agents: agents.map((a) => ({
|
|
1423
|
+
name: a.name,
|
|
1424
|
+
command: a.command,
|
|
1425
|
+
args: a.args
|
|
1426
|
+
})),
|
|
1427
|
+
default: defaultAgent
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
sendJson(res, status, data) {
|
|
1431
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
1432
|
+
res.end(JSON.stringify(data));
|
|
1433
|
+
}
|
|
1434
|
+
readBody(req) {
|
|
1435
|
+
return new Promise((resolve) => {
|
|
1436
|
+
let data = "";
|
|
1437
|
+
req.on("data", (chunk) => {
|
|
1438
|
+
data += chunk;
|
|
1439
|
+
});
|
|
1440
|
+
req.on("end", () => resolve(data));
|
|
1441
|
+
req.on("error", () => resolve(""));
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
|
|
1262
1446
|
// src/adapters/telegram/adapter.ts
|
|
1263
1447
|
import { Bot } from "grammy";
|
|
1264
1448
|
|
|
@@ -1406,20 +1590,25 @@ function splitMessage(text, maxLength = 4096) {
|
|
|
1406
1590
|
}
|
|
1407
1591
|
|
|
1408
1592
|
// src/adapters/telegram/streaming.ts
|
|
1593
|
+
var nextDraftId = 1;
|
|
1409
1594
|
var MessageDraft = class {
|
|
1410
|
-
//
|
|
1411
|
-
constructor(bot, chatId, threadId) {
|
|
1595
|
+
// Only set in fallback mode (sendMessageDraft returns true, not Message)
|
|
1596
|
+
constructor(bot, chatId, threadId, throttleMs = 200, sendQueue) {
|
|
1412
1597
|
this.bot = bot;
|
|
1413
1598
|
this.chatId = chatId;
|
|
1414
1599
|
this.threadId = threadId;
|
|
1600
|
+
this.sendQueue = sendQueue;
|
|
1601
|
+
this.draftId = nextDraftId++;
|
|
1602
|
+
this.minInterval = throttleMs;
|
|
1415
1603
|
}
|
|
1416
|
-
|
|
1604
|
+
draftId;
|
|
1417
1605
|
buffer = "";
|
|
1418
1606
|
lastFlush = 0;
|
|
1419
1607
|
flushTimer;
|
|
1420
1608
|
flushPromise = Promise.resolve();
|
|
1421
|
-
|
|
1422
|
-
|
|
1609
|
+
minInterval;
|
|
1610
|
+
useFallback = false;
|
|
1611
|
+
messageId;
|
|
1423
1612
|
append(text) {
|
|
1424
1613
|
this.buffer += text;
|
|
1425
1614
|
this.scheduleFlush();
|
|
@@ -1444,26 +1633,49 @@ var MessageDraft = class {
|
|
|
1444
1633
|
const html = markdownToTelegramHtml(this.buffer);
|
|
1445
1634
|
const truncated = html.length > 4096 ? html.slice(0, 4090) + "\n..." : html;
|
|
1446
1635
|
if (!truncated) return;
|
|
1636
|
+
if (this.useFallback) {
|
|
1637
|
+
await this.flushFallback(truncated);
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
try {
|
|
1641
|
+
await this.bot.api.sendMessageDraft(this.chatId, this.draftId, truncated, {
|
|
1642
|
+
message_thread_id: this.threadId,
|
|
1643
|
+
parse_mode: "HTML"
|
|
1644
|
+
});
|
|
1645
|
+
} catch {
|
|
1646
|
+
this.useFallback = true;
|
|
1647
|
+
this.minInterval = 1e3;
|
|
1648
|
+
await this.flushFallback(truncated);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
async flushFallback(html) {
|
|
1652
|
+
const exec = this.sendQueue ? (fn) => this.sendQueue.enqueue(fn) : (fn) => fn();
|
|
1447
1653
|
try {
|
|
1448
1654
|
if (!this.messageId) {
|
|
1449
|
-
const msg = await
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1655
|
+
const msg = await exec(
|
|
1656
|
+
() => this.bot.api.sendMessage(this.chatId, html, {
|
|
1657
|
+
message_thread_id: this.threadId,
|
|
1658
|
+
parse_mode: "HTML",
|
|
1659
|
+
disable_notification: true
|
|
1660
|
+
})
|
|
1661
|
+
);
|
|
1454
1662
|
this.messageId = msg.message_id;
|
|
1455
1663
|
} else {
|
|
1456
|
-
await
|
|
1457
|
-
|
|
1458
|
-
|
|
1664
|
+
await exec(
|
|
1665
|
+
() => this.bot.api.editMessageText(this.chatId, this.messageId, html, {
|
|
1666
|
+
parse_mode: "HTML"
|
|
1667
|
+
})
|
|
1668
|
+
);
|
|
1459
1669
|
}
|
|
1460
1670
|
} catch {
|
|
1461
1671
|
try {
|
|
1462
1672
|
if (!this.messageId) {
|
|
1463
|
-
const msg = await
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1673
|
+
const msg = await exec(
|
|
1674
|
+
() => this.bot.api.sendMessage(this.chatId, this.buffer.slice(0, 4096), {
|
|
1675
|
+
message_thread_id: this.threadId,
|
|
1676
|
+
disable_notification: true
|
|
1677
|
+
})
|
|
1678
|
+
);
|
|
1467
1679
|
this.messageId = msg.message_id;
|
|
1468
1680
|
}
|
|
1469
1681
|
} catch {
|
|
@@ -1545,7 +1757,7 @@ function buildDeepLink(chatId, messageId) {
|
|
|
1545
1757
|
// src/adapters/telegram/commands.ts
|
|
1546
1758
|
import { InlineKeyboard } from "grammy";
|
|
1547
1759
|
import { nanoid as nanoid2 } from "nanoid";
|
|
1548
|
-
var
|
|
1760
|
+
var log6 = createChildLogger({ module: "telegram-commands" });
|
|
1549
1761
|
function setupCommands(bot, core, chatId) {
|
|
1550
1762
|
bot.command("new", (ctx) => handleNew(ctx, core, chatId));
|
|
1551
1763
|
bot.command("new_chat", (ctx) => handleNewChat(ctx, core, chatId));
|
|
@@ -1600,7 +1812,7 @@ async function handleNew(ctx, core, chatId) {
|
|
|
1600
1812
|
const args = matchStr.split(" ").filter(Boolean);
|
|
1601
1813
|
const agentName = args[0];
|
|
1602
1814
|
const workspace = args[1];
|
|
1603
|
-
|
|
1815
|
+
log6.info({ userId: ctx.from?.id, agentName }, "New session command");
|
|
1604
1816
|
let threadId;
|
|
1605
1817
|
try {
|
|
1606
1818
|
const topicName = `\u{1F504} New Session`;
|
|
@@ -1633,15 +1845,16 @@ async function handleNew(ctx, core, chatId) {
|
|
|
1633
1845
|
parse_mode: "HTML"
|
|
1634
1846
|
}
|
|
1635
1847
|
);
|
|
1636
|
-
session.warmup().catch((err) =>
|
|
1848
|
+
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
1637
1849
|
} catch (err) {
|
|
1850
|
+
log6.error({ err }, "Session creation failed");
|
|
1638
1851
|
if (threadId) {
|
|
1639
1852
|
try {
|
|
1640
1853
|
await ctx.api.deleteForumTopic(chatId, threadId);
|
|
1641
1854
|
} catch {
|
|
1642
1855
|
}
|
|
1643
1856
|
}
|
|
1644
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1857
|
+
const message = err instanceof Error ? err.message : typeof err === "object" ? JSON.stringify(err) : String(err);
|
|
1645
1858
|
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
1646
1859
|
}
|
|
1647
1860
|
}
|
|
@@ -1686,7 +1899,7 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1686
1899
|
parse_mode: "HTML"
|
|
1687
1900
|
}
|
|
1688
1901
|
);
|
|
1689
|
-
session.warmup().catch((err) =>
|
|
1902
|
+
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
1690
1903
|
} catch (err) {
|
|
1691
1904
|
const message = err instanceof Error ? err.message : String(err);
|
|
1692
1905
|
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
@@ -1700,7 +1913,7 @@ async function handleCancel(ctx, core) {
|
|
|
1700
1913
|
String(threadId)
|
|
1701
1914
|
);
|
|
1702
1915
|
if (session) {
|
|
1703
|
-
|
|
1916
|
+
log6.info({ sessionId: session.id }, "Cancel session command");
|
|
1704
1917
|
await session.cancel();
|
|
1705
1918
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
1706
1919
|
}
|
|
@@ -1808,6 +2021,41 @@ function setupSkillCallbacks(bot, core) {
|
|
|
1808
2021
|
await session.enqueuePrompt(`/${entry.commandName}`);
|
|
1809
2022
|
});
|
|
1810
2023
|
}
|
|
2024
|
+
async function executeNewSession(bot, core, chatId, agentName, workspace) {
|
|
2025
|
+
const threadId = await createSessionTopic(bot, chatId, "\u{1F504} New Session");
|
|
2026
|
+
await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
|
|
2027
|
+
message_thread_id: threadId,
|
|
2028
|
+
parse_mode: "HTML"
|
|
2029
|
+
});
|
|
2030
|
+
try {
|
|
2031
|
+
const session = await core.handleNewSession(
|
|
2032
|
+
"telegram",
|
|
2033
|
+
agentName,
|
|
2034
|
+
workspace
|
|
2035
|
+
);
|
|
2036
|
+
session.threadId = String(threadId);
|
|
2037
|
+
await core.sessionManager.updateSessionPlatform(session.id, {
|
|
2038
|
+
topicId: threadId
|
|
2039
|
+
});
|
|
2040
|
+
const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
|
|
2041
|
+
await renameSessionTopic(bot, chatId, threadId, finalName);
|
|
2042
|
+
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
2043
|
+
return { session, threadId };
|
|
2044
|
+
} catch (err) {
|
|
2045
|
+
try {
|
|
2046
|
+
await bot.api.deleteForumTopic(chatId, threadId);
|
|
2047
|
+
} catch {
|
|
2048
|
+
}
|
|
2049
|
+
throw err;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
async function executeCancelSession(core, excludeSessionId) {
|
|
2053
|
+
const sessions = core.sessionManager.listSessions("telegram").filter((s) => s.status === "active" && s.id !== excludeSessionId).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2054
|
+
const session = sessions[0];
|
|
2055
|
+
if (!session) return null;
|
|
2056
|
+
await session.cancel();
|
|
2057
|
+
return session;
|
|
2058
|
+
}
|
|
1811
2059
|
var STATIC_COMMANDS = [
|
|
1812
2060
|
{ command: "new", description: "Create new session" },
|
|
1813
2061
|
{ command: "new_chat", description: "New chat, same agent & workspace" },
|
|
@@ -1821,7 +2069,7 @@ var STATIC_COMMANDS = [
|
|
|
1821
2069
|
// src/adapters/telegram/permissions.ts
|
|
1822
2070
|
import { InlineKeyboard as InlineKeyboard2 } from "grammy";
|
|
1823
2071
|
import { nanoid as nanoid3 } from "nanoid";
|
|
1824
|
-
var
|
|
2072
|
+
var log7 = createChildLogger({ module: "telegram-permissions" });
|
|
1825
2073
|
var PermissionHandler = class {
|
|
1826
2074
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
1827
2075
|
this.bot = bot;
|
|
@@ -1881,7 +2129,7 @@ ${escapeHtml(request.description)}`,
|
|
|
1881
2129
|
}
|
|
1882
2130
|
const session = this.getSession(pending.sessionId);
|
|
1883
2131
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
1884
|
-
|
|
2132
|
+
log7.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
1885
2133
|
if (session?.pendingPermission?.requestId === pending.requestId) {
|
|
1886
2134
|
session.pendingPermission.resolve(optionId);
|
|
1887
2135
|
session.pendingPermission = void 0;
|
|
@@ -1947,8 +2195,186 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
1947
2195
|
return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
|
|
1948
2196
|
}
|
|
1949
2197
|
|
|
2198
|
+
// src/adapters/telegram/send-queue.ts
|
|
2199
|
+
var TelegramSendQueue = class {
|
|
2200
|
+
queue = Promise.resolve();
|
|
2201
|
+
lastExec = 0;
|
|
2202
|
+
minInterval;
|
|
2203
|
+
constructor(minInterval = 100) {
|
|
2204
|
+
this.minInterval = minInterval;
|
|
2205
|
+
}
|
|
2206
|
+
enqueue(fn) {
|
|
2207
|
+
let resolve;
|
|
2208
|
+
let reject;
|
|
2209
|
+
const resultPromise = new Promise((res, rej) => {
|
|
2210
|
+
resolve = res;
|
|
2211
|
+
reject = rej;
|
|
2212
|
+
});
|
|
2213
|
+
this.queue = this.queue.then(async () => {
|
|
2214
|
+
const elapsed = Date.now() - this.lastExec;
|
|
2215
|
+
if (elapsed < this.minInterval) {
|
|
2216
|
+
await new Promise((r) => setTimeout(r, this.minInterval - elapsed));
|
|
2217
|
+
}
|
|
2218
|
+
try {
|
|
2219
|
+
const result = await fn();
|
|
2220
|
+
resolve(result);
|
|
2221
|
+
} catch (err) {
|
|
2222
|
+
reject(err);
|
|
2223
|
+
} finally {
|
|
2224
|
+
this.lastExec = Date.now();
|
|
2225
|
+
}
|
|
2226
|
+
});
|
|
2227
|
+
return resultPromise;
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
|
|
2231
|
+
// src/adapters/telegram/action-detect.ts
|
|
2232
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
2233
|
+
import { InlineKeyboard as InlineKeyboard3 } from "grammy";
|
|
2234
|
+
var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
|
|
2235
|
+
var CMD_CANCEL_RE = /\/cancel\b/;
|
|
2236
|
+
var KW_NEW_RE = /(?:tao|tạo|create|new)\s+session/i;
|
|
2237
|
+
var KW_CANCEL_RE = /(?:huy|huỷ|cancel|dung|dừng)\s+session/i;
|
|
2238
|
+
function detectAction(text) {
|
|
2239
|
+
if (!text) return null;
|
|
2240
|
+
const cancelCmd = CMD_CANCEL_RE.exec(text);
|
|
2241
|
+
if (cancelCmd) return { action: "cancel_session" };
|
|
2242
|
+
const newCmd = CMD_NEW_RE.exec(text);
|
|
2243
|
+
if (newCmd) {
|
|
2244
|
+
return {
|
|
2245
|
+
action: "new_session",
|
|
2246
|
+
agent: newCmd[1] || void 0,
|
|
2247
|
+
workspace: newCmd[2] || void 0
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
if (KW_CANCEL_RE.test(text)) return { action: "cancel_session" };
|
|
2251
|
+
if (KW_NEW_RE.test(text))
|
|
2252
|
+
return { action: "new_session", agent: void 0, workspace: void 0 };
|
|
2253
|
+
return null;
|
|
2254
|
+
}
|
|
2255
|
+
var ACTION_TTL_MS = 5 * 60 * 1e3;
|
|
2256
|
+
var actionMap = /* @__PURE__ */ new Map();
|
|
2257
|
+
function storeAction(action) {
|
|
2258
|
+
const id = nanoid4(10);
|
|
2259
|
+
actionMap.set(id, { action, createdAt: Date.now() });
|
|
2260
|
+
for (const [key, entry] of actionMap) {
|
|
2261
|
+
if (Date.now() - entry.createdAt > ACTION_TTL_MS) {
|
|
2262
|
+
actionMap.delete(key);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return id;
|
|
2266
|
+
}
|
|
2267
|
+
function getAction(id) {
|
|
2268
|
+
const entry = actionMap.get(id);
|
|
2269
|
+
if (!entry) return void 0;
|
|
2270
|
+
if (Date.now() - entry.createdAt > ACTION_TTL_MS) {
|
|
2271
|
+
actionMap.delete(id);
|
|
2272
|
+
return void 0;
|
|
2273
|
+
}
|
|
2274
|
+
return entry.action;
|
|
2275
|
+
}
|
|
2276
|
+
function removeAction(id) {
|
|
2277
|
+
actionMap.delete(id);
|
|
2278
|
+
}
|
|
2279
|
+
function buildActionKeyboard(actionId, action) {
|
|
2280
|
+
const keyboard = new InlineKeyboard3();
|
|
2281
|
+
if (action.action === "new_session") {
|
|
2282
|
+
keyboard.text("\u2705 T\u1EA1o session", `a:${actionId}`);
|
|
2283
|
+
keyboard.text("\u274C Hu\u1EF7", `a:dismiss:${actionId}`);
|
|
2284
|
+
} else {
|
|
2285
|
+
keyboard.text("\u26D4 Hu\u1EF7 session", `a:${actionId}`);
|
|
2286
|
+
keyboard.text("\u274C Kh\xF4ng", `a:dismiss:${actionId}`);
|
|
2287
|
+
}
|
|
2288
|
+
return keyboard;
|
|
2289
|
+
}
|
|
2290
|
+
function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
2291
|
+
bot.callbackQuery(/^a:dismiss:/, async (ctx) => {
|
|
2292
|
+
const actionId = ctx.callbackQuery.data.replace("a:dismiss:", "");
|
|
2293
|
+
removeAction(actionId);
|
|
2294
|
+
try {
|
|
2295
|
+
await ctx.editMessageReplyMarkup({
|
|
2296
|
+
reply_markup: { inline_keyboard: [] }
|
|
2297
|
+
});
|
|
2298
|
+
} catch {
|
|
2299
|
+
}
|
|
2300
|
+
await ctx.answerCallbackQuery({ text: "\u0110\xE3 hu\u1EF7" });
|
|
2301
|
+
});
|
|
2302
|
+
bot.callbackQuery(/^a:(?!dismiss)/, async (ctx) => {
|
|
2303
|
+
const actionId = ctx.callbackQuery.data.replace("a:", "");
|
|
2304
|
+
const action = getAction(actionId);
|
|
2305
|
+
if (!action) {
|
|
2306
|
+
await ctx.answerCallbackQuery({ text: "Action \u0111\xE3 h\u1EBFt h\u1EA1n" });
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
removeAction(actionId);
|
|
2310
|
+
try {
|
|
2311
|
+
if (action.action === "new_session") {
|
|
2312
|
+
await ctx.answerCallbackQuery({ text: "\u23F3 \u0110ang t\u1EA1o session..." });
|
|
2313
|
+
const { threadId } = await executeNewSession(
|
|
2314
|
+
bot,
|
|
2315
|
+
core,
|
|
2316
|
+
chatId,
|
|
2317
|
+
action.agent,
|
|
2318
|
+
action.workspace
|
|
2319
|
+
);
|
|
2320
|
+
const topicLink = `https://t.me/c/${String(chatId).replace("-100", "")}/${threadId}`;
|
|
2321
|
+
const originalText = ctx.callbackQuery.message?.text ?? "";
|
|
2322
|
+
try {
|
|
2323
|
+
await ctx.editMessageText(
|
|
2324
|
+
originalText + `
|
|
2325
|
+
|
|
2326
|
+
\u2705 Session created \u2192 <a href="${topicLink}">Go to topic</a>`,
|
|
2327
|
+
{ parse_mode: "HTML" }
|
|
2328
|
+
);
|
|
2329
|
+
} catch {
|
|
2330
|
+
await ctx.editMessageReplyMarkup({
|
|
2331
|
+
reply_markup: { inline_keyboard: [] }
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
} else if (action.action === "cancel_session") {
|
|
2335
|
+
const assistantId = getAssistantSessionId();
|
|
2336
|
+
const cancelled = await executeCancelSession(core, assistantId);
|
|
2337
|
+
if (cancelled) {
|
|
2338
|
+
await ctx.answerCallbackQuery({ text: "\u26D4 Session \u0111\xE3 hu\u1EF7" });
|
|
2339
|
+
const originalText = ctx.callbackQuery.message?.text ?? "";
|
|
2340
|
+
try {
|
|
2341
|
+
await ctx.editMessageText(
|
|
2342
|
+
originalText + `
|
|
2343
|
+
|
|
2344
|
+
\u26D4 Session "${cancelled.name ?? cancelled.id}" \u0111\xE3 hu\u1EF7`,
|
|
2345
|
+
{ parse_mode: "HTML" }
|
|
2346
|
+
);
|
|
2347
|
+
} catch {
|
|
2348
|
+
await ctx.editMessageReplyMarkup({
|
|
2349
|
+
reply_markup: { inline_keyboard: [] }
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
} else {
|
|
2353
|
+
await ctx.answerCallbackQuery({
|
|
2354
|
+
text: "Kh\xF4ng c\xF3 session n\xE0o \u0111ang ch\u1EA1y"
|
|
2355
|
+
});
|
|
2356
|
+
try {
|
|
2357
|
+
await ctx.editMessageReplyMarkup({
|
|
2358
|
+
reply_markup: { inline_keyboard: [] }
|
|
2359
|
+
});
|
|
2360
|
+
} catch {
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
} catch {
|
|
2365
|
+
await ctx.answerCallbackQuery({ text: "\u274C L\u1ED7i, th\u1EED l\u1EA1i sau" });
|
|
2366
|
+
try {
|
|
2367
|
+
await ctx.editMessageReplyMarkup({
|
|
2368
|
+
reply_markup: { inline_keyboard: [] }
|
|
2369
|
+
});
|
|
2370
|
+
} catch {
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2375
|
+
|
|
1950
2376
|
// src/adapters/telegram/adapter.ts
|
|
1951
|
-
var
|
|
2377
|
+
var log8 = createChildLogger({ module: "telegram" });
|
|
1952
2378
|
function patchedFetch(input, init) {
|
|
1953
2379
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
1954
2380
|
const nativeController = new AbortController();
|
|
@@ -1966,6 +2392,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
1966
2392
|
bot;
|
|
1967
2393
|
telegramConfig;
|
|
1968
2394
|
sessionDrafts = /* @__PURE__ */ new Map();
|
|
2395
|
+
sessionTextBuffers = /* @__PURE__ */ new Map();
|
|
1969
2396
|
toolCallMessages = /* @__PURE__ */ new Map();
|
|
1970
2397
|
// sessionId → (toolCallId → state)
|
|
1971
2398
|
permissionHandler;
|
|
@@ -1974,6 +2401,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
1974
2401
|
assistantTopicId;
|
|
1975
2402
|
skillMessages = /* @__PURE__ */ new Map();
|
|
1976
2403
|
// sessionId → pinned messageId
|
|
2404
|
+
sendQueue = new TelegramSendQueue();
|
|
1977
2405
|
constructor(core, config) {
|
|
1978
2406
|
super(core, config);
|
|
1979
2407
|
this.telegramConfig = config;
|
|
@@ -1982,7 +2410,23 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
1982
2410
|
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
1983
2411
|
this.bot.catch((err) => {
|
|
1984
2412
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
1985
|
-
|
|
2413
|
+
log8.error({ err: rootCause }, "Telegram bot error");
|
|
2414
|
+
});
|
|
2415
|
+
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
2416
|
+
const maxRetries = 3;
|
|
2417
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
2418
|
+
const result = await prev(method, payload, signal);
|
|
2419
|
+
if (result.ok || result.error_code !== 429 || attempt === maxRetries) {
|
|
2420
|
+
return result;
|
|
2421
|
+
}
|
|
2422
|
+
const retryAfter = (result.parameters?.retry_after ?? 5) + 1;
|
|
2423
|
+
log8.warn(
|
|
2424
|
+
{ method, retryAfter, attempt: attempt + 1 },
|
|
2425
|
+
"Rate limited by Telegram, retrying"
|
|
2426
|
+
);
|
|
2427
|
+
await new Promise((r) => setTimeout(r, retryAfter * 1e3));
|
|
2428
|
+
}
|
|
2429
|
+
return prev(method, payload, signal);
|
|
1986
2430
|
});
|
|
1987
2431
|
this.bot.api.config.use((prev, method, payload, signal) => {
|
|
1988
2432
|
if (method === "getUpdates") {
|
|
@@ -2021,6 +2465,12 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2021
2465
|
(notification) => this.sendNotification(notification)
|
|
2022
2466
|
);
|
|
2023
2467
|
setupSkillCallbacks(this.bot, this.core);
|
|
2468
|
+
setupActionCallbacks(
|
|
2469
|
+
this.bot,
|
|
2470
|
+
this.core,
|
|
2471
|
+
this.telegramConfig.chatId,
|
|
2472
|
+
() => this.assistantSession?.id
|
|
2473
|
+
);
|
|
2024
2474
|
setupMenuCallbacks(
|
|
2025
2475
|
this.bot,
|
|
2026
2476
|
this.core,
|
|
@@ -2035,7 +2485,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2035
2485
|
this.setupRoutes();
|
|
2036
2486
|
this.bot.start({
|
|
2037
2487
|
allowed_updates: ["message", "callback_query"],
|
|
2038
|
-
onStart: () =>
|
|
2488
|
+
onStart: () => log8.info(
|
|
2039
2489
|
{ chatId: this.telegramConfig.chatId },
|
|
2040
2490
|
"Telegram bot started"
|
|
2041
2491
|
)
|
|
@@ -2047,7 +2497,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2047
2497
|
this.assistantTopicId
|
|
2048
2498
|
);
|
|
2049
2499
|
} catch (err) {
|
|
2050
|
-
|
|
2500
|
+
log8.error({ err }, "Failed to spawn assistant");
|
|
2051
2501
|
}
|
|
2052
2502
|
try {
|
|
2053
2503
|
const config = this.core.configManager.get();
|
|
@@ -2068,7 +2518,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2068
2518
|
reply_markup: buildMenuKeyboard()
|
|
2069
2519
|
});
|
|
2070
2520
|
} catch (err) {
|
|
2071
|
-
|
|
2521
|
+
log8.warn({ err }, "Failed to send welcome message");
|
|
2072
2522
|
}
|
|
2073
2523
|
}
|
|
2074
2524
|
async stop() {
|
|
@@ -2076,7 +2526,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2076
2526
|
await this.assistantSession.destroy();
|
|
2077
2527
|
}
|
|
2078
2528
|
await this.bot.stop();
|
|
2079
|
-
|
|
2529
|
+
log8.info("Telegram bot stopped");
|
|
2080
2530
|
}
|
|
2081
2531
|
setupRoutes() {
|
|
2082
2532
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -2094,7 +2544,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2094
2544
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2095
2545
|
});
|
|
2096
2546
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
2097
|
-
(err) =>
|
|
2547
|
+
(err) => log8.error({ err }, "Assistant error")
|
|
2098
2548
|
);
|
|
2099
2549
|
return;
|
|
2100
2550
|
}
|
|
@@ -2105,7 +2555,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2105
2555
|
threadId: String(threadId),
|
|
2106
2556
|
userId: String(ctx.from.id),
|
|
2107
2557
|
text: ctx.message.text
|
|
2108
|
-
}).catch((err) =>
|
|
2558
|
+
}).catch((err) => log8.error({ err }, "handleMessage error"));
|
|
2109
2559
|
});
|
|
2110
2560
|
}
|
|
2111
2561
|
// --- ChannelAdapter implementations ---
|
|
@@ -2125,24 +2575,32 @@ Workspace: <code>${workspace}</code>
|
|
|
2125
2575
|
draft = new MessageDraft(
|
|
2126
2576
|
this.bot,
|
|
2127
2577
|
this.telegramConfig.chatId,
|
|
2128
|
-
threadId
|
|
2578
|
+
threadId,
|
|
2579
|
+
this.telegramConfig.streamThrottleMs,
|
|
2580
|
+
this.sendQueue
|
|
2129
2581
|
);
|
|
2130
2582
|
this.sessionDrafts.set(sessionId, draft);
|
|
2131
2583
|
}
|
|
2132
2584
|
draft.append(content.text);
|
|
2585
|
+
this.sessionTextBuffers.set(
|
|
2586
|
+
sessionId,
|
|
2587
|
+
(this.sessionTextBuffers.get(sessionId) ?? "") + content.text
|
|
2588
|
+
);
|
|
2133
2589
|
break;
|
|
2134
2590
|
}
|
|
2135
2591
|
case "tool_call": {
|
|
2136
2592
|
await this.finalizeDraft(sessionId);
|
|
2137
2593
|
const meta = content.metadata;
|
|
2138
|
-
const msg = await this.
|
|
2139
|
-
this.
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2594
|
+
const msg = await this.sendQueue.enqueue(
|
|
2595
|
+
() => this.bot.api.sendMessage(
|
|
2596
|
+
this.telegramConfig.chatId,
|
|
2597
|
+
formatToolCall(meta),
|
|
2598
|
+
{
|
|
2599
|
+
message_thread_id: threadId,
|
|
2600
|
+
parse_mode: "HTML",
|
|
2601
|
+
disable_notification: true
|
|
2602
|
+
}
|
|
2603
|
+
)
|
|
2146
2604
|
);
|
|
2147
2605
|
if (!this.toolCallMessages.has(sessionId)) {
|
|
2148
2606
|
this.toolCallMessages.set(sessionId, /* @__PURE__ */ new Map());
|
|
@@ -2168,11 +2626,13 @@ Workspace: <code>${workspace}</code>
|
|
|
2168
2626
|
viewerLinks
|
|
2169
2627
|
};
|
|
2170
2628
|
try {
|
|
2171
|
-
await this.
|
|
2172
|
-
this.
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2629
|
+
await this.sendQueue.enqueue(
|
|
2630
|
+
() => this.bot.api.editMessageText(
|
|
2631
|
+
this.telegramConfig.chatId,
|
|
2632
|
+
toolState.msgId,
|
|
2633
|
+
formatToolUpdate(merged),
|
|
2634
|
+
{ parse_mode: "HTML" }
|
|
2635
|
+
)
|
|
2176
2636
|
);
|
|
2177
2637
|
} catch {
|
|
2178
2638
|
}
|
|
@@ -2181,30 +2641,35 @@ Workspace: <code>${workspace}</code>
|
|
|
2181
2641
|
}
|
|
2182
2642
|
case "plan": {
|
|
2183
2643
|
await this.finalizeDraft(sessionId);
|
|
2184
|
-
await this.
|
|
2185
|
-
this.
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2644
|
+
await this.sendQueue.enqueue(
|
|
2645
|
+
() => this.bot.api.sendMessage(
|
|
2646
|
+
this.telegramConfig.chatId,
|
|
2647
|
+
formatPlan(
|
|
2648
|
+
content.metadata
|
|
2649
|
+
),
|
|
2650
|
+
{
|
|
2651
|
+
message_thread_id: threadId,
|
|
2652
|
+
parse_mode: "HTML",
|
|
2653
|
+
disable_notification: true
|
|
2654
|
+
}
|
|
2655
|
+
)
|
|
2194
2656
|
);
|
|
2195
2657
|
break;
|
|
2196
2658
|
}
|
|
2197
2659
|
case "usage": {
|
|
2198
|
-
await this.
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2660
|
+
await this.finalizeDraft(sessionId);
|
|
2661
|
+
await this.sendQueue.enqueue(
|
|
2662
|
+
() => this.bot.api.sendMessage(
|
|
2663
|
+
this.telegramConfig.chatId,
|
|
2664
|
+
formatUsage(
|
|
2665
|
+
content.metadata
|
|
2666
|
+
),
|
|
2667
|
+
{
|
|
2668
|
+
message_thread_id: threadId,
|
|
2669
|
+
parse_mode: "HTML",
|
|
2670
|
+
disable_notification: true
|
|
2671
|
+
}
|
|
2672
|
+
)
|
|
2208
2673
|
);
|
|
2209
2674
|
break;
|
|
2210
2675
|
}
|
|
@@ -2213,42 +2678,48 @@ Workspace: <code>${workspace}</code>
|
|
|
2213
2678
|
this.sessionDrafts.delete(sessionId);
|
|
2214
2679
|
this.toolCallMessages.delete(sessionId);
|
|
2215
2680
|
await this.cleanupSkillCommands(sessionId);
|
|
2216
|
-
await this.
|
|
2217
|
-
this.
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2681
|
+
await this.sendQueue.enqueue(
|
|
2682
|
+
() => this.bot.api.sendMessage(
|
|
2683
|
+
this.telegramConfig.chatId,
|
|
2684
|
+
`\u2705 <b>Done</b>`,
|
|
2685
|
+
{
|
|
2686
|
+
message_thread_id: threadId,
|
|
2687
|
+
parse_mode: "HTML",
|
|
2688
|
+
disable_notification: true
|
|
2689
|
+
}
|
|
2690
|
+
)
|
|
2224
2691
|
);
|
|
2225
2692
|
break;
|
|
2226
2693
|
}
|
|
2227
2694
|
case "error": {
|
|
2228
2695
|
await this.finalizeDraft(sessionId);
|
|
2229
|
-
await this.
|
|
2230
|
-
this.
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2696
|
+
await this.sendQueue.enqueue(
|
|
2697
|
+
() => this.bot.api.sendMessage(
|
|
2698
|
+
this.telegramConfig.chatId,
|
|
2699
|
+
`\u274C <b>Error:</b> ${escapeHtml(content.text)}`,
|
|
2700
|
+
{
|
|
2701
|
+
message_thread_id: threadId,
|
|
2702
|
+
parse_mode: "HTML",
|
|
2703
|
+
disable_notification: true
|
|
2704
|
+
}
|
|
2705
|
+
)
|
|
2237
2706
|
);
|
|
2238
2707
|
break;
|
|
2239
2708
|
}
|
|
2240
2709
|
}
|
|
2241
2710
|
}
|
|
2242
2711
|
async sendPermissionRequest(sessionId, request) {
|
|
2243
|
-
|
|
2712
|
+
log8.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
2244
2713
|
const session = this.core.sessionManager.getSession(
|
|
2245
2714
|
sessionId
|
|
2246
2715
|
);
|
|
2247
2716
|
if (!session) return;
|
|
2248
|
-
await this.
|
|
2717
|
+
await this.sendQueue.enqueue(
|
|
2718
|
+
() => this.permissionHandler.sendPermissionRequest(session, request)
|
|
2719
|
+
);
|
|
2249
2720
|
}
|
|
2250
2721
|
async sendNotification(notification) {
|
|
2251
|
-
|
|
2722
|
+
log8.info(
|
|
2252
2723
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
2253
2724
|
"Notification sent"
|
|
2254
2725
|
);
|
|
@@ -2267,14 +2738,16 @@ Workspace: <code>${workspace}</code>
|
|
|
2267
2738
|
|
|
2268
2739
|
<a href="${notification.deepLink}">\u2192 Go to message</a>`;
|
|
2269
2740
|
}
|
|
2270
|
-
await this.
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2741
|
+
await this.sendQueue.enqueue(
|
|
2742
|
+
() => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
|
|
2743
|
+
message_thread_id: this.notificationTopicId,
|
|
2744
|
+
parse_mode: "HTML",
|
|
2745
|
+
disable_notification: false
|
|
2746
|
+
})
|
|
2747
|
+
);
|
|
2275
2748
|
}
|
|
2276
2749
|
async createSessionThread(sessionId, name) {
|
|
2277
|
-
|
|
2750
|
+
log8.info({ sessionId, name }, "Session topic created");
|
|
2278
2751
|
return String(
|
|
2279
2752
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
2280
2753
|
);
|
|
@@ -2319,15 +2792,17 @@ Workspace: <code>${workspace}</code>
|
|
|
2319
2792
|
}
|
|
2320
2793
|
}
|
|
2321
2794
|
try {
|
|
2322
|
-
const msg = await this.
|
|
2323
|
-
this.
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2795
|
+
const msg = await this.sendQueue.enqueue(
|
|
2796
|
+
() => this.bot.api.sendMessage(
|
|
2797
|
+
this.telegramConfig.chatId,
|
|
2798
|
+
text,
|
|
2799
|
+
{
|
|
2800
|
+
message_thread_id: threadId,
|
|
2801
|
+
parse_mode: "HTML",
|
|
2802
|
+
reply_markup: keyboard,
|
|
2803
|
+
disable_notification: true
|
|
2804
|
+
}
|
|
2805
|
+
)
|
|
2331
2806
|
);
|
|
2332
2807
|
this.skillMessages.set(sessionId, msg.message_id);
|
|
2333
2808
|
await this.bot.api.pinChatMessage(
|
|
@@ -2338,7 +2813,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2338
2813
|
}
|
|
2339
2814
|
);
|
|
2340
2815
|
} catch (err) {
|
|
2341
|
-
|
|
2816
|
+
log8.error({ err, sessionId }, "Failed to send skill commands");
|
|
2342
2817
|
}
|
|
2343
2818
|
await this.updateCommandAutocomplete(session.agentName, commands);
|
|
2344
2819
|
}
|
|
@@ -2369,12 +2844,12 @@ Workspace: <code>${workspace}</code>
|
|
|
2369
2844
|
await this.bot.api.setMyCommands(all, {
|
|
2370
2845
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
2371
2846
|
});
|
|
2372
|
-
|
|
2847
|
+
log8.info(
|
|
2373
2848
|
{ count: all.length, skills: validSkills.length },
|
|
2374
2849
|
"Updated command autocomplete"
|
|
2375
2850
|
);
|
|
2376
2851
|
} catch (err) {
|
|
2377
|
-
|
|
2852
|
+
log8.error(
|
|
2378
2853
|
{ err, commands: all },
|
|
2379
2854
|
"Failed to update command autocomplete"
|
|
2380
2855
|
);
|
|
@@ -2382,9 +2857,29 @@ Workspace: <code>${workspace}</code>
|
|
|
2382
2857
|
}
|
|
2383
2858
|
async finalizeDraft(sessionId) {
|
|
2384
2859
|
const draft = this.sessionDrafts.get(sessionId);
|
|
2385
|
-
if (draft)
|
|
2386
|
-
|
|
2387
|
-
|
|
2860
|
+
if (!draft) return;
|
|
2861
|
+
const finalMsgId = await draft.finalize();
|
|
2862
|
+
this.sessionDrafts.delete(sessionId);
|
|
2863
|
+
if (sessionId === this.assistantSession?.id) {
|
|
2864
|
+
const fullText = this.sessionTextBuffers.get(sessionId);
|
|
2865
|
+
this.sessionTextBuffers.delete(sessionId);
|
|
2866
|
+
if (fullText && finalMsgId) {
|
|
2867
|
+
const detected = detectAction(fullText);
|
|
2868
|
+
if (detected) {
|
|
2869
|
+
const actionId = storeAction(detected);
|
|
2870
|
+
const keyboard = buildActionKeyboard(actionId, detected);
|
|
2871
|
+
try {
|
|
2872
|
+
await this.bot.api.editMessageReplyMarkup(
|
|
2873
|
+
this.telegramConfig.chatId,
|
|
2874
|
+
finalMsgId,
|
|
2875
|
+
{ reply_markup: keyboard }
|
|
2876
|
+
);
|
|
2877
|
+
} catch {
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
} else {
|
|
2882
|
+
this.sessionTextBuffers.delete(sessionId);
|
|
2388
2883
|
}
|
|
2389
2884
|
}
|
|
2390
2885
|
};
|
|
@@ -2400,6 +2895,7 @@ export {
|
|
|
2400
2895
|
NotificationManager,
|
|
2401
2896
|
OpenACPCore,
|
|
2402
2897
|
ChannelAdapter,
|
|
2898
|
+
ApiServer,
|
|
2403
2899
|
TelegramAdapter
|
|
2404
2900
|
};
|
|
2405
|
-
//# sourceMappingURL=chunk-
|
|
2901
|
+
//# sourceMappingURL=chunk-S5MPFOR3.js.map
|