@openacp/cli 0.2.23 → 0.2.24

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 (44) hide show
  1. package/dist/autostart-YBYXQA77.js +18 -0
  2. package/dist/autostart-YBYXQA77.js.map +1 -0
  3. package/dist/{setup-XQBEZZQB.js → chunk-4BN7NSKB.js} +140 -9
  4. package/dist/chunk-4BN7NSKB.js.map +1 -0
  5. package/dist/chunk-CQMS5U7Z.js +63 -0
  6. package/dist/chunk-CQMS5U7Z.js.map +1 -0
  7. package/dist/{chunk-XOVJLTEC.js → chunk-FGXG3H3F.js} +14 -58
  8. package/dist/chunk-FGXG3H3F.js.map +1 -0
  9. package/dist/{chunk-EIBLQU3H.js → chunk-IX63F4JG.js} +449 -43
  10. package/dist/chunk-IX63F4JG.js.map +1 -0
  11. package/dist/{chunk-ZATQZUJT.js → chunk-MNJDYDGH.js} +9 -1
  12. package/dist/{chunk-ZATQZUJT.js.map → chunk-MNJDYDGH.js.map} +1 -1
  13. package/dist/chunk-PQRVTUNH.js +145 -0
  14. package/dist/chunk-PQRVTUNH.js.map +1 -0
  15. package/dist/chunk-QWUJIKTX.js +527 -0
  16. package/dist/chunk-QWUJIKTX.js.map +1 -0
  17. package/dist/chunk-S6O7SM6A.js +129 -0
  18. package/dist/chunk-S6O7SM6A.js.map +1 -0
  19. package/dist/chunk-WXS6ONOD.js +103 -0
  20. package/dist/chunk-WXS6ONOD.js.map +1 -0
  21. package/dist/cli.js +354 -4
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config-2XALNLAA.js +14 -0
  24. package/dist/config-2XALNLAA.js.map +1 -0
  25. package/dist/config-editor-56B6YU7B.js +11 -0
  26. package/dist/config-editor-56B6YU7B.js.map +1 -0
  27. package/dist/daemon-3E5OMLT3.js +29 -0
  28. package/dist/daemon-3E5OMLT3.js.map +1 -0
  29. package/dist/index.d.ts +96 -18
  30. package/dist/index.js +35 -6
  31. package/dist/install-cloudflared-57NRTI4E.js +8 -0
  32. package/dist/install-cloudflared-57NRTI4E.js.map +1 -0
  33. package/dist/{main-VJUX7RUY.js → main-YNCSLYVV.js} +43 -11
  34. package/dist/main-YNCSLYVV.js.map +1 -0
  35. package/dist/setup-FTNJACSC.js +27 -0
  36. package/dist/setup-FTNJACSC.js.map +1 -0
  37. package/dist/{tunnel-service-I6NUMBT4.js → tunnel-service-I6WM6USB.js} +10 -10
  38. package/dist/tunnel-service-I6WM6USB.js.map +1 -0
  39. package/package.json +2 -2
  40. package/dist/chunk-EIBLQU3H.js.map +0 -1
  41. package/dist/chunk-XOVJLTEC.js.map +0 -1
  42. package/dist/main-VJUX7RUY.js.map +0 -1
  43. package/dist/setup-XQBEZZQB.js.map +0 -1
  44. 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-ZATQZUJT.js";
4
+ } from "./chunk-MNJDYDGH.js";
5
5
 
6
6
  // src/core/streams.ts
