@openacp/cli 0.2.30 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/{chunk-CEAIK37M.js → chunk-7W5SOJPD.js} +2 -2
  2. package/dist/{chunk-7VEHVMVM.js → chunk-CA6FXPLH.js} +9 -8
  3. package/dist/chunk-CA6FXPLH.js.map +1 -0
  4. package/dist/{chunk-2QSUSMBI.js → chunk-LVSQQRCF.js} +2 -2
  5. package/dist/{chunk-ZMNBZAXN.js → chunk-NS2L445T.js} +4 -4
  6. package/dist/chunk-NS2L445T.js.map +1 -0
  7. package/dist/{chunk-WKHCVK5P.js → chunk-SWQRUVBW.js} +595 -106
  8. package/dist/chunk-SWQRUVBW.js.map +1 -0
  9. package/dist/{chunk-GP66XLS6.js → chunk-YXMRR2E3.js} +23 -3
  10. package/dist/chunk-YXMRR2E3.js.map +1 -0
  11. package/dist/cli.js +16 -16
  12. package/dist/{config-3EDZ3IMJ.js → config-2CBRLF3R.js} +2 -2
  13. package/dist/config-editor-UN56HQCW.js +11 -0
  14. package/dist/{daemon-TNQKCKTB.js → daemon-UXC7PB4P.js} +3 -3
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.js +6 -6
  17. package/dist/{main-CW4GRPSX.js → main-PLTMIVYL.js} +10 -10
  18. package/dist/{setup-JRNMPTIT.js → setup-UKWBLJIT.js} +3 -3
  19. package/package.json +1 -1
  20. package/dist/chunk-7VEHVMVM.js.map +0 -1
  21. package/dist/chunk-GP66XLS6.js.map +0 -1
  22. package/dist/chunk-WKHCVK5P.js.map +0 -1
  23. package/dist/chunk-ZMNBZAXN.js.map +0 -1
  24. package/dist/config-editor-KCD6FFC3.js +0 -11
  25. /package/dist/{chunk-CEAIK37M.js.map → chunk-7W5SOJPD.js.map} +0 -0
  26. /package/dist/{chunk-2QSUSMBI.js.map → chunk-LVSQQRCF.js.map} +0 -0
  27. /package/dist/{config-3EDZ3IMJ.js.map → config-2CBRLF3R.js.map} +0 -0
  28. /package/dist/{config-editor-KCD6FFC3.js.map → config-editor-UN56HQCW.js.map} +0 -0
  29. /package/dist/{daemon-TNQKCKTB.js.map → daemon-UXC7PB4P.js.map} +0 -0
  30. /package/dist/{main-CW4GRPSX.js.map → main-PLTMIVYL.js.map} +0 -0
  31. /package/dist/{setup-JRNMPTIT.js.map → setup-UKWBLJIT.js.map} +0 -0