7
7
  function nodeToWebWritable(nodeStream) {
@@ -580,6 +580,7 @@ var Session = class {
580
580
  try {
581
581
  const start = Date.now();
582
582
  await this.agentInstance.prompt('Reply with only "ready".');
583
+ this.status = "active";
583
584
  this.log.info({ durationMs: Date.now() - start }, "Warm-up complete");
584
585
  } catch (err) {
585
586
  this.log.error({ err }, "Warm-up failed");
@@ -1259,6 +1260,187 @@ var ChannelAdapter = class {
1259
1260
  }
1260
1261
  };
1261
1262
 
1263
+ // src/core/api-server.ts
1264
+ import * as http from "http";
1265
+ import * as fs3 from "fs";
1266
+ import * as path4 from "path";
1267
+ import * as os2 from "os";
1268
+ var log4 = createChildLogger({ module: "api-server" });
1269
+ var DEFAULT_PORT_FILE = path4.join(os2.homedir(), ".openacp", "api.port");
1270
+ var ApiServer = class {
1271
+ constructor(core, config, portFilePath) {
1272
+ this.core = core;
1273
+ this.config = config;
1274
+ this.portFilePath = portFilePath ?? DEFAULT_PORT_FILE;
1275
+ }
1276
+ server = null;
1277
+ actualPort = 0;
1278
+ portFilePath;
1279
+ async start() {
1280
+ this.server = http.createServer((req, res) => this.handleRequest(req, res));
1281
+ await new Promise((resolve, reject) => {
1282
+ this.server.on("error", (err) => {
1283
+ if (err.code === "EADDRINUSE") {
1284
+ log4.warn({ port: this.config.port }, "API port in use, continuing without API server");
1285
+ this.server = null;
1286
+ resolve();
1287
+ } else {
1288
+ reject(err);
1289
+ }
1290
+ });
1291
+ this.server.listen(this.config.port, this.config.host, () => {
1292
+ const addr = this.server.address();
1293
+ if (addr && typeof addr === "object") {
1294
+ this.actualPort = addr.port;
1295
+ }
1296
+ this.writePortFile();
1297
+ log4.info({ host: this.config.host, port: this.actualPort }, "API server listening");
1298
+ resolve();
1299
+ });
1300
+ });
1301
+ }
1302
+ async stop() {
1303
+ this.removePortFile();
1304
+ if (this.server) {
1305
+ await new Promise((resolve) => {
1306
+ this.server.close(() => resolve());
1307
+ });
1308
+ this.server = null;
1309
+ }
1310
+ }
1311
+ getPort() {
1312
+ return this.actualPort;
1313
+ }
1314
+ writePortFile() {
1315
+ const dir = path4.dirname(this.portFilePath);
1316
+ fs3.mkdirSync(dir, { recursive: true });
1317
+ fs3.writeFileSync(this.portFilePath, String(this.actualPort));
1318
+ }
1319
+ removePortFile() {
1320
+ try {
1321
+ fs3.unlinkSync(this.portFilePath);
1322
+ } catch {
1323
+ }
1324
+ }
1325
+ async handleRequest(req, res) {
1326
+ const method = req.method?.toUpperCase();
1327
+ const url = req.url || "";
1328
+ try {
1329
+ if (method === "POST" && url === "/api/sessions") {
1330
+ await this.handleCreateSession(req, res);
1331
+ } else if (method === "DELETE" && url.match(/^\/api\/sessions\/(.+)$/)) {
1332
+ const sessionId = url.match(/^\/api\/sessions\/(.+)$/)[1];
1333
+ await this.handleCancelSession(sessionId, res);
1334
+ } else if (method === "GET" && url === "/api/sessions") {
1335
+ await this.handleListSessions(res);
1336
+ } else if (method === "GET" && url === "/api/agents") {
1337
+ await this.handleListAgents(res);
1338
+ } else {
1339
+ this.sendJson(res, 404, { error: "Not found" });
1340
+ }
1341
+ } catch (err) {
1342
+ log4.error({ err }, "API request error");
1343
+ this.sendJson(res, 500, { error: "Internal server error" });
1344
+ }
1345
+ }
1346
+ async handleCreateSession(req, res) {
1347
+ const body = await this.readBody(req);
1348
+ let agent;
1349
+ let workspace;
1350
+ if (body) {
1351
+ try {
1352
+ const parsed = JSON.parse(body);
1353
+ agent = parsed.agent;
1354
+ workspace = parsed.workspace;
1355
+ } catch {
1356
+ this.sendJson(res, 400, { error: "Invalid JSON body" });
1357
+ return;
1358
+ }
1359
+ }
1360
+ const config = this.core.configManager.get();
1361
+ const activeSessions = this.core.sessionManager.listSessions().filter((s) => s.status === "active" || s.status === "initializing");
1362
+ if (activeSessions.length >= config.security.maxConcurrentSessions) {
1363
+ this.sendJson(res, 429, {
1364
+ error: `Max concurrent sessions (${config.security.maxConcurrentSessions}) reached. Cancel a session first.`
1365
+ });
1366
+ return;
1367
+ }
1368
+ const [adapterId, adapter] = this.core.adapters.entries().next().value ?? [null, null];
1369
+ const channelId = adapterId ?? "api";
1370
+ const session = await this.core.handleNewSession(channelId, agent, workspace);
1371
+ if (adapter) {
1372
+ try {
1373
+ const threadId = await adapter.createSessionThread(session.id, `\u{1F504} ${session.agentName} \u2014 New Session`);
1374
+ session.threadId = threadId;
1375
+ this.core.wireSessionEvents(session, adapter);
1376
+ } catch (err) {
1377
+ log4.warn({ err, sessionId: session.id }, "Failed to create session thread on adapter, running headless");
1378
+ }
1379
+ }
1380
+ if (!adapter) {
1381
+ session.agentInstance.onPermissionRequest = async (request) => {
1382
+ const allowOption = request.options.find((o) => o.isAllow);
1383
+ log4.debug({ sessionId: session.id, permissionId: request.id, option: allowOption?.id }, "Auto-approving permission for API session");
1384
+ return allowOption?.id ?? request.options[0]?.id ?? "";
1385
+ };
1386
+ }
1387
+ session.warmup().catch((err) => log4.warn({ err, sessionId: session.id }, "API session warmup failed"));
1388
+ this.sendJson(res, 200, {
1389
+ sessionId: session.id,
1390
+ agent: session.agentName,
1391
+ status: session.status,
1392
+ workspace: session.workingDirectory
1393
+ });
1394
+ }
1395
+ async handleCancelSession(sessionId, res) {
1396
+ const session = this.core.sessionManager.getSession(sessionId);
1397
+ if (!session) {
1398
+ this.sendJson(res, 404, { error: `Session "${sessionId}" not found` });
1399
+ return;
1400
+ }
1401
+ await session.cancel();
1402
+ this.sendJson(res, 200, { ok: true });
1403
+ }
1404
+ async handleListSessions(res) {
1405
+ const sessions = this.core.sessionManager.listSessions();
1406
+ this.sendJson(res, 200, {
1407
+ sessions: sessions.map((s) => ({
1408
+ id: s.id,
1409
+ agent: s.agentName,
1410
+ status: s.status,
1411
+ name: s.name ?? null,
1412
+ workspace: s.workingDirectory
1413
+ }))
1414
+ });
1415
+ }
1416
+ async handleListAgents(res) {
1417
+ const agents = this.core.agentManager.getAvailableAgents();
1418
+ const defaultAgent = this.core.configManager.get().defaultAgent;
1419
+ this.sendJson(res, 200, {
1420
+ agents: agents.map((a) => ({
1421
+ name: a.name,
1422
+ command: a.command,
1423
+ args: a.args
1424
+ })),
1425
+ default: defaultAgent
1426
+ });
1427
+ }
1428
+ sendJson(res, status, data) {
1429
+ res.writeHead(status, { "Content-Type": "application/json" });
1430
+ res.end(JSON.stringify(data));
1431
+ }
1432
+ readBody(req) {
1433
+ return new Promise((resolve) => {
1434
+ let data = "";
1435
+ req.on("data", (chunk) => {
1436
+ data += chunk;
1437
+ });
1438
+ req.on("end", () => resolve(data));
1439
+ req.on("error", () => resolve(""));
1440
+ });
1441
+ }
1442
+ };
1443
+
1262
1444
  // src/adapters/telegram/adapter.ts
1263
1445
  import { Bot } from "grammy";
1264
1446
 
@@ -1453,24 +1635,33 @@ var MessageDraft = class {
1453
1635
  });
1454
1636
  this.messageId = msg.message_id;
1455
1637
  } else {
1456
- await this.bot.api.editMessageText(this.chatId, this.messageId, truncated, {
1457
- parse_mode: "HTML"
1458
- });
1638
+ await this.bot.api.editMessageText(
1639
+ this.chatId,
1640
+ this.messageId,
1641
+ truncated,
1642
+ {
1643
+ parse_mode: "HTML"
1644
+ }
1645
+ );
1459
1646
  }
1460
1647
  } catch {
1461
1648
  try {
1462
1649
  if (!this.messageId) {
1463
- const msg = await this.bot.api.sendMessage(this.chatId, this.buffer.slice(0, 4096), {
1464
- message_thread_id: this.threadId,
1465
- disable_notification: true
1466
- });
1650
+ const msg = await this.bot.api.sendMessage(
1651
+ this.chatId,
1652
+ this.buffer.slice(0, 4096),
1653
+ {
1654
+ message_thread_id: this.threadId,
1655
+ disable_notification: true
1656
+ }
1657
+ );
1467
1658
  this.messageId = msg.message_id;
1468
1659
  }
1469
1660
  } catch {
1470
1661
  }
1471
1662
  }
1472
1663
  }
1473
- async finalize() {
1664
+ async finalize(replyMarkup) {
1474
1665
  if (this.flushTimer) {
1475
1666
  clearTimeout(this.flushTimer);
1476
1667
  this.flushTimer = void 0;
@@ -1482,25 +1673,38 @@ var MessageDraft = class {
1482
1673
  try {
1483
1674
  for (let i = 0; i < chunks.length; i++) {
1484
1675
  const chunk = chunks[i];
1676
+ const isLast = i === chunks.length - 1;
1677
+ const markup = isLast && replyMarkup ? { reply_markup: replyMarkup } : {};
1485
1678
  if (i === 0 && this.messageId) {
1486
- await this.bot.api.editMessageText(this.chatId, this.messageId, chunk, {
1487
- parse_mode: "HTML"
1488
- });
1679
+ await this.bot.api.editMessageText(
1680
+ this.chatId,
1681
+ this.messageId,
1682
+ chunk,
1683
+ {
1684
+ parse_mode: "HTML",
1685
+ ...markup
1686
+ }
1687
+ );
1489
1688
  } else {
1490
1689
  const msg = await this.bot.api.sendMessage(this.chatId, chunk, {
1491
1690
  message_thread_id: this.threadId,
1492
1691
  parse_mode: "HTML",
1493
- disable_notification: true
1692
+ disable_notification: true,
1693
+ ...markup
1494
1694
  });
1495
1695
  this.messageId = msg.message_id;
1496
1696
  }
1497
1697
  }
1498
1698
  } catch {
1499
1699
  try {
1500
- await this.bot.api.sendMessage(this.chatId, this.buffer.slice(0, 4096), {
1501
- message_thread_id: this.threadId,
1502
- disable_notification: true
1503
- });
1700
+ await this.bot.api.sendMessage(
1701
+ this.chatId,
1702
+ this.buffer.slice(0, 4096),
1703
+ {
1704
+ message_thread_id: this.threadId,
1705
+ disable_notification: true
1706
+ }
1707
+ );
1504
1708
  } catch {
1505
1709
  }
1506
1710
  }
@@ -1509,6 +1713,9 @@ var MessageDraft = class {
1509
1713
  getMessageId() {
1510
1714
  return this.messageId;
1511
1715
  }
1716
+ getBuffer() {
1717
+ return this.buffer;
1718
+ }
1512
1719
  };
1513
1720
 
1514
1721
  // src/adapters/telegram/topics.ts
@@ -1545,7 +1752,7 @@ function buildDeepLink(chatId, messageId) {
1545
1752
  // src/adapters/telegram/commands.ts
1546
1753
  import { InlineKeyboard } from "grammy";
1547
1754
  import { nanoid as nanoid2 } from "nanoid";
1548
- var log5 = createChildLogger({ module: "telegram-commands" });
1755
+ var log6 = createChildLogger({ module: "telegram-commands" });
1549
1756
  function setupCommands(bot, core, chatId) {
1550
1757
  bot.command("new", (ctx) => handleNew(ctx, core, chatId));
1551
1758
  bot.command("new_chat", (ctx) => handleNewChat(ctx, core, chatId));
@@ -1600,7 +1807,7 @@ async function handleNew(ctx, core, chatId) {
1600
1807
  const args = matchStr.split(" ").filter(Boolean);
1601
1808
  const agentName = args[0];
1602
1809
  const workspace = args[1];
1603
- log5.info({ userId: ctx.from?.id, agentName }, "New session command");
1810
+ log6.info({ userId: ctx.from?.id, agentName }, "New session command");
1604
1811
  let threadId;
1605
1812
  try {
1606
1813
  const topicName = `\u{1F504} New Session`;
@@ -1633,15 +1840,16 @@ async function handleNew(ctx, core, chatId) {
1633
1840
  parse_mode: "HTML"
1634
1841
  }
1635
1842
  );
1636
- session.warmup().catch((err) => log5.error({ err }, "Warm-up error"));
1843
+ session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
1637
1844
  } catch (err) {
1845
+ log6.error({ err }, "Session creation failed");
1638
1846
  if (threadId) {
1639
1847
  try {
1640
1848
  await ctx.api.deleteForumTopic(chatId, threadId);
1641
1849
  } catch {
1642
1850
  }
1643
1851
  }
1644
- const message = err instanceof Error ? err.message : String(err);
1852
+ const message = err instanceof Error ? err.message : typeof err === "object" ? JSON.stringify(err) : String(err);
1645
1853
  await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
1646
1854
  }
1647
1855
  }
@@ -1686,7 +1894,7 @@ async function handleNewChat(ctx, core, chatId) {
1686
1894
  parse_mode: "HTML"
1687
1895
  }
1688
1896
  );
1689
- session.warmup().catch((err) => log5.error({ err }, "Warm-up error"));
1897
+ session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
1690
1898
  } catch (err) {
1691
1899
  const message = err instanceof Error ? err.message : String(err);
1692
1900
  await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
@@ -1700,7 +1908,7 @@ async function handleCancel(ctx, core) {
1700
1908
  String(threadId)
1701
1909
  );
1702
1910
  if (session) {
1703
- log5.info({ sessionId: session.id }, "Cancel session command");
1911
+ log6.info({ sessionId: session.id }, "Cancel session command");
1704
1912
  await session.cancel();
1705
1913
  await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
1706
1914
  }
@@ -1808,6 +2016,41 @@ function setupSkillCallbacks(bot, core) {
1808
2016
  await session.enqueuePrompt(`/${entry.commandName}`);
1809
2017
  });
1810
2018
  }
2019
+ async function executeNewSession(bot, core, chatId, agentName, workspace) {
2020
+ const threadId = await createSessionTopic(bot, chatId, "\u{1F504} New Session");
2021
+ await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
2022
+ message_thread_id: threadId,
2023
+ parse_mode: "HTML"
2024
+ });
2025
+ try {
2026
+ const session = await core.handleNewSession(
2027
+ "telegram",
2028
+ agentName,
2029
+ workspace
2030
+ );
2031
+ session.threadId = String(threadId);
2032
+ await core.sessionManager.updateSessionPlatform(session.id, {
2033
+ topicId: threadId
2034
+ });
2035
+ const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
2036
+ await renameSessionTopic(bot, chatId, threadId, finalName);
2037
+ session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
2038
+ return { session, threadId };
2039
+ } catch (err) {
2040
+ try {
2041
+ await bot.api.deleteForumTopic(chatId, threadId);
2042
+ } catch {
2043
+ }
2044
+ throw err;
2045
+ }
2046
+ }
2047
+ async function executeCancelSession(core, excludeSessionId) {
2048
+ const sessions = core.sessionManager.listSessions("telegram").filter((s) => s.status === "active" && s.id !== excludeSessionId).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
2049
+ const session = sessions[0];
2050
+ if (!session) return null;
2051
+ await session.cancel();
2052
+ return session;
2053
+ }
1811
2054
  var STATIC_COMMANDS = [
1812
2055
  { command: "new", description: "Create new session" },
1813
2056
  { command: "new_chat", description: "New chat, same agent & workspace" },
@@ -1821,7 +2064,7 @@ var STATIC_COMMANDS = [
1821
2064
  // src/adapters/telegram/permissions.ts
1822
2065
  import { InlineKeyboard as InlineKeyboard2 } from "grammy";
1823
2066
  import { nanoid as nanoid3 } from "nanoid";
1824
- var log6 = createChildLogger({ module: "telegram-permissions" });
2067
+ var log7 = createChildLogger({ module: "telegram-permissions" });
1825
2068
  var PermissionHandler = class {
1826
2069
  constructor(bot, chatId, getSession, sendNotification) {
1827
2070
  this.bot = bot;
@@ -1881,7 +2124,7 @@ ${escapeHtml(request.description)}`,
1881
2124
  }
1882
2125
  const session = this.getSession(pending.sessionId);
1883
2126
  const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
1884
- log6.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
2127
+ log7.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
1885
2128
  if (session?.pendingPermission?.requestId === pending.requestId) {
1886
2129
  session.pendingPermission.resolve(optionId);
1887
2130
  session.pendingPermission = void 0;
@@ -1947,8 +2190,153 @@ function redirectToAssistant(chatId, assistantTopicId) {
1947
2190
  return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
1948
2191
  }
1949
2192
 
2193
+ // src/adapters/telegram/action-detect.ts
2194
+ import { nanoid as nanoid4 } from "nanoid";
2195
+ import { InlineKeyboard as InlineKeyboard3 } from "grammy";
2196
+ var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
2197
+ var CMD_CANCEL_RE = /\/cancel\b/;
2198
+ var KW_NEW_RE = /(?:tao|tạo|create|new)\s+session/i;
2199
+ var KW_CANCEL_RE = /(?:huy|huỷ|cancel|dung|dừng)\s+session/i;
2200
+ function detectAction(text) {
2201
+ if (!text) return null;
2202
+ const cancelCmd = CMD_CANCEL_RE.exec(text);
2203
+ if (cancelCmd) return { action: "cancel_session" };
2204
+ const newCmd = CMD_NEW_RE.exec(text);
2205
+ if (newCmd) {
2206
+ return {
2207
+ action: "new_session",
2208
+ agent: newCmd[1] || void 0,
2209
+ workspace: newCmd[2] || void 0
2210
+ };
2211
+ }
2212
+ if (KW_CANCEL_RE.test(text)) return { action: "cancel_session" };
2213
+ if (KW_NEW_RE.test(text))
2214
+ return { action: "new_session", agent: void 0, workspace: void 0 };
2215
+ return null;
2216
+ }
2217
+ var ACTION_TTL_MS = 5 * 60 * 1e3;
2218
+ var actionMap = /* @__PURE__ */ new Map();
2219
+ function storeAction(action) {
2220
+ const id = nanoid4(10);
2221
+ actionMap.set(id, { action, createdAt: Date.now() });
2222
+ for (const [key, entry] of actionMap) {
2223
+ if (Date.now() - entry.createdAt > ACTION_TTL_MS) {
2224
+ actionMap.delete(key);
2225
+ }
2226
+ }
2227
+ return id;
2228
+ }
2229
+ function getAction(id) {
2230
+ const entry = actionMap.get(id);
2231
+ if (!entry) return void 0;
2232
+ if (Date.now() - entry.createdAt > ACTION_TTL_MS) {
2233
+ actionMap.delete(id);
2234
+ return void 0;
2235
+ }
2236
+ return entry.action;
2237
+ }
2238
+ function removeAction(id) {
2239
+ actionMap.delete(id);
2240
+ }
2241
+ function buildActionKeyboard(actionId, action) {
2242
+ const keyboard = new InlineKeyboard3();
2243
+ if (action.action === "new_session") {
2244
+ keyboard.text("\u2705 T\u1EA1o session", `a:${actionId}`);
2245
+ keyboard.text("\u274C Hu\u1EF7", `a:dismiss:${actionId}`);
2246
+ } else {
2247
+ keyboard.text("\u26D4 Hu\u1EF7 session", `a:${actionId}`);
2248
+ keyboard.text("\u274C Kh\xF4ng", `a:dismiss:${actionId}`);
2249
+ }
2250
+ return keyboard;
2251
+ }
2252
+ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
2253
+ bot.callbackQuery(/^a:dismiss:/, async (ctx) => {
2254
+ const actionId = ctx.callbackQuery.data.replace("a:dismiss:", "");
2255
+ removeAction(actionId);
2256
+ try {
2257
+ await ctx.editMessageReplyMarkup({
2258
+ reply_markup: { inline_keyboard: [] }
2259
+ });
2260
+ } catch {
2261
+ }
2262
+ await ctx.answerCallbackQuery({ text: "\u0110\xE3 hu\u1EF7" });
2263
+ });
2264
+ bot.callbackQuery(/^a:(?!dismiss)/, async (ctx) => {
2265
+ const actionId = ctx.callbackQuery.data.replace("a:", "");
2266
+ const action = getAction(actionId);
2267
+ if (!action) {
2268
+ await ctx.answerCallbackQuery({ text: "Action \u0111\xE3 h\u1EBFt h\u1EA1n" });
2269
+ return;
2270
+ }
2271
+ removeAction(actionId);
2272
+ try {
2273
+ if (action.action === "new_session") {
2274
+ await ctx.answerCallbackQuery({ text: "\u23F3 \u0110ang t\u1EA1o session..." });
2275
+ const { threadId } = await executeNewSession(
2276
+ bot,
2277
+ core,
2278
+ chatId,
2279
+ action.agent,
2280
+ action.workspace
2281
+ );
2282
+ const topicLink = `https://t.me/c/${String(chatId).replace("-100", "")}/${threadId}`;
2283
+ const originalText = ctx.callbackQuery.message?.text ?? "";
2284
+ try {
2285
+ await ctx.editMessageText(
2286
+ originalText + `
2287
+
2288
+ \u2705 Session created \u2192 <a href="${topicLink}">Go to topic</a>`,
2289
+ { parse_mode: "HTML" }
2290
+ );
2291
+ } catch {
2292
+ await ctx.editMessageReplyMarkup({
2293
+ reply_markup: { inline_keyboard: [] }
2294
+ });
2295
+ }
2296
+ } else if (action.action === "cancel_session") {
2297
+ const assistantId = getAssistantSessionId();
2298
+ const cancelled = await executeCancelSession(core, assistantId);
2299
+ if (cancelled) {
2300
+ await ctx.answerCallbackQuery({ text: "\u26D4 Session \u0111\xE3 hu\u1EF7" });
2301
+ const originalText = ctx.callbackQuery.message?.text ?? "";
2302
+ try {
2303
+ await ctx.editMessageText(
2304
+ originalText + `
2305
+
2306
+ \u26D4 Session "${cancelled.name ?? cancelled.id}" \u0111\xE3 hu\u1EF7`,
2307
+ { parse_mode: "HTML" }
2308
+ );
2309
+ } catch {
2310
+ await ctx.editMessageReplyMarkup({
2311
+ reply_markup: { inline_keyboard: [] }
2312
+ });
2313
+ }
2314
+ } else {
2315
+ await ctx.answerCallbackQuery({
2316
+ text: "Kh\xF4ng c\xF3 session n\xE0o \u0111ang ch\u1EA1y"
2317
+ });
2318
+ try {
2319
+ await ctx.editMessageReplyMarkup({
2320
+ reply_markup: { inline_keyboard: [] }
2321
+ });
2322
+ } catch {
2323
+ }
2324
+ }
2325
+ }
2326
+ } catch {
2327
+ await ctx.answerCallbackQuery({ text: "\u274C L\u1ED7i, th\u1EED l\u1EA1i sau" });
2328
+ try {
2329
+ await ctx.editMessageReplyMarkup({
2330
+ reply_markup: { inline_keyboard: [] }
2331
+ });
2332
+ } catch {
2333
+ }
2334
+ }
2335
+ });
2336
+ }
2337
+
1950
2338
  // src/adapters/telegram/adapter.ts