@@ -523,6 +523,7 @@ var Session = class {
523
523
  adapter;
524
524
  // Set by wireSessionEvents for renaming
525
525
  pendingPermission;
526
+ dangerousMode = false;
526
527
  log;
527
528
  constructor(opts) {
528
529
  this.id = opts.id || nanoid(12);
@@ -593,7 +594,8 @@ var Session = class {
593
594
  async warmup() {
594
595
  this.promptRunning = true;
595
596
  const prevHandler = this.agentInstance.onSessionUpdate;
596
- this.agentInstance.onSessionUpdate = () => {
597
+ this.agentInstance.onSessionUpdate = (event) => {
598
+ if (event.type === "commands_update") prevHandler(event);
597
599
  };
598
600
  try {
599
601
  const start = Date.now();
@@ -612,6 +614,7 @@ var Session = class {
612
614
  }
613
615
  }
614
616
  async cancel() {
617
+ this.promptQueue = [];
615
618
  this.status = "cancelled";
616
619
  this.log.info("Session cancelled");
617
620
  await this.agentInstance.cancel();
@@ -666,6 +669,12 @@ var SessionManager = class {
666
669
  }
667
670
  return void 0;
668
671
  }
672
+ getRecordByThread(channelId, threadId) {
673
+ return this.store?.findByPlatform(
674
+ channelId,
675
+ (p) => String(p.topicId) === threadId
676
+ );
677
+ }
669
678
  registerSession(session) {
670
679
  this.sessions.set(session.id, session);
671
680
  }
@@ -707,11 +716,11 @@ var SessionManager = class {
707
716
  const session = this.sessions.get(sessionId);
708
717
  if (session) {
709
718
  await session.cancel();
710
- if (this.store) {
711
- const record = this.store.get(sessionId);
712
- if (record) {
713
- await this.store.save({ ...record, status: "cancelled" });
714
- }
719
+ }
720
+ if (this.store) {
721
+ const record = this.store.get(sessionId);
722
+ if (record && record.status !== "cancelled") {
723
+ await this.store.save({ ...record, status: "cancelled" });
715
724
  }
716
725
  }
717
726
  }
@@ -1044,9 +1053,9 @@ var OpenACPCore = class {
1044
1053
  );
1045
1054
  const adapter = this.adapters.get(message.channelId);
1046
1055
  if (adapter) {
1047
- await adapter.sendMessage("system", {
1056
+ await adapter.sendMessage(message.threadId, {
1048
1057
  type: "error",
1049
- text: `Max concurrent sessions (${config.security.maxConcurrentSessions}) reached. Cancel a session first.`
1058
+ text: `\u26A0\uFE0F Session limit reached (${config.security.maxConcurrentSessions}). Please cancel existing sessions with /cancel before starting new ones.`
1050
1059
  });
1051
1060
  }
1052
1061
  return;
@@ -1086,11 +1095,19 @@ var OpenACPCore = class {
1086
1095
  channelId,
1087
1096
  currentThreadId
1088
1097
  );
1089
- if (!currentSession) return null;
1098
+ if (currentSession) {
1099
+ return this.handleNewSession(
1100
+ channelId,
1101
+ currentSession.agentName,
1102
+ currentSession.workingDirectory
1103
+ );
1104
+ }
1105
+ const record = this.sessionManager.getRecordByThread(channelId, currentThreadId);
1106
+ if (!record || record.status === "cancelled" || record.status === "error") return null;
1090
1107
  return this.handleNewSession(
1091
1108
  channelId,
1092
- currentSession.agentName,
1093
- currentSession.workingDirectory
1109
+ record.agentName,
1110
+ record.workingDir
1094
1111
  );
1095
1112
  }
1096
1113
  // --- Lazy Resume ---
@@ -1609,20 +1626,23 @@ function formatViewerLinks(links, filePath) {
1609
1626
  \u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
1610
1627
  return text;
1611
1628
  }
1612
- function formatPlan(plan) {
1613
- const statusIcon = { pending: "\u2B1C", in_progress: "\u{1F504}", completed: "\u2705" };
1614
- const lines = plan.entries.map(
1615
- (e, i) => `${statusIcon[e.status] || "\u2B1C"} ${i + 1}. ${escapeHtml(e.content)}`
1616
- );
1617
- return `<b>Plan:</b>
1618
- ${lines.join("\n")}`;
1629
+ function formatTokens(n) {
1630
+ return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
1631
+ }
1632
+ function progressBar(ratio) {
1633
+ const filled = Math.round(Math.min(ratio, 1) * 10);
1634
+ return "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
1619
1635
  }
1620
1636
  function formatUsage(usage) {
1621
- const parts = [];
1622
- if (usage.tokensUsed != null) parts.push(`Tokens: ${usage.tokensUsed.toLocaleString()}`);
1623
- if (usage.contextSize != null) parts.push(`Context: ${usage.contextSize.toLocaleString()}`);
1624
- if (usage.cost) parts.push(`Cost: $${usage.cost.amount.toFixed(4)}`);
1625
- return `\u{1F4CA} ${parts.join(" | ")}`;
1637
+ const { tokensUsed, contextSize } = usage;
1638
+ if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
1639
+ if (contextSize == null) return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens`;
1640
+ const ratio = tokensUsed / contextSize;
1641
+ const pct = Math.round(ratio * 100);
1642
+ const bar = progressBar(ratio);
1643
+ const emoji = pct >= 85 ? "\u26A0\uFE0F" : "\u{1F4CA}";
1644
+ return `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
1645
+ ${bar} ${pct}%`;
1626
1646
  }
1627
1647
  function splitMessage(text, maxLength = 4096) {
1628
1648
  if (text.length <= maxLength) return [text];
@@ -1663,6 +1683,7 @@ var MessageDraft = class {
1663
1683
  flushPromise = Promise.resolve();
1664
1684
  lastSentBuffer = "";
1665
1685
  append(text) {
1686
+ if (!text) return;
1666
1687
  this.buffer += text;
1667
1688
  this.scheduleFlush();
1668
1689
  }
@@ -1709,7 +1730,6 @@ var MessageDraft = class {
1709
1730
  );
1710
1731
  this.lastSentBuffer = this.buffer;
1711
1732
  } catch {
1712
- this.messageId = void 0;
1713
1733
  }
1714
1734
  }
1715
1735
  }
@@ -1813,6 +1833,8 @@ function setupCommands(bot, core, chatId, assistant) {
1813
1833
  bot.command("agents", (ctx) => handleAgents(ctx, core));
1814
1834
  bot.command("help", (ctx) => handleHelp(ctx));
1815
1835
  bot.command("menu", (ctx) => handleMenu(ctx));
1836
+ bot.command("enable_dangerous", (ctx) => handleEnableDangerous(ctx, core));
1837
+ bot.command("disable_dangerous", (ctx) => handleDisableDangerous(ctx, core));
1816
1838
  }
1817
1839
  function buildMenuKeyboard() {
1818
1840
  return new InlineKeyboard().text("\u{1F195} New Session", "m:new").text("\u{1F4AC} New Chat", "m:new_chat").row().text("\u26D4 Cancel", "m:cancel").text("\u{1F4CA} Status", "m:status").row().text("\u{1F916} Agents", "m:agents").text("\u2753 Help", "m:help");
@@ -1898,7 +1920,8 @@ async function handleNew(ctx, core, chatId, assistant) {
1898
1920
  <b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
1899
1921
  {
1900
1922
  message_thread_id: threadId,
1901
- parse_mode: "HTML"
1923
+ parse_mode: "HTML",
1924
+ reply_markup: buildDangerousModeKeyboard(session.id, false)
1902
1925
  }
1903
1926
  );
1904
1927
  session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
@@ -1952,7 +1975,8 @@ async function handleNewChat(ctx, core, chatId) {
1952
1975
  <b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
1953
1976
  {
1954
1977
  message_thread_id: newThreadId,
1955
- parse_mode: "HTML"
1978
+ parse_mode: "HTML",
1979
+ reply_markup: buildDangerousModeKeyboard(session.id, false)
1956
1980
  }
1957
1981
  );
1958
1982
  session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
@@ -1981,6 +2005,13 @@ async function handleCancel(ctx, core, assistant) {
1981
2005
  log6.info({ sessionId: session.id }, "Cancel session command");
1982
2006
  await session.cancel();
1983
2007
  await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
2008
+ return;
2009
+ }
2010
+ const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
2011
+ if (record && record.status !== "cancelled" && record.status !== "error") {
2012
+ log6.info({ sessionId: record.sessionId }, "Cancel session command (from store)");
2013
+ await core.sessionManager.cancelSession(record.sessionId);
2014
+ await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
1984
2015
  }
1985
2016
  }
1986
2017
  async function handleStatus(ctx, core) {
@@ -2000,9 +2031,20 @@ async function handleStatus(ctx, core) {
2000
2031
  { parse_mode: "HTML" }
2001
2032
  );
2002
2033
  } else {
2003
- await ctx.reply("No active session in this topic.", {
2004
- parse_mode: "HTML"
2005
- });
2034
+ const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
2035
+ if (record) {
2036
+ await ctx.reply(
2037
+ `<b>Session:</b> ${escapeHtml(record.name || record.sessionId)}
2038
+ <b>Agent:</b> ${escapeHtml(record.agentName)}
2039
+ <b>Status:</b> ${escapeHtml(record.status)} (not loaded)
2040
+ <b>Workspace:</b> <code>${escapeHtml(record.workingDir)}</code>`,
2041
+ { parse_mode: "HTML" }
2042
+ );
2043
+ } else {
2044
+ await ctx.reply("No active session in this topic.", {
2045
+ parse_mode: "HTML"
2046
+ });
2047
+ }
2006
2048
  }
2007
2049
  } else {
2008
2050
  const sessions = core.sessionManager.listSessions("telegram");
@@ -2047,6 +2089,81 @@ Or just chat in the \u{1F916} Assistant topic for help!`,
2047
2089
  { parse_mode: "HTML" }
2048
2090
  );
2049
2091
  }
2092
+ function buildDangerousModeKeyboard(sessionId, enabled) {
2093
+ return new InlineKeyboard().text(
2094
+ enabled ? "\u{1F510} Disable Dangerous Mode" : "\u2620\uFE0F Enable Dangerous Mode",
2095
+ `d:${sessionId}`
2096
+ );
2097
+ }
2098
+ function setupDangerousModeCallbacks(bot, core) {
2099
+ bot.callbackQuery(/^d:/, async (ctx) => {
2100
+ const sessionId = ctx.callbackQuery.data.slice(2);
2101
+ const session = core.sessionManager.getSession(sessionId);
2102
+ if (!session) {
2103
+ try {
2104
+ await ctx.answerCallbackQuery({ text: "\u26A0\uFE0F Session not found or already ended." });
2105
+ } catch {
2106
+ }
2107
+ return;
2108
+ }
2109
+ session.dangerousMode = !session.dangerousMode;
2110
+ log6.info({ sessionId, dangerousMode: session.dangerousMode }, "Dangerous mode toggled via button");
2111
+ const toastText = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
2112
+ try {
2113
+ await ctx.answerCallbackQuery({ text: toastText });
2114
+ } catch {
2115
+ }
2116
+ try {
2117
+ await ctx.editMessageReplyMarkup({
2118
+ reply_markup: buildDangerousModeKeyboard(sessionId, session.dangerousMode)
2119
+ });
2120
+ } catch {
2121
+ }
2122
+ });
2123
+ }
2124
+ async function handleEnableDangerous(ctx, core) {
2125
+ const threadId = ctx.message?.message_thread_id;
2126
+ if (!threadId) {
2127
+ await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
2128
+ return;
2129
+ }
2130
+ const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
2131
+ if (!session) {
2132
+ await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
2133
+ return;
2134
+ }
2135
+ if (session.dangerousMode) {
2136
+ await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
2137
+ return;
2138
+ }
2139
+ session.dangerousMode = true;
2140
+ await ctx.reply(
2141
+ `\u26A0\uFE0F <b>Dangerous mode enabled</b>
2142
+
2143
+ All permission requests will be auto-approved. Claude can run arbitrary commands without asking.
2144
+
2145
+ Use /disable_dangerous to restore normal behaviour.`,
2146
+ { parse_mode: "HTML" }
2147
+ );
2148
+ }
2149
+ async function handleDisableDangerous(ctx, core) {
2150
+ const threadId = ctx.message?.message_thread_id;
2151
+ if (!threadId) {
2152
+ await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
2153
+ return;
2154
+ }
2155
+ const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
2156
+ if (!session) {
2157
+ await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
2158
+ return;
2159
+ }
2160
+ if (!session.dangerousMode) {
2161
+ await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
2162
+ return;
2163
+ }
2164
+ session.dangerousMode = false;
2165
+ await ctx.reply("\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.", { parse_mode: "HTML" });
2166
+ }
2050
2167
  function botFromCtx(ctx) {
2051
2168
  return { api: ctx.api };
2052
2169
  }
@@ -2088,10 +2205,11 @@ function setupSkillCallbacks(bot, core) {
2088
2205
  }
2089
2206
  async function executeNewSession(bot, core, chatId, agentName, workspace) {
2090
2207
  const threadId = await createSessionTopic(bot, chatId, "\u{1F504} New Session");
2091
- await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
2208
+ const setupMsg = await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
2092
2209
  message_thread_id: threadId,
2093
2210
  parse_mode: "HTML"
2094
2211
  });
2212
+ const firstMsgId = setupMsg.message_id;
2095
2213
  try {
2096
2214
  const session = await core.handleNewSession(
2097
2215
  "telegram",
@@ -2105,7 +2223,7 @@ async function executeNewSession(bot, core, chatId, agentName, workspace) {
2105
2223
  const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
2106
2224
  await renameSessionTopic(bot, chatId, threadId, finalName);
2107
2225
  session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
2108
- return { session, threadId };
2226
+ return { session, threadId, firstMsgId };
2109
2227
  } catch (err) {
2110
2228
  try {
2111
2229
  await bot.api.deleteForumTopic(chatId, threadId);
@@ -2128,7 +2246,9 @@ var STATIC_COMMANDS = [
2128
2246
  { command: "status", description: "Show status" },
2129
2247
  { command: "agents", description: "List available agents" },
2130
2248
  { command: "help", description: "Help" },
2131
- { command: "menu", description: "Show menu" }
2249
+ { command: "menu", description: "Show menu" },
2250
+ { command: "enable_dangerous", description: "Auto-approve all permission requests (session only)" },
2251
+ { command: "disable_dangerous", description: "Restore normal permission prompts (session only)" }
2132
2252
  ];
2133
2253
 
2134
2254
  // src/adapters/telegram/permissions.ts
@@ -2213,8 +2333,10 @@ ${escapeHtml(request.description)}`,
2213
2333
  };
2214
2334
 
2215
2335
  // src/adapters/telegram/assistant.ts
2336
+ var log8 = createChildLogger({ module: "telegram-assistant" });
2216
2337
  async function spawnAssistant(core, adapter, assistantTopicId) {
2217
2338
  const config = core.configManager.get();
2339
+ log8.info({ agent: config.defaultAgent }, "Creating assistant session...");
2218
2340
  const session = await core.sessionManager.createSession(
2219
2341
  "telegram",
2220
2342
  config.defaultAgent,
@@ -2222,10 +2344,16 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
2222
2344
  core.agentManager
2223
2345
  );
2224
2346
  session.threadId = String(assistantTopicId);
2225
- const systemPrompt = buildAssistantSystemPrompt(config);
2226
- await session.enqueuePrompt(systemPrompt);
2347
+ session.name = "Assistant";
2348
+ log8.info({ sessionId: session.id }, "Assistant agent spawned");
2227
2349
  core.wireSessionEvents(session, adapter);
2228
- return session;
2350
+ const systemPrompt = buildAssistantSystemPrompt(config);
2351
+ const ready = session.enqueuePrompt(systemPrompt).then(() => {
2352
+ log8.info({ sessionId: session.id }, "Assistant system prompt completed");
2353
+ }).catch((err) => {
2354
+ log8.warn({ err }, "Assistant system prompt failed");
2355
+ });
2356
+ return { session, ready };
2229
2357
  }
2230
2358
  function buildAssistantSystemPrompt(config) {
2231
2359
  const agentNames = Object.keys(config.agents).join(", ");
@@ -2260,6 +2388,286 @@ function redirectToAssistant(chatId, assistantTopicId) {
2260
2388
  return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
2261
2389
  }
2262
2390
 
2391
+ // src/adapters/telegram/activity.ts
2392
+ var log9 = createChildLogger({ module: "telegram:activity" });
2393
+ var THINKING_REFRESH_MS = 15e3;
2394
+ var THINKING_MAX_MS = 3 * 60 * 1e3;
2395
+ var ThinkingIndicator = class {
2396
+ constructor(api, chatId, threadId, sendQueue) {
2397
+ this.api = api;
2398
+ this.chatId = chatId;
2399
+ this.threadId = threadId;
2400
+ this.sendQueue = sendQueue;
2401
+ }
2402
+ msgId;
2403
+ sending = false;
2404
+ dismissed = false;
2405
+ refreshTimer;
2406
+ showTime = 0;
2407
+ async show() {
2408
+ if (this.msgId || this.sending || this.dismissed) return;
2409
+ this.sending = true;
2410
+ this.showTime = Date.now();
2411
+ try {
2412
+ const result = await this.sendQueue.enqueue(
2413
+ () => this.api.sendMessage(this.chatId, "\u{1F4AD} <i>Thinking...</i>", {
2414
+ message_thread_id: this.threadId,
2415
+ parse_mode: "HTML",
2416
+ disable_notification: true
2417
+ })
2418
+ );
2419
+ if (result && !this.dismissed) {
2420
+ this.msgId = result.message_id;
2421
+ this.startRefreshTimer();
2422
+ }
2423
+ } catch (err) {
2424
+ log9.warn({ err }, "ThinkingIndicator.show() failed");
2425
+ } finally {
2426
+ this.sending = false;
2427
+ }
2428
+ }
2429
+ /** Clear state — stops refresh timer, no Telegram API call */
2430
+ dismiss() {
2431
+ this.dismissed = true;
2432
+ this.msgId = void 0;
2433
+ this.stopRefreshTimer();
2434
+ }
2435
+ /** Reset for a new prompt cycle */
2436
+ reset() {
2437
+ this.dismissed = false;
2438
+ }
2439
+ startRefreshTimer() {
2440
+ this.stopRefreshTimer();
2441
+ this.refreshTimer = setInterval(() => {
2442
+ if (this.dismissed || !this.msgId || Date.now() - this.showTime >= THINKING_MAX_MS) {
2443
+ this.stopRefreshTimer();
2444
+ return;
2445
+ }
2446
+ const elapsed = Math.round((Date.now() - this.showTime) / 1e3);
2447
+ this.sendQueue.enqueue(() => {
2448
+ if (this.dismissed) return Promise.resolve(void 0);
2449
+ return this.api.sendMessage(this.chatId, `\u{1F4AD} <i>Still thinking... (${elapsed}s)</i>`, {
2450
+ message_thread_id: this.threadId,
2451
+ parse_mode: "HTML",
2452
+ disable_notification: true
2453
+ });
2454
+ }).then((result) => {
2455
+ if (result && !this.dismissed) {
2456
+ this.msgId = result.message_id;
2457
+ }
2458
+ }).catch(() => {
2459
+ });
2460
+ }, THINKING_REFRESH_MS);
2461
+ }
2462
+ stopRefreshTimer() {
2463
+ if (this.refreshTimer) {
2464
+ clearInterval(this.refreshTimer);
2465
+ this.refreshTimer = void 0;
2466
+ }
2467
+ }
2468
+ };
2469
+ var UsageMessage = class {
2470
+ constructor(api, chatId, threadId, sendQueue) {
2471
+ this.api = api;
2472
+ this.chatId = chatId;
2473
+ this.threadId = threadId;
2474
+ this.sendQueue = sendQueue;
2475
+ }
2476
+ msgId;
2477
+ async send(usage) {
2478
+ const text = formatUsage(usage);
2479
+ try {
2480
+ if (this.msgId) {
2481
+ await this.sendQueue.enqueue(
2482
+ () => this.api.editMessageText(this.chatId, this.msgId, text, {
2483
+ parse_mode: "HTML"
2484
+ })
2485
+ );
2486
+ } else {
2487
+ const result = await this.sendQueue.enqueue(
2488
+ () => this.api.sendMessage(this.chatId, text, {
2489
+ message_thread_id: this.threadId,
2490
+ parse_mode: "HTML",
2491
+ disable_notification: true
2492
+ })
2493
+ );
2494
+ if (result) this.msgId = result.message_id;
2495
+ }
2496
+ } catch (err) {
2497
+ log9.warn({ err }, "UsageMessage.send() failed");
2498
+ }
2499
+ }
2500
+ getMsgId() {
2501
+ return this.msgId;
2502
+ }
2503
+ async delete() {
2504
+ if (!this.msgId) return;
2505
+ const id = this.msgId;
2506
+ this.msgId = void 0;
2507
+ try {
2508
+ await this.sendQueue.enqueue(() => this.api.deleteMessage(this.chatId, id));
2509
+ } catch (err) {
2510
+ log9.warn({ err }, "UsageMessage.delete() failed");
2511
+ }
2512
+ }
2513
+ };
2514
+ function formatPlanCard(entries) {
2515
+ const statusIcon = {
2516
+ completed: "\u2705",
2517
+ in_progress: "\u{1F504}",
2518
+ pending: "\u2B1C",
2519
+ failed: "\u274C"
2520
+ };
2521
+ const total = entries.length;
2522
+ const done = entries.filter((e) => e.status === "completed").length;
2523
+ const ratio = total > 0 ? done / total : 0;
2524
+ const filled = Math.round(ratio * 10);
2525
+ const bar = "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
2526
+ const pct = Math.round(ratio * 100);
2527
+ const header = `\u{1F4CB} <b>Plan</b>
2528
+ ${bar} ${pct}% \xB7 ${done}/${total}`;
2529
+ const lines = entries.map((e, i) => {
2530
+ const icon = statusIcon[e.status] ?? "\u2B1C";
2531
+ return `${icon} ${i + 1}. ${e.content}`;
2532
+ });
2533
+ return [header, ...lines].join("\n");
2534
+ }
2535
+ var PlanCard = class {
2536
+ constructor(api, chatId, threadId, sendQueue) {
2537
+ this.api = api;
2538
+ this.chatId = chatId;
2539
+ this.threadId = threadId;
2540
+ this.sendQueue = sendQueue;
2541
+ }
2542
+ msgId;
2543
+ flushPromise = Promise.resolve();
2544
+ latestEntries;
2545
+ flushTimer;
2546
+ update(entries) {
2547
+ this.latestEntries = entries;
2548
+ if (this.flushTimer) clearTimeout(this.flushTimer);
2549
+ this.flushTimer = setTimeout(() => {
2550
+ this.flushTimer = void 0;
2551
+ this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
2552
+ });
2553
+ }, 3500);
2554
+ }
2555
+ async finalize() {
2556
+ if (!this.latestEntries) return;
2557
+ if (this.flushTimer) {
2558
+ clearTimeout(this.flushTimer);
2559
+ this.flushTimer = void 0;
2560
+ }
2561
+ await this.flushPromise;
2562
+ this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
2563
+ });
2564
+ await this.flushPromise;
2565
+ }
2566
+ destroy() {
2567
+ if (this.flushTimer) {
2568
+ clearTimeout(this.flushTimer);
2569
+ this.flushTimer = void 0;
2570
+ }
2571
+ }
2572
+ async _flush() {
2573
+ if (!this.latestEntries) return;
2574
+ const text = formatPlanCard(this.latestEntries);
2575
+ try {
2576
+ if (this.msgId) {
2577
+ await this.sendQueue.enqueue(
2578
+ () => this.api.editMessageText(this.chatId, this.msgId, text, {
2579
+ parse_mode: "HTML"
2580
+ })
2581
+ );
2582
+ } else {
2583
+ const result = await this.sendQueue.enqueue(
2584
+ () => this.api.sendMessage(this.chatId, text, {
2585
+ message_thread_id: this.threadId,
2586
+ parse_mode: "HTML",
2587
+ disable_notification: true
2588
+ })
2589
+ );
2590
+ if (result) this.msgId = result.message_id;
2591
+ }
2592
+ } catch (err) {
2593
+ log9.warn({ err }, "PlanCard flush failed");
2594
+ }
2595
+ }
2596
+ };
2597
+ var ActivityTracker = class {
2598
+ constructor(api, chatId, threadId, sendQueue) {
2599
+ this.api = api;
2600
+ this.chatId = chatId;
2601
+ this.threadId = threadId;
2602
+ this.sendQueue = sendQueue;
2603
+ this.thinking = new ThinkingIndicator(api, chatId, threadId, sendQueue);
2604
+ this.planCard = new PlanCard(api, chatId, threadId, sendQueue);
2605
+ this.usage = new UsageMessage(api, chatId, threadId, sendQueue);
2606
+ }
2607
+ isFirstEvent = true;
2608
+ hasPlanCard = false;
2609
+ thinking;
2610
+ planCard;
2611
+ usage;
2612
+ async onNewPrompt() {
2613
+ this.isFirstEvent = true;
2614
+ this.hasPlanCard = false;
2615
+ this.thinking.dismiss();
2616
+ this.thinking.reset();
2617
+ }
2618
+ async onThought() {
2619
+ await this._firstEventGuard();
2620
+ await this.thinking.show();
2621
+ }
2622
+ async onPlan(entries) {
2623
+ await this._firstEventGuard();
2624
+ this.thinking.dismiss();
2625
+ this.hasPlanCard = true;
2626
+ this.planCard.update(entries);
2627
+ }
2628
+ async onToolCall() {
2629
+ await this._firstEventGuard();
2630
+ this.thinking.dismiss();
2631
+ this.thinking.reset();
2632
+ }
2633
+ async onTextStart() {
2634
+ await this._firstEventGuard();
2635
+ this.thinking.dismiss();
2636
+ }
2637
+ async sendUsage(data) {
2638
+ await this.usage.send(data);
2639
+ }
2640
+ getUsageMsgId() {
2641
+ return this.usage.getMsgId();
2642
+ }
2643
+ async onComplete() {
2644
+ if (this.hasPlanCard) {
2645
+ await this.planCard.finalize();
2646
+ } else {
2647
+ try {
2648
+ await this.sendQueue.enqueue(
2649
+ () => this.api.sendMessage(this.chatId, "\u2705 <b>Done</b>", {
2650
+ message_thread_id: this.threadId,
2651
+ parse_mode: "HTML",
2652
+ disable_notification: true
2653
+ })
2654
+ );
2655
+ } catch (err) {
2656
+ log9.warn({ err }, "ActivityTracker.onComplete() Done send failed");
2657
+ }
2658
+ }
2659
+ }
2660
+ destroy() {
2661
+ this.thinking.dismiss();
2662
+ this.planCard.destroy();
2663
+ }
2664
+ async _firstEventGuard() {
2665
+ if (!this.isFirstEvent) return;
2666
+ this.isFirstEvent = false;
2667
+ await this.usage.delete();
2668
+ }
2669
+ };
2670
+
2263
2671
  // src/adapters/telegram/send-queue.ts
2264
2672
  var TelegramSendQueue = class {
2265
2673
  items = [];
@@ -2408,14 +2816,14 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
2408
2816
  try {
2409
2817
  if (action.action === "new_session") {
2410
2818
  await ctx.answerCallbackQuery({ text: "\u23F3 Creating session..." });
2411
- const { threadId } = await executeNewSession(
2819
+ const { threadId, firstMsgId } = await executeNewSession(
2412
2820
  bot,
2413
2821
  core,
2414
2822
  chatId,
2415
2823
  action.agent,
2416
2824
  action.workspace
2417
2825
  );
2418
- const topicLink = `https://t.me/c/${String(chatId).replace("-100", "")}/${threadId}`;
2826
+ const topicLink = `https://t.me/c/${String(chatId).replace("-100", "")}/${firstMsgId ?? threadId}`;
2419
2827
  const originalText = ctx.callbackQuery.message?.text ?? "";
2420
2828
  try {
2421
2829
  await ctx.editMessageText(
@@ -2472,7 +2880,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
2472
2880
  }
2473
2881
 
2474
2882
  // src/adapters/telegram/adapter.ts
2475
- var log8 = createChildLogger({ module: "telegram" });
2883
+ var log10 = createChildLogger({ module: "telegram" });
2476
2884
  function patchedFetch(input, init) {
2477
2885
  if (init?.signal && !(init.signal instanceof AbortSignal)) {
2478
2886
  const nativeController = new AbortController();
@@ -2495,11 +2903,26 @@ var TelegramAdapter = class extends ChannelAdapter {
2495
2903
  // sessionId → (toolCallId → state)
2496
2904
  permissionHandler;
2497
2905
  assistantSession = null;
2906
+ assistantInitializing = false;
2498
2907
  notificationTopicId;
2499
2908
  assistantTopicId;
2500
2909
  skillMessages = /* @__PURE__ */ new Map();
2501
2910
  // sessionId → pinned messageId
2502
2911
  sendQueue = new TelegramSendQueue(3e3);
2912
+ sessionTrackers = /* @__PURE__ */ new Map();
2913
+ getOrCreateTracker(sessionId, threadId) {
2914
+ let tracker = this.sessionTrackers.get(sessionId);
2915
+ if (!tracker) {
2916
+ tracker = new ActivityTracker(
2917
+ this.bot.api,
2918
+ this.telegramConfig.chatId,
2919
+ threadId,
2920
+ this.sendQueue
2921
+ );
2922
+ this.sessionTrackers.set(sessionId, tracker);
2923
+ }
2924
+ return tracker;
2925
+ }
2503
2926
  constructor(core, config) {
2504
2927
  super(core, config);
2505
2928
  this.telegramConfig = config;
@@ -2508,7 +2931,7 @@ var TelegramAdapter = class extends ChannelAdapter {
2508
2931
  this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
2509
2932
  this.bot.catch((err) => {
2510
2933
  const rootCause = err.error instanceof Error ? err.error : err;
2511
- log8.error({ err: rootCause }, "Telegram bot error");
2934
+ log10.error({ err: rootCause }, "Telegram bot error");
2512
2935
  });
2513
2936
  this.bot.api.config.use(async (prev, method, payload, signal) => {
2514
2937
  const maxRetries = 3;
@@ -2522,7 +2945,7 @@ var TelegramAdapter = class extends ChannelAdapter {
2522
2945
  if (rateLimitedMethods.includes(method)) {
2523
2946
  this.sendQueue.onRateLimited();
2524
2947
  }
2525
- log8.warn(
2948
+ log10.warn(
2526
2949
  { method, retryAfter, attempt: attempt + 1 },
2527
2950
  "Rate limited by Telegram, retrying"
2528
2951
  );
@@ -2567,6 +2990,7 @@ var TelegramAdapter = class extends ChannelAdapter {
2567
2990
  (notification) => this.sendNotification(notification)
2568
2991
  );
2569
2992
  setupSkillCallbacks(this.bot, this.core);
2993
+ setupDangerousModeCallbacks(this.bot, this.core);
2570
2994
  setupActionCallbacks(
2571
2995
  this.bot,
2572
2996
  this.core,
@@ -2591,26 +3015,15 @@ var TelegramAdapter = class extends ChannelAdapter {
2591
3015
  this.setupRoutes();
2592
3016
  this.bot.start({
2593
3017
  allowed_updates: ["message", "callback_query"],
2594
- onStart: () => log8.info(
3018
+ onStart: () => log10.info(
2595
3019
  { chatId: this.telegramConfig.chatId },
2596
3020
  "Telegram bot started"
2597
3021
  )
2598
3022
  });
2599
- try {
2600
- this.assistantSession = await spawnAssistant(
2601
- this.core,
2602
- this,
2603
- this.assistantTopicId
2604
- );
2605
- } catch (err) {
2606
- log8.error({ err }, "Failed to spawn assistant");
2607
- }
2608
3023
  try {
2609
3024
  const config = this.core.configManager.get();
2610
3025
  const agents = this.core.agentManager.getAvailableAgents();
2611
- const agentList = agents.map(
2612
- (a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`
2613
- ).join(", ");
3026
+ const agentList = agents.map((a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`).join(", ");
2614
3027
  const workspace = escapeHtml(config.workspace.baseDir);
2615
3028
  const welcomeText = `\u{1F44B} <b>OpenACP Assistant</b> is online.
2616
3029
 
@@ -2624,7 +3037,32 @@ Workspace: <code>${workspace}</code>
2624
3037
  reply_markup: buildMenuKeyboard()
2625
3038
  });
2626
3039
  } catch (err) {
2627
- log8.warn({ err }, "Failed to send welcome message");
3040
+ log10.warn({ err }, "Failed to send welcome message");
3041
+ }
3042
+ try {
3043
+ log10.info("Spawning assistant session...");
3044
+ const { session, ready } = await spawnAssistant(
3045
+ this.core,
3046
+ this,
3047
+ this.assistantTopicId
3048
+ );
3049
+ this.assistantSession = session;
3050
+ this.assistantInitializing = true;
3051
+ log10.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
3052
+ ready.then(() => {
3053
+ this.assistantInitializing = false;
3054
+ log10.info({ sessionId: session.id }, "Assistant ready for user messages");
3055
+ });
3056
+ } catch (err) {
3057
+ log10.error({ err }, "Failed to spawn assistant");
3058
+ this.bot.api.sendMessage(
3059
+ this.telegramConfig.chatId,
3060
+ `\u26A0\uFE0F <b>Failed to start assistant session.</b>
3061
+
3062
+ <code>${err instanceof Error ? err.message : String(err)}</code>`,
3063
+ { message_thread_id: this.assistantTopicId, parse_mode: "HTML" }
3064
+ ).catch(() => {
3065
+ });
2628
3066
  }
2629
3067
  }
2630
3068
  async stop() {
@@ -2632,7 +3070,7 @@ Workspace: <code>${workspace}</code>
2632
3070
  await this.assistantSession.destroy();
2633
3071
  }
2634
3072
  await this.bot.stop();
2635
- log8.info("Telegram bot stopped");
3073
+ log10.info("Telegram bot stopped");
2636
3074
  }
2637
3075
  setupRoutes() {
2638
3076
  this.bot.on("message:text", async (ctx) => {
@@ -2647,16 +3085,24 @@ Workspace: <code>${workspace}</code>
2647
3085
  }
2648
3086
  if (threadId === this.notificationTopicId) return;
2649
3087
  if (threadId === this.assistantTopicId) {
3088
+ if (!this.assistantSession) {
3089
+ await ctx.reply("\u26A0\uFE0F Assistant is not available yet. Please try again shortly.", { parse_mode: "HTML" });
3090
+ return;
3091
+ }
2650
3092
  await this.finalizeDraft(this.assistantSession.id);
2651
3093
  ctx.replyWithChatAction("typing").catch(() => {
2652
3094
  });
2653
3095
  handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
2654
- (err) => log8.error({ err }, "Assistant error")
3096
+ (err) => log10.error({ err }, "Assistant error")
2655
3097
  );
2656
3098
  return;
2657
3099
  }
2658
3100
  const sessionId = this.core.sessionManager.getSessionByThread("telegram", String(threadId))?.id;
2659
3101
  if (sessionId) await this.finalizeDraft(sessionId);
3102
+ if (sessionId) {
3103
+ const tracker = this.sessionTrackers.get(sessionId);
3104
+ if (tracker) await tracker.onNewPrompt();
3105
+ }
2660
3106
  ctx.replyWithChatAction("typing").catch(() => {
2661
3107
  });
2662
3108
  this.core.handleMessage({
@@ -2664,11 +3110,12 @@ Workspace: <code>${workspace}</code>
2664
3110
  threadId: String(threadId),
2665
3111
  userId: String(ctx.from.id),
2666
3112
  text: ctx.message.text
2667
- }).catch((err) => log8.error({ err }, "handleMessage error"));
3113
+ }).catch((err) => log10.error({ err }, "handleMessage error"));
2668
3114
  });
2669
3115
  }
2670
3116
  // --- ChannelAdapter implementations ---
2671
3117
  async sendMessage(sessionId, content) {
3118
+ if (this.assistantInitializing && sessionId === this.assistantSession?.id) return;
2672
3119
  const session = this.core.sessionManager.getSession(
2673
3120
  sessionId
2674
3121
  );
@@ -2676,11 +3123,15 @@ Workspace: <code>${workspace}</code>
2676
3123
  const threadId = Number(session.threadId);
2677
3124
  switch (content.type) {
2678
3125
  case "thought": {
3126
+ const tracker = this.getOrCreateTracker(sessionId, threadId);
3127
+ await tracker.onThought();
2679
3128
  break;
2680
3129
  }
2681
3130
  case "text": {
2682
3131
  let draft = this.sessionDrafts.get(sessionId);
2683
3132
  if (!draft) {
3133
+ const tracker = this.getOrCreateTracker(sessionId, threadId);
3134
+ await tracker.onTextStart();
2684
3135
  draft = new MessageDraft(
2685
3136
  this.bot,
2686
3137
  this.telegramConfig.chatId,
@@ -2698,6 +3149,8 @@ Workspace: <code>${workspace}</code>
2698
3149
  break;
2699
3150
  }
2700
3151
  case "tool_call": {
3152
+ const tracker = this.getOrCreateTracker(sessionId, threadId);
3153
+ await tracker.onToolCall();
2701
3154
  await this.finalizeDraft(sessionId);
2702
3155
  const meta = content.metadata;
2703
3156
  if (!this.toolCallMessages.has(sessionId)) {
@@ -2737,7 +3190,7 @@ Workspace: <code>${workspace}</code>
2737
3190
  if (toolState) {
2738
3191
  if (meta.viewerLinks) {
2739
3192
  toolState.viewerLinks = meta.viewerLinks;
2740
- log8.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
3193
+ log10.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
2741
3194
  }
2742
3195
  const viewerFilePath = content.metadata?.viewerFilePath;
2743
3196
  if (viewerFilePath) toolState.viewerFilePath = viewerFilePath;
@@ -2746,7 +3199,7 @@ Workspace: <code>${workspace}</code>
2746
3199
  const isTerminal = meta.status === "completed" || meta.status === "failed";
2747
3200
  if (!isTerminal && !meta.viewerLinks) break;
2748
3201
  await toolState.ready;
2749
- log8.debug(
3202
+ log10.debug(
2750
3203
  { toolId: meta.id, status: meta.status, hasViewerLinks: !!toolState.viewerLinks, viewerLinks: toolState.viewerLinks, name: toolState.name, msgId: toolState.msgId },
2751
3204
  "Tool completed, preparing edit"
2752
3205
  );
@@ -2768,7 +3221,7 @@ Workspace: <code>${workspace}</code>
2768
3221
  )
2769
3222
  );
2770
3223
  } catch (err) {
2771
- log8.warn(
3224
+ log10.warn(
2772
3225
  { err, msgId: toolState.msgId, textLen: formattedText.length, hasViewerLinks: !!merged.viewerLinks },
2773
3226
  "Tool update edit failed"
2774
3227
  );
@@ -2777,37 +3230,42 @@ Workspace: <code>${workspace}</code>
2777
3230
  break;
2778
3231
  }
2779
3232
  case "plan": {
2780
- await this.finalizeDraft(sessionId);
2781
- await this.sendQueue.enqueue(
2782
- () => this.bot.api.sendMessage(
2783
- this.telegramConfig.chatId,
2784
- formatPlan(
2785
- content.metadata
2786
- ),
2787
- {
2788
- message_thread_id: threadId,
2789
- parse_mode: "HTML",
2790
- disable_notification: true
2791
- }
2792
- )
3233
+ const meta = content.metadata;
3234
+ const tracker = this.getOrCreateTracker(sessionId, threadId);
3235
+ await tracker.onPlan(
3236
+ meta.entries.map((e) => ({
3237
+ content: e.content,
3238
+ status: e.status,
3239
+ priority: e.priority ?? "medium"
3240
+ }))
2793
3241
  );
2794
3242
  break;
2795
3243
  }
2796
3244
  case "usage": {
3245
+ const meta = content.metadata;
2797
3246
  await this.finalizeDraft(sessionId);
2798
- await this.sendQueue.enqueue(
2799
- () => this.bot.api.sendMessage(
2800
- this.telegramConfig.chatId,
2801
- formatUsage(
2802
- content.metadata
2803
- ),
2804
- {
2805
- message_thread_id: threadId,
3247
+ const tracker = this.getOrCreateTracker(sessionId, threadId);
3248
+ await tracker.sendUsage(meta);
3249
+ if (this.notificationTopicId && sessionId !== this.assistantSession?.id) {
3250
+ const sess = this.core.sessionManager.getSession(sessionId);
3251
+ const sessionName = sess?.name || "Session";
3252
+ const chatIdStr = String(this.telegramConfig.chatId);
3253
+ const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
3254
+ const usageMsgId = tracker.getUsageMsgId();
3255
+ const deepLink = `https://t.me/c/${numericId}/${usageMsgId ?? threadId}`;
3256
+ const text = `\u2705 <b>${escapeHtml(sessionName)}</b>
3257
+ Task completed.
3258
+
3259
+ <a href="${deepLink}">\u2192 Go to topic</a>`;
3260
+ this.sendQueue.enqueue(
3261
+ () => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
3262
+ message_thread_id: this.notificationTopicId,
2806
3263
  parse_mode: "HTML",
2807
- disable_notification: true
2808
- }
2809
- )
2810
- );
3264
+ disable_notification: false
3265
+ })
3266
+ ).catch(() => {
3267
+ });
3268
+ }
2811
3269
  break;
2812
3270
  }
2813
3271
  case "session_end": {
@@ -2815,21 +3273,33 @@ Workspace: <code>${workspace}</code>
2815
3273
  this.sessionDrafts.delete(sessionId);
2816
3274
  this.toolCallMessages.delete(sessionId);
2817
3275
  await this.cleanupSkillCommands(sessionId);
2818
- await this.sendQueue.enqueue(
2819
- () => this.bot.api.sendMessage(
2820
- this.telegramConfig.chatId,
2821
- `\u2705 <b>Done</b>`,
2822
- {
2823
- message_thread_id: threadId,
2824
- parse_mode: "HTML",
2825
- disable_notification: true
2826
- }
2827
- )
2828
- );
3276
+ const tracker = this.sessionTrackers.get(sessionId);
3277
+ if (tracker) {
3278
+ await tracker.onComplete();
3279
+ tracker.destroy();
3280
+ this.sessionTrackers.delete(sessionId);
3281
+ } else {
3282
+ await this.sendQueue.enqueue(
3283
+ () => this.bot.api.sendMessage(
3284
+ this.telegramConfig.chatId,
3285
+ `\u2705 <b>Done</b>`,
3286
+ {
3287
+ message_thread_id: threadId,
3288
+ parse_mode: "HTML",
3289
+ disable_notification: true
3290
+ }
3291
+ )
3292
+ );
3293
+ }
2829
3294
  break;
2830
3295
  }
2831
3296
  case "error": {
2832
3297
  await this.finalizeDraft(sessionId);
3298
+ const tracker = this.sessionTrackers.get(sessionId);
3299
+ if (tracker) {
3300
+ tracker.destroy();
3301
+ this.sessionTrackers.delete(sessionId);
3302
+ }
2833
3303
  await this.sendQueue.enqueue(
2834
3304
  () => this.bot.api.sendMessage(
2835
3305
  this.telegramConfig.chatId,
@@ -2846,17 +3316,27 @@ Workspace: <code>${workspace}</code>
2846
3316
  }
2847
3317
  }
2848
3318
  async sendPermissionRequest(sessionId, request) {
2849
- log8.info({ sessionId, requestId: request.id }, "Permission request sent");
3319
+ log10.info({ sessionId, requestId: request.id }, "Permission request sent");
2850
3320
  const session = this.core.sessionManager.getSession(
2851
3321
  sessionId
2852
3322
  );
2853
3323
  if (!session) return;
3324
+ if (session.dangerousMode) {
3325
+ const allowOption = request.options.find((o) => o.isAllow);
3326
+ if (allowOption && session.pendingPermission?.requestId === request.id) {
3327
+ log10.info({ sessionId, requestId: request.id, optionId: allowOption.id }, "Dangerous mode: auto-approving permission");
3328
+ session.pendingPermission.resolve(allowOption.id);
3329
+ session.pendingPermission = void 0;
3330
+ }
3331
+ return;
3332
+ }
2854
3333
  await this.sendQueue.enqueue(
2855
3334
  () => this.permissionHandler.sendPermissionRequest(session, request)
2856
3335
  );
2857
3336
  }
2858
3337
  async sendNotification(notification) {
2859
- log8.info(
3338
+ if (notification.sessionId === this.assistantSession?.id) return;
3339
+ log10.info(
2860
3340
  { sessionId: notification.sessionId, type: notification.type },
2861
3341
  "Notification sent"
2862
3342
  );
@@ -2870,10 +3350,18 @@ Workspace: <code>${workspace}</code>
2870
3350
  let text = `${emoji[notification.type] || "\u2139\uFE0F"} <b>${escapeHtml(notification.sessionName || "New session")}</b>
2871
3351
  `;
2872
3352
  text += escapeHtml(notification.summary);
2873
- if (notification.deepLink) {
3353
+ const deepLink = notification.deepLink ?? (() => {
3354
+ const session = this.core.sessionManager.getSession(notification.sessionId);
3355
+ const threadId = session?.threadId;
3356
+ if (!threadId) return void 0;
3357
+ const chatIdStr = String(this.telegramConfig.chatId);
3358
+ const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
3359
+ return `https://t.me/c/${numericId}/${threadId}`;
3360
+ })();
3361
+ if (deepLink) {
2874
3362
  text += `
2875
3363
 
2876
- <a href="${notification.deepLink}">\u2192 Go to message</a>`;
3364
+ <a href="${deepLink}">\u2192 Go to topic</a>`;
2877
3365
  }
2878
3366
  await this.sendQueue.enqueue(
2879
3367
  () => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
@@ -2884,7 +3372,7 @@ Workspace: <code>${workspace}</code>
2884
3372
  );
2885
3373
  }
2886
3374
  async createSessionThread(sessionId, name) {
2887
- log8.info({ sessionId, name }, "Session topic created");
3375
+ log10.info({ sessionId, name }, "Session topic created");
2888
3376
  return String(
2889
3377
  await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
2890
3378
  );
@@ -2906,6 +3394,7 @@ Workspace: <code>${workspace}</code>
2906
3394
  );
2907
3395
  }
2908
3396
  async sendSkillCommands(sessionId, commands) {
3397
+ if (sessionId === this.assistantSession?.id) return;
2909
3398
  const session = this.core.sessionManager.getSession(
2910
3399
  sessionId
2911
3400
  );
@@ -2968,7 +3457,7 @@ Workspace: <code>${workspace}</code>
2968
3457
  }
2969
3458
  );
2970
3459
  } catch (err) {
2971
- log8.error({ err, sessionId }, "Failed to send skill commands");
3460
+ log10.error({ err, sessionId }, "Failed to send skill commands");
2972
3461
  }
2973
3462
  await this.updateCommandAutocomplete(session.agentName, commands);
2974
3463
  }
@@ -3004,12 +3493,12 @@ Workspace: <code>${workspace}</code>
3004
3493
  await this.bot.api.setMyCommands(all, {
3005
3494
  scope: { type: "chat", chat_id: this.telegramConfig.chatId }
3006
3495
  });
3007
- log8.info(
3496
+ log10.info(
3008
3497
  { count: all.length, skills: validSkills.length },
3009
3498
  "Updated command autocomplete"
3010
3499
  );
3011
3500
  } catch (err) {
3012
- log8.error(
3501
+ log10.error(
3013
3502
  { err, commands: all },
3014
3503
  "Failed to update command autocomplete"
3015
3504
  );
@@ -3018,8 +3507,8 @@ Workspace: <code>${workspace}</code>
3018
3507
  async finalizeDraft(sessionId) {
3019
3508
  const draft = this.sessionDrafts.get(sessionId);
3020
3509
  if (!draft) return;
3021
- const finalMsgId = await draft.finalize();
3022
3510
  this.sessionDrafts.delete(sessionId);
3511
+ const finalMsgId = await draft.finalize();
3023
3512
  if (sessionId === this.assistantSession?.id) {
3024
3513
  const fullText = this.sessionTextBuffers.get(sessionId);
3025
3514
  this.sessionTextBuffers.delete(sessionId);
@@ -3058,4 +3547,4 @@ export {
3058
3547
  ApiServer,
3059
3548
  TelegramAdapter
3060
3549
  };
3061
- //# sourceMappingURL=chunk-WKHCVK5P.js.map
3550
+ //# sourceMappingURL=chunk-SWQRUVBW.js.map