1951
- var log7 = createChildLogger({ module: "telegram" });
2339
+ var log8 = createChildLogger({ module: "telegram" });
1952
2340
  function patchedFetch(input, init) {
1953
2341
  if (init?.signal && !(init.signal instanceof AbortSignal)) {
1954
2342
  const nativeController = new AbortController();
@@ -1982,7 +2370,7 @@ var TelegramAdapter = class extends ChannelAdapter {
1982
2370
  this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
1983
2371
  this.bot.catch((err) => {
1984
2372
  const rootCause = err.error instanceof Error ? err.error : err;
1985
- log7.error({ err: rootCause }, "Telegram bot error");
2373
+ log8.error({ err: rootCause }, "Telegram bot error");
1986
2374
  });
1987
2375
  this.bot.api.config.use((prev, method, payload, signal) => {
1988
2376
  if (method === "getUpdates") {
@@ -2021,6 +2409,12 @@ var TelegramAdapter = class extends ChannelAdapter {
2021
2409
  (notification) => this.sendNotification(notification)
2022
2410
  );
2023
2411
  setupSkillCallbacks(this.bot, this.core);
2412
+ setupActionCallbacks(
2413
+ this.bot,
2414
+ this.core,
2415
+ this.telegramConfig.chatId,
2416
+ () => this.assistantSession?.id
2417
+ );
2024
2418
  setupMenuCallbacks(
2025
2419
  this.bot,
2026
2420
  this.core,
@@ -2035,7 +2429,7 @@ var TelegramAdapter = class extends ChannelAdapter {
2035
2429
  this.setupRoutes();
2036
2430
  this.bot.start({
2037
2431
  allowed_updates: ["message", "callback_query"],
2038
- onStart: () => log7.info(
2432
+ onStart: () => log8.info(
2039
2433
  { chatId: this.telegramConfig.chatId },
2040
2434
  "Telegram bot started"
2041
2435
  )
@@ -2047,7 +2441,7 @@ var TelegramAdapter = class extends ChannelAdapter {
2047
2441
  this.assistantTopicId
2048
2442
  );
2049
2443
  } catch (err) {
2050
- log7.error({ err }, "Failed to spawn assistant");
2444
+ log8.error({ err }, "Failed to spawn assistant");
2051
2445
  }
2052
2446
  try {
2053
2447
  const config = this.core.configManager.get();
@@ -2068,7 +2462,7 @@ Workspace: <code>${workspace}</code>
2068
2462
  reply_markup: buildMenuKeyboard()
2069
2463
  });
2070
2464
  } catch (err) {
2071
- log7.warn({ err }, "Failed to send welcome message");
2465
+ log8.warn({ err }, "Failed to send welcome message");
2072
2466
  }
2073
2467
  }
2074
2468
  async stop() {
@@ -2076,7 +2470,7 @@ Workspace: <code>${workspace}</code>
2076
2470
  await this.assistantSession.destroy();
2077
2471
  }
2078
2472
  await this.bot.stop();
2079
- log7.info("Telegram bot stopped");
2473
+ log8.info("Telegram bot stopped");
2080
2474
  }
2081
2475
  setupRoutes() {
2082
2476
  this.bot.on("message:text", async (ctx) => {
@@ -2094,7 +2488,7 @@ Workspace: <code>${workspace}</code>
2094
2488
  ctx.replyWithChatAction("typing").catch(() => {
2095
2489
  });
2096
2490
  handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
2097
- (err) => log7.error({ err }, "Assistant error")
2491
+ (err) => log8.error({ err }, "Assistant error")
2098
2492
  );
2099
2493
  return;
2100
2494
  }
@@ -2105,7 +2499,7 @@ Workspace: <code>${workspace}</code>
2105
2499
  threadId: String(threadId),
2106
2500
  userId: String(ctx.from.id),
2107
2501
  text: ctx.message.text
2108
- }).catch((err) => log7.error({ err }, "handleMessage error"));
2502
+ }).catch((err) => log8.error({ err }, "handleMessage error"));
2109
2503
  });
2110
2504
  }
2111
2505
  // --- ChannelAdapter implementations ---
@@ -2195,6 +2589,7 @@ Workspace: <code>${workspace}</code>
2195
2589
  break;
2196
2590
  }
2197
2591
  case "usage": {
2592
+ await this.finalizeDraft(sessionId);
2198
2593
  await this.bot.api.sendMessage(
2199
2594
  this.telegramConfig.chatId,
2200
2595
  formatUsage(
@@ -2240,7 +2635,7 @@ Workspace: <code>${workspace}</code>
2240
2635
  }
2241
2636
  }
2242
2637
  async sendPermissionRequest(sessionId, request) {
2243
- log7.info({ sessionId, requestId: request.id }, "Permission request sent");
2638
+ log8.info({ sessionId, requestId: request.id }, "Permission request sent");
2244
2639
  const session = this.core.sessionManager.getSession(
2245
2640
  sessionId
2246
2641
  );
@@ -2248,7 +2643,7 @@ Workspace: <code>${workspace}</code>
2248
2643
  await this.permissionHandler.sendPermissionRequest(session, request);
2249
2644
  }
2250
2645
  async sendNotification(notification) {
2251
- log7.info(
2646
+ log8.info(
2252
2647
  { sessionId: notification.sessionId, type: notification.type },
2253
2648
  "Notification sent"
2254
2649
  );
@@ -2274,7 +2669,7 @@ Workspace: <code>${workspace}</code>
2274
2669
  });
2275
2670
  }
2276
2671
  async createSessionThread(sessionId, name) {
2277
- log7.info({ sessionId, name }, "Session topic created");
2672
+ log8.info({ sessionId, name }, "Session topic created");
2278
2673
  return String(
2279
2674
  await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
2280
2675
  );
@@ -2338,7 +2733,7 @@ Workspace: <code>${workspace}</code>
2338
2733
  }
2339
2734
  );
2340
2735
  } catch (err) {
2341
- log7.error({ err, sessionId }, "Failed to send skill commands");
2736
+ log8.error({ err, sessionId }, "Failed to send skill commands");
2342
2737
  }
2343
2738
  await this.updateCommandAutocomplete(session.agentName, commands);
2344
2739
  }
@@ -2369,12 +2764,12 @@ Workspace: <code>${workspace}</code>
2369
2764
  await this.bot.api.setMyCommands(all, {
2370
2765
  scope: { type: "chat", chat_id: this.telegramConfig.chatId }
2371
2766
  });
2372
- log7.info(
2767
+ log8.info(
2373
2768
  { count: all.length, skills: validSkills.length },
2374
2769
  "Updated command autocomplete"
2375
2770
  );
2376
2771
  } catch (err) {
2377
- log7.error(
2772
+ log8.error(
2378
2773
  { err, commands: all },
2379
2774
  "Failed to update command autocomplete"
2380
2775
  );
@@ -2382,10 +2777,20 @@ Workspace: <code>${workspace}</code>
2382
2777
  }
2383
2778
  async finalizeDraft(sessionId) {
2384
2779
  const draft = this.sessionDrafts.get(sessionId);
2385
- if (draft) {
2386
- await draft.finalize();
2387
- this.sessionDrafts.delete(sessionId);
2780
+ if (!draft) return;
2781
+ let keyboard;
2782
+ if (sessionId === this.assistantSession?.id) {
2783
+ const fullText = draft.getBuffer();
2784
+ if (fullText) {
2785
+ const detected = detectAction(fullText);
2786
+ if (detected) {
2787
+ const actionId = storeAction(detected);
2788
+ keyboard = buildActionKeyboard(actionId, detected);
2789
+ }
2790
+ }
2388
2791
  }
2792
+ await draft.finalize(keyboard);
2793
+ this.sessionDrafts.delete(sessionId);
2389
2794
  }
2390
2795
  };
2391
2796
 
@@ -2400,6 +2805,7 @@ export {
2400
2805
  NotificationManager,
2401
2806
  OpenACPCore,
2402
2807
  ChannelAdapter,
2808
+ ApiServer,
2403
2809
  TelegramAdapter
2404
2810
  };
2405
- //# sourceMappingURL=chunk-EIBLQU3H.js.map
2811
+ //# sourceMappingURL=chunk-IX63F4JG.js.map