@openacp/cli 0.6.6 → 0.6.8

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 (68) hide show
  1. package/dist/{adapter-RKK7A5GI.js → adapter-7GY3N4ZH.js} +9 -9
  2. package/dist/{admin-SCP25TN2.js → admin-2HAFXQBG.js} +6 -4
  3. package/dist/{chunk-WVMSP4AF.js → chunk-2J2RBYWN.js} +2 -2
  4. package/dist/{chunk-ZKTIZME6.js → chunk-47B7GNOE.js} +2 -2
  5. package/dist/{chunk-XVL6AGMG.js → chunk-5OVPEDUB.js} +2 -2
  6. package/dist/{chunk-AHPRT3RY.js → chunk-5SXG7X5D.js} +523 -251
  7. package/dist/chunk-5SXG7X5D.js.map +1 -0
  8. package/dist/{chunk-F3AICYO4.js → chunk-JHYXKVV2.js} +19 -1
  9. package/dist/chunk-JHYXKVV2.js.map +1 -0
  10. package/dist/{chunk-FCLGYYTY.js → chunk-JUYDFUSN.js} +224 -2
  11. package/dist/chunk-JUYDFUSN.js.map +1 -0
  12. package/dist/{chunk-774Y4RAK.js → chunk-KIRH7TUJ.js} +94 -24
  13. package/dist/chunk-KIRH7TUJ.js.map +1 -0
  14. package/dist/{chunk-4GQ3I65A.js → chunk-LBIKITQT.js} +1 -2
  15. package/dist/{chunk-4GQ3I65A.js.map → chunk-LBIKITQT.js.map} +1 -1
  16. package/dist/{chunk-ZMVVW3BK.js → chunk-LO4Y5WQ7.js} +127 -18
  17. package/dist/chunk-LO4Y5WQ7.js.map +1 -0
  18. package/dist/{chunk-3IRAWHMC.js → chunk-MZXWCDBU.js} +3 -3
  19. package/dist/{chunk-YQRF3IOR.js → chunk-O7CPGUAI.js} +2 -2
  20. package/dist/{chunk-7KZI2236.js → chunk-RHE2JSYE.js} +2 -2
  21. package/dist/{chunk-3ZO3MHZN.js → chunk-SHHMBGB3.js} +4 -3
  22. package/dist/chunk-SHHMBGB3.js.map +1 -0
  23. package/dist/{chunk-JV6XQRAE.js → chunk-XANPHG7W.js} +2 -2
  24. package/dist/{chunk-PJVKOZTR.js → chunk-YEOY2PBJ.js} +2 -2
  25. package/dist/cli.js +21 -21
  26. package/dist/{config-B26J3XXN.js → config-CQAS6YHR.js} +2 -2
  27. package/dist/{config-editor-QTGUK3CD.js → config-editor-37BM56WF.js} +4 -4
  28. package/dist/{config-registry-7I6GGDOY.js → config-registry-HDXFES2D.js} +2 -2
  29. package/dist/{daemon-5DS5BQXJ.js → daemon-K33ZPSEZ.js} +3 -3
  30. package/dist/{discord-QKT3JMRW.js → discord-VOHXRTCH.js} +113 -131
  31. package/dist/discord-VOHXRTCH.js.map +1 -0
  32. package/dist/{doctor-QQ3YZEYV.js → doctor-HASEBMUD.js} +4 -4
  33. package/dist/doctor-W4VGLDVM.js +9 -0
  34. package/dist/index.d.ts +25 -9
  35. package/dist/index.js +11 -11
  36. package/dist/{main-B5L3DD3Y.js → main-DUXVFTDD.js} +19 -19
  37. package/dist/{new-session-K6UCWYOP.js → new-session-NHK7TOEW.js} +3 -3
  38. package/dist/{settings-RRF77IC4.js → settings-6TF4WIGJ.js} +3 -3
  39. package/dist/{setup-5ZKSUR26.js → setup-RJCEB6FS.js} +3 -3
  40. package/package.json +1 -16
  41. package/dist/chunk-3ZO3MHZN.js.map +0 -1
  42. package/dist/chunk-774Y4RAK.js.map +0 -1
  43. package/dist/chunk-AHPRT3RY.js.map +0 -1
  44. package/dist/chunk-F3AICYO4.js.map +0 -1
  45. package/dist/chunk-FCLGYYTY.js.map +0 -1
  46. package/dist/chunk-ZMVVW3BK.js.map +0 -1
  47. package/dist/discord-QKT3JMRW.js.map +0 -1
  48. package/dist/doctor-6SUCVUZB.js +0 -9
  49. /package/dist/{adapter-RKK7A5GI.js.map → adapter-7GY3N4ZH.js.map} +0 -0
  50. /package/dist/{admin-SCP25TN2.js.map → admin-2HAFXQBG.js.map} +0 -0
  51. /package/dist/{chunk-WVMSP4AF.js.map → chunk-2J2RBYWN.js.map} +0 -0
  52. /package/dist/{chunk-ZKTIZME6.js.map → chunk-47B7GNOE.js.map} +0 -0
  53. /package/dist/{chunk-XVL6AGMG.js.map → chunk-5OVPEDUB.js.map} +0 -0
  54. /package/dist/{chunk-3IRAWHMC.js.map → chunk-MZXWCDBU.js.map} +0 -0
  55. /package/dist/{chunk-YQRF3IOR.js.map → chunk-O7CPGUAI.js.map} +0 -0
  56. /package/dist/{chunk-7KZI2236.js.map → chunk-RHE2JSYE.js.map} +0 -0
  57. /package/dist/{chunk-JV6XQRAE.js.map → chunk-XANPHG7W.js.map} +0 -0
  58. /package/dist/{chunk-PJVKOZTR.js.map → chunk-YEOY2PBJ.js.map} +0 -0
  59. /package/dist/{config-B26J3XXN.js.map → config-CQAS6YHR.js.map} +0 -0
  60. /package/dist/{config-editor-QTGUK3CD.js.map → config-editor-37BM56WF.js.map} +0 -0
  61. /package/dist/{config-registry-7I6GGDOY.js.map → config-registry-HDXFES2D.js.map} +0 -0
  62. /package/dist/{daemon-5DS5BQXJ.js.map → daemon-K33ZPSEZ.js.map} +0 -0
  63. /package/dist/{doctor-6SUCVUZB.js.map → doctor-HASEBMUD.js.map} +0 -0
  64. /package/dist/{doctor-QQ3YZEYV.js.map → doctor-W4VGLDVM.js.map} +0 -0
  65. /package/dist/{main-B5L3DD3Y.js.map → main-DUXVFTDD.js.map} +0 -0
  66. /package/dist/{new-session-K6UCWYOP.js.map → new-session-NHK7TOEW.js.map} +0 -0
  67. /package/dist/{settings-RRF77IC4.js.map → settings-6TF4WIGJ.js.map} +0 -0
  68. /package/dist/{setup-5ZKSUR26.js.map → setup-RJCEB6FS.js.map} +0 -0
@@ -1,10 +1,20 @@
1
1
  import {
2
2
  PRODUCT_GUIDE,
3
- dispatchMessage
4
- } from "./chunk-FCLGYYTY.js";
3
+ STATUS_ICONS,
4
+ dispatchMessage,
5
+ evaluateNoise,
6
+ extractContentText,
7
+ formatTokens,
8
+ formatToolSummary,
9
+ formatToolTitle,
10
+ progressBar,
11
+ splitMessage,
12
+ stripCodeFences,
13
+ truncateContent
14
+ } from "./chunk-JUYDFUSN.js";
5
15
  import {
6
16
  DoctorEngine
7
- } from "./chunk-PJVKOZTR.js";
17
+ } from "./chunk-YEOY2PBJ.js";
8
18
  import {
9
19
  buildMenuKeyboard,
10
20
  buildSkillMessages,
@@ -15,16 +25,16 @@ import {
15
25
  import {
16
26
  CheckpointReader,
17
27
  DEFAULT_MAX_TOKENS
18
- } from "./chunk-ZMVVW3BK.js";
28
+ } from "./chunk-LO4Y5WQ7.js";
19
29
  import {
20
30
  ChannelAdapter
21
- } from "./chunk-4GQ3I65A.js";
31
+ } from "./chunk-LBIKITQT.js";
22
32
  import {
23
33
  getConfigValue,
24
34
  getSafeFields,
25
35
  isHotReloadable,
26
36
  resolveOptions
27
- } from "./chunk-F3AICYO4.js";
37
+ } from "./chunk-JHYXKVV2.js";
28
38
  import {
29
39
  createChildLogger
30
40
  } from "./chunk-GAK6PIBW.js";
@@ -80,13 +90,16 @@ function escapeHtml(text) {
80
90
  function markdownToTelegramHtml(md) {
81
91
  const codeBlocks = [];
82
92
  const inlineCodes = [];
83
- let text = md.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, lang, code) => {
84
- const index = codeBlocks.length;
85
- const escapedCode = escapeHtml(code);
86
- const langAttr = lang ? ` class="language-${escapeHtml(lang)}"` : "";
87
- codeBlocks.push(`<pre><code${langAttr}>${escapedCode}</code></pre>`);
88
- return `\0CODE_BLOCK_${index}\0`;
89
- });
93
+ let text = md.replace(
94
+ /```(\w*)\n?([\s\S]*?)```/g,
95
+ (_match, lang, code) => {
96
+ const index = codeBlocks.length;
97
+ const escapedCode = escapeHtml(code);
98
+ const langAttr = lang ? ` class="language-${escapeHtml(lang)}"` : "";
99
+ codeBlocks.push(`<pre><code${langAttr}>${escapedCode}</code></pre>`);
100
+ return `\0CODE_BLOCK_${index}\0`;
101
+ }
102
+ );
90
103
  text = text.replace(/`([^`]+)`/g, (_match, code) => {
91
104
  const index = inlineCodes.length;
92
105
  inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
@@ -104,72 +117,32 @@ function markdownToTelegramHtml(md) {
104
117
  });
105
118
  return text;
106
119
  }
107
- var STATUS_ICON = {
108
- pending: "\u23F3",
109
- in_progress: "\u{1F504}",
110
- completed: "\u2705",
111
- failed: "\u274C"
112
- };
113
- var KIND_ICON = {
114
- read: "\u{1F4D6}",
115
- edit: "\u270F\uFE0F",
116
- delete: "\u{1F5D1}\uFE0F",
117
- execute: "\u25B6\uFE0F",
118
- search: "\u{1F50D}",
119
- fetch: "\u{1F310}",
120
- think: "\u{1F9E0}",
121
- move: "\u{1F4E6}",
122
- other: "\u{1F6E0}\uFE0F"
123
- };
124
- function extractContentText(content, depth = 0) {
125
- if (!content || depth > 5) return "";
126
- if (typeof content === "string") return content;
127
- if (Array.isArray(content)) {
128
- return content.map((c) => extractContentText(c, depth + 1)).filter(Boolean).join("\n");
129
- }
130
- if (typeof content === "object" && content !== null) {
131
- const c = content;
132
- if (c.type === "text" && typeof c.text === "string") return c.text;
133
- if (typeof c.text === "string") return c.text;
134
- if (typeof c.content === "string") return c.content;
135
- if (c.content && typeof c.content === "object") return extractContentText(c.content, depth + 1);
136
- if (c.input) return extractContentText(c.input, depth + 1);
137
- if (c.output) return extractContentText(c.output, depth + 1);
138
- const keys = Object.keys(c).filter((k) => k !== "type");
139
- if (keys.length === 0) return "";
140
- return JSON.stringify(c, null, 2);
141
- }
142
- return String(content);
143
- }
144
- function truncateContent(text, maxLen = 3800) {
145
- if (text.length <= maxLen) return text;
146
- return text.slice(0, maxLen) + "\n\u2026 (truncated)";
147
- }
148
- function formatToolCall(tool) {
149
- const si = STATUS_ICON[tool.status || ""] || "\u{1F527}";
150
- const ki = KIND_ICON[tool.kind || ""] || "\u{1F6E0}\uFE0F";
151
- let text = `${si} ${ki} <b>${escapeHtml(tool.name || "Tool")}</b>`;
120
+ function formatToolCall(tool, verbosity = "medium") {
121
+ const si = STATUS_ICONS[tool.status || ""] || "\u{1F527}";
122
+ const name = tool.name || "Tool";
123
+ const label = verbosity === "low" ? formatToolTitle(name, tool.rawInput) : formatToolSummary(name, tool.rawInput);
124
+ let text = `${si} <b>${escapeHtml(label)}</b>`;
152
125
  text += formatViewerLinks(tool.viewerLinks, tool.viewerFilePath);
153
- if (!tool.viewerLinks) {
154
- const details = extractContentText(tool.content);
126
+ if (verbosity === "high" || verbosity === "medium" && !tool.viewerLinks) {
127
+ const details = stripCodeFences(extractContentText(tool.content));
155
128
  if (details) {
156
129
  text += `
157
- <pre>${escapeHtml(truncateContent(details))}</pre>`;
130
+ <pre>${escapeHtml(truncateContent(details, 3800))}</pre>`;
158
131
  }
159
132
  }
160
133
  return text;
161
134
  }
162
- function formatToolUpdate(update) {
163
- const si = STATUS_ICON[update.status] || "\u{1F527}";
164
- const ki = KIND_ICON[update.kind || ""] || "\u{1F6E0}\uFE0F";
135
+ function formatToolUpdate(update, verbosity = "medium") {
136
+ const si = STATUS_ICONS[update.status] || "\u{1F527}";
165
137
  const name = update.name || "Tool";
166
- let text = `${si} ${ki} <b>${escapeHtml(name)}</b>`;
138
+ const label = verbosity === "low" ? formatToolTitle(name, update.rawInput) : formatToolSummary(name, update.rawInput);
139
+ let text = `${si} <b>${escapeHtml(label)}</b>`;
167
140
  text += formatViewerLinks(update.viewerLinks, update.viewerFilePath);
168
- if (!update.viewerLinks) {
169
- const details = extractContentText(update.content);
141
+ if (verbosity === "high" || verbosity === "medium" && !update.viewerLinks) {
142
+ const details = stripCodeFences(extractContentText(update.content));
170
143
  if (details) {
171
144
  text += `
172
- <pre>${escapeHtml(truncateContent(details))}</pre>`;
145
+ <pre>${escapeHtml(truncateContent(details, 3800))}</pre>`;
173
146
  }
174
147
  }
175
148
  return text;
@@ -178,19 +151,14 @@ function formatViewerLinks(links, filePath) {
178
151
  if (!links) return "";
179
152
  const fileName = filePath ? filePath.split("/").pop() || filePath : "";
180
153
  let text = "\n";
181
- if (links.file) text += `
154
+ if (links.file)
155
+ text += `
182
156
  \u{1F4C4} <a href="${escapeHtml(links.file)}">View ${escapeHtml(fileName || "file")}</a>`;
183
- if (links.diff) text += `
157
+ if (links.diff)
158
+ text += `
184
159
  \u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
185
160
  return text;
186
161
  }
187
- function formatTokens(n) {
188
- return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
189
- }
190
- function progressBar(ratio) {
191
- const filled = Math.round(Math.min(ratio, 1) * 10);
192
- return "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
193
- }
194
162
  function formatUsage(usage) {
195
163
  const { tokensUsed, contextSize } = usage;
196
164
  if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
@@ -217,49 +185,30 @@ function formatUsageReport(summaries, budgetStatus) {
217
185
  const lines = ["\u{1F4CA} <b>Usage Report</b>"];
218
186
  for (const summary of summaries) {
219
187
  lines.push("");
220
- lines.push(`\u2500\u2500 <b>${PERIOD_LABEL[summary.period] ?? summary.period}</b> \u2500\u2500`);
188
+ lines.push(
189
+ `\u2500\u2500 <b>${PERIOD_LABEL[summary.period] ?? summary.period}</b> \u2500\u2500`
190
+ );
221
191
  lines.push(
222
192
  `\u{1F4B0} ${formatCost(summary.totalCost)} \xB7 \u{1F524} ${formatTokens(summary.totalTokens)} tokens \xB7 \u{1F4CB} ${summary.sessionCount} sessions`
223
193
  );
224
194
  if (summary.period === "month" && budgetStatus.budget > 0) {
225
195
  const bar = progressBar(budgetStatus.used / budgetStatus.budget);
226
- lines.push(`Budget: ${formatCost(budgetStatus.used)} / ${formatCost(budgetStatus.budget)} (${budgetStatus.percent}%)`);
196
+ lines.push(
197
+ `Budget: ${formatCost(budgetStatus.used)} / ${formatCost(budgetStatus.budget)} (${budgetStatus.percent}%)`
198
+ );
227
199
  lines.push(`${bar} ${budgetStatus.percent}%`);
228
200
  }
229
201
  }
230
202
  return lines.join("\n");
231
203
  }
232
- function splitMessage(text, maxLength = 3800) {
233
- if (text.length <= maxLength) return [text];
234
- const chunks = [];
235
- let remaining = text;
236
- while (remaining.length > 0) {
237
- if (remaining.length <= maxLength) {
238
- chunks.push(remaining);
239
- break;
240
- }
241
- const wouldLeaveSmall = remaining.length < maxLength * 1.3;
242
- const searchLimit = wouldLeaveSmall ? Math.floor(remaining.length / 2) + 300 : maxLength;
243
- let splitAt = remaining.lastIndexOf("\n\n", searchLimit);
244
- if (splitAt === -1 || splitAt < searchLimit * 0.2) {
245
- splitAt = remaining.lastIndexOf("\n", searchLimit);
246
- }
247
- if (splitAt === -1 || splitAt < searchLimit * 0.2) {
248
- splitAt = searchLimit;
249
- }
250
- const candidate = remaining.slice(0, splitAt);
251
- const fences = candidate.match(/```/g);
252
- if (fences && fences.length % 2 !== 0) {
253
- const closingFence = remaining.indexOf("```", splitAt);
254
- if (closingFence !== -1) {
255
- const afterFence = remaining.indexOf("\n", closingFence + 3);
256
- splitAt = afterFence !== -1 ? afterFence + 1 : closingFence + 3;
257
- }
258
- }
259
- chunks.push(remaining.slice(0, splitAt));
260
- remaining = remaining.slice(splitAt).replace(/^\n+/, "");
261
- }
262
- return chunks;
204
+ function formatSummary(summary, sessionName) {
205
+ const header = sessionName ? `\u{1F4CB} <b>Summary \u2014 ${escapeHtml(sessionName)}</b>` : "\u{1F4CB} <b>Session Summary</b>";
206
+ return `${header}
207
+
208
+ ${escapeHtml(summary)}`;
209
+ }
210
+ function splitMessage2(text, maxLength = 3800) {
211
+ return splitMessage(text, maxLength);
263
212
  }
264
213
 
265
214
  // src/adapters/telegram/commands/admin.ts
@@ -271,7 +220,10 @@ function setupDangerousModeCallbacks(bot, core) {
271
220
  const session = core.sessionManager.getSession(sessionId);
272
221
  if (session) {
273
222
  session.dangerousMode = !session.dangerousMode;
274
- log.info({ sessionId, dangerousMode: session.dangerousMode }, "Dangerous mode toggled via button");
223
+ log.info(
224
+ { sessionId, dangerousMode: session.dangerousMode },
225
+ "Dangerous mode toggled via button"
226
+ );
275
227
  core.sessionManager.patchRecord(sessionId, { dangerousMode: session.dangerousMode }).catch(() => {
276
228
  });
277
229
  const toastText2 = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
@@ -281,7 +233,11 @@ function setupDangerousModeCallbacks(bot, core) {
281
233
  }
282
234
  try {
283
235
  await ctx.editMessageReplyMarkup({
284
- reply_markup: buildSessionControlKeyboard(sessionId, session.dangerousMode, session.voiceMode === "on")
236
+ reply_markup: buildSessionControlKeyboard(
237
+ sessionId,
238
+ session.dangerousMode,
239
+ session.voiceMode === "on"
240
+ )
285
241
  });
286
242
  } catch {
287
243
  }
@@ -290,7 +246,9 @@ function setupDangerousModeCallbacks(bot, core) {
290
246
  const record = core.sessionManager.getSessionRecord(sessionId);
291
247
  if (!record || record.status === "cancelled" || record.status === "error") {
292
248
  try {
293
- await ctx.answerCallbackQuery({ text: "\u26A0\uFE0F Session not found or already ended." });
249
+ await ctx.answerCallbackQuery({
250
+ text: "\u26A0\uFE0F Session not found or already ended."
251
+ });
294
252
  } catch {
295
253
  }
296
254
  return;
@@ -298,7 +256,10 @@ function setupDangerousModeCallbacks(bot, core) {
298
256
  const newDangerousMode = !(record.dangerousMode ?? false);
299
257
  core.sessionManager.patchRecord(sessionId, { dangerousMode: newDangerousMode }).catch(() => {
300
258
  });
301
- log.info({ sessionId, dangerousMode: newDangerousMode }, "Dangerous mode toggled via button (store-only, session not in memory)");
259
+ log.info(
260
+ { sessionId, dangerousMode: newDangerousMode },
261
+ "Dangerous mode toggled via button (store-only, session not in memory)"
262
+ );
302
263
  const toastText = newDangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
303
264
  try {
304
265
  await ctx.answerCallbackQuery({ text: toastText });
@@ -306,7 +267,11 @@ function setupDangerousModeCallbacks(bot, core) {
306
267
  }
307
268
  try {
308
269
  await ctx.editMessageReplyMarkup({
309
- reply_markup: buildSessionControlKeyboard(sessionId, newDangerousMode, false)
270
+ reply_markup: buildSessionControlKeyboard(
271
+ sessionId,
272
+ newDangerousMode,
273
+ false
274
+ )
310
275
  });
311
276
  } catch {
312
277
  }
@@ -315,26 +280,40 @@ function setupDangerousModeCallbacks(bot, core) {
315
280
  async function handleEnableDangerous(ctx, core) {
316
281
  const threadId = ctx.message?.message_thread_id;
317
282
  if (!threadId) {
318
- await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
283
+ await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
284
+ parse_mode: "HTML"
285
+ });
319
286
  return;
320
287
  }
321
- const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
288
+ const session = core.sessionManager.getSessionByThread(
289
+ "telegram",
290
+ String(threadId)
291
+ );
322
292
  if (session) {
323
293
  if (session.dangerousMode) {
324
- await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
294
+ await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", {
295
+ parse_mode: "HTML"
296
+ });
325
297
  return;
326
298
  }
327
299
  session.dangerousMode = true;
328
300
  core.sessionManager.patchRecord(session.id, { dangerousMode: true }).catch(() => {
329
301
  });
330
302
  } else {
331
- const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
303
+ const record = core.sessionManager.getRecordByThread(
304
+ "telegram",
305
+ String(threadId)
306
+ );
332
307
  if (!record || record.status === "cancelled" || record.status === "error") {
333
- await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
308
+ await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
309
+ parse_mode: "HTML"
310
+ });
334
311
  return;
335
312
  }
336
313
  if (record.dangerousMode) {
337
- await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
314
+ await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", {
315
+ parse_mode: "HTML"
316
+ });
338
317
  return;
339
318
  }
340
319
  core.sessionManager.patchRecord(record.sessionId, { dangerousMode: true }).catch(() => {
@@ -352,32 +331,49 @@ Use /disable_dangerous to restore normal behaviour.`,
352
331
  async function handleDisableDangerous(ctx, core) {
353
332
  const threadId = ctx.message?.message_thread_id;
354
333
  if (!threadId) {
355
- await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
334
+ await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
335
+ parse_mode: "HTML"
336
+ });
356
337
  return;
357
338
  }
358
- const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
339
+ const session = core.sessionManager.getSessionByThread(
340
+ "telegram",
341
+ String(threadId)
342
+ );
359
343
  if (session) {
360
344
  if (!session.dangerousMode) {
361
- await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
345
+ await ctx.reply("\u{1F510} Dangerous mode is already disabled.", {
346
+ parse_mode: "HTML"
347
+ });
362
348
  return;
363
349
  }
364
350
  session.dangerousMode = false;
365
351
  core.sessionManager.patchRecord(session.id, { dangerousMode: false }).catch(() => {
366
352
  });
367
353
  } else {
368
- const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
354
+ const record = core.sessionManager.getRecordByThread(
355
+ "telegram",
356
+ String(threadId)
357
+ );
369
358
  if (!record || record.status === "cancelled" || record.status === "error") {
370
- await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
359
+ await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
360
+ parse_mode: "HTML"
361
+ });
371
362
  return;
372
363
  }
373
364
  if (!record.dangerousMode) {
374
- await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
365
+ await ctx.reply("\u{1F510} Dangerous mode is already disabled.", {
366
+ parse_mode: "HTML"
367
+ });
375
368
  return;
376
369
  }
377
370
  core.sessionManager.patchRecord(record.sessionId, { dangerousMode: false }).catch(() => {
378
371
  });
379
372
  }
380
- await ctx.reply("\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.", { parse_mode: "HTML" });
373
+ await ctx.reply(
374
+ "\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.",
375
+ { parse_mode: "HTML" }
376
+ );
381
377
  }
382
378
  function buildSessionControlKeyboard(sessionId, dangerousMode, voiceMode) {
383
379
  return new InlineKeyboard().text(
@@ -394,7 +390,9 @@ function setupTTSCallbacks(bot, core) {
394
390
  const session = core.sessionManager.getSession(sessionId);
395
391
  if (!session) {
396
392
  try {
397
- await ctx.answerCallbackQuery({ text: "\u26A0\uFE0F Session not found or not active." });
393
+ await ctx.answerCallbackQuery({
394
+ text: "\u26A0\uFE0F Session not found or not active."
395
+ });
398
396
  } catch {
399
397
  }
400
398
  return;
@@ -408,7 +406,11 @@ function setupTTSCallbacks(bot, core) {
408
406
  }
409
407
  try {
410
408
  await ctx.editMessageReplyMarkup({
411
- reply_markup: buildSessionControlKeyboard(sessionId, session.dangerousMode, newMode === "on")
409
+ reply_markup: buildSessionControlKeyboard(
410
+ sessionId,
411
+ session.dangerousMode,
412
+ newMode === "on"
413
+ )
412
414
  });
413
415
  } catch {
414
416
  }
@@ -417,42 +419,113 @@ function setupTTSCallbacks(bot, core) {
417
419
  async function handleTTS(ctx, core) {
418
420
  const threadId = ctx.message?.message_thread_id;
419
421
  if (!threadId) {
420
- await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
422
+ await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
423
+ parse_mode: "HTML"
424
+ });
421
425
  return;
422
426
  }
423
427
  const session = await core.getOrResumeSession("telegram", String(threadId));
424
428
  if (!session) {
425
- await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
429
+ await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
430
+ parse_mode: "HTML"
431
+ });
426
432
  return;
427
433
  }
428
434
  const args = ctx.message?.text?.split(/\s+/).slice(1) ?? [];
429
435
  const arg = args[0]?.toLowerCase();
430
436
  if (arg === "on") {
431
437
  session.setVoiceMode("on");
432
- await ctx.reply("\u{1F50A} Text to Speech enabled for this session.", { parse_mode: "HTML" });
438
+ await ctx.reply("\u{1F50A} Text to Speech enabled for this session.", {
439
+ parse_mode: "HTML"
440
+ });
433
441
  } else if (arg === "off") {
434
442
  session.setVoiceMode("off");
435
443
  await ctx.reply("\u{1F507} Text to Speech disabled.", { parse_mode: "HTML" });
436
444
  } else {
437
445
  session.setVoiceMode("next");
438
- await ctx.reply("\u{1F50A} Text to Speech enabled for the next message.", { parse_mode: "HTML" });
446
+ await ctx.reply("\u{1F50A} Text to Speech enabled for the next message.", {
447
+ parse_mode: "HTML"
448
+ });
439
449
  }
440
450
  }
451
+ var VERBOSITY_LABELS = {
452
+ low: "\u{1F507} Low",
453
+ medium: "\u{1F4CA} Medium",
454
+ high: "\u{1F4D6} High"
455
+ };
456
+ async function handleVerbosity(ctx, core) {
457
+ const args = ctx.message?.text?.split(/\s+/).slice(1) ?? [];
458
+ const arg = args[0]?.toLowerCase();
459
+ if (arg === "low" || arg === "medium" || arg === "high") {
460
+ await core.configManager.save(
461
+ { channels: { telegram: { displayVerbosity: arg } } },
462
+ "channels.telegram.displayVerbosity"
463
+ );
464
+ await ctx.reply(
465
+ `${VERBOSITY_LABELS[arg]} Display verbosity set to <b>${arg}</b>.`,
466
+ { parse_mode: "HTML" }
467
+ );
468
+ } else {
469
+ const current = core.configManager.get().channels?.telegram?.displayVerbosity ?? "medium";
470
+ await ctx.reply(
471
+ `\u{1F4CA} Current verbosity: <b>${current}</b>
472
+
473
+ Usage: <code>/verbosity low|medium|high</code>
474
+
475
+ \u2022 <b>low</b> \u2014 minimal output, title only
476
+ \u2022 <b>medium</b> \u2014 balanced (default)
477
+ \u2022 <b>high</b> \u2014 full detail with content`,
478
+ { parse_mode: "HTML" }
479
+ );
480
+ }
481
+ }
482
+ function setupVerbosityCallbacks(bot, core) {
483
+ bot.callbackQuery(/^vb:/, async (ctx) => {
484
+ const level = ctx.callbackQuery.data.slice(3);
485
+ if (level !== "low" && level !== "medium" && level !== "high") return;
486
+ await core.configManager.save(
487
+ { channels: { telegram: { displayVerbosity: level } } },
488
+ "channels.telegram.displayVerbosity"
489
+ );
490
+ try {
491
+ await ctx.answerCallbackQuery({
492
+ text: `${VERBOSITY_LABELS[level]} Verbosity: ${level}`
493
+ });
494
+ } catch {
495
+ }
496
+ });
497
+ }
441
498
  async function handleUpdate(ctx, core) {
442
499
  if (!core.requestRestart) {
443
- await ctx.reply("\u26A0\uFE0F Update is not available (no restart handler registered).", { parse_mode: "HTML" });
500
+ await ctx.reply(
501
+ "\u26A0\uFE0F Update is not available (no restart handler registered).",
502
+ { parse_mode: "HTML" }
503
+ );
444
504
  return;
445
505
  }
446
506
  const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-AXXV6IV2.js");
447
507
  const current = getCurrentVersion();
448
- const statusMsg = await ctx.reply(`\u{1F50D} Checking for updates... (current: v${escapeHtml(current)})`, { parse_mode: "HTML" });
508
+ const statusMsg = await ctx.reply(
509
+ `\u{1F50D} Checking for updates... (current: v${escapeHtml(current)})`,
510
+ { parse_mode: "HTML" }
511
+ );
449
512
  const latest = await getLatestVersion();
450
513
  if (!latest) {
451
- await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, "\u274C Could not check for updates.", { parse_mode: "HTML" });
514
+ await ctx.api.editMessageText(
515
+ ctx.chat.id,
516
+ statusMsg.message_id,
517
+ "\u274C Could not check for updates.",
518
+ { parse_mode: "HTML" }
519
+ );
452
520
  return;
453
521
  }
454
522
  if (compareVersions(current, latest) >= 0) {
455
- await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, `\u2705 Already up to date (v${escapeHtml(current)}).`, { parse_mode: "HTML" });
523
+ await ctx.api.editMessageText(
524
+ ctx.chat.id,
525
+ statusMsg.message_id,
526
+ `\u2705 Already up to date (v${escapeHtml(current)}).`,
527
+ { parse_mode: "HTML" }
528
+ );
456
529
  return;
457
530
  }
458
531
  await ctx.api.editMessageText(
@@ -463,7 +536,12 @@ async function handleUpdate(ctx, core) {
463
536
  );
464
537
  const ok = await runUpdate();
465
538
  if (!ok) {
466
- await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, "\u274C Update failed. Try manually: <code>npm install -g @openacp/cli@latest</code>", { parse_mode: "HTML" });
539
+ await ctx.api.editMessageText(
540
+ ctx.chat.id,
541
+ statusMsg.message_id,
542
+ "\u274C Update failed. Try manually: <code>npm install -g @openacp/cli@latest</code>",
543
+ { parse_mode: "HTML" }
544
+ );
467
545
  return;
468
546
  }
469
547
  await ctx.api.editMessageText(
@@ -477,10 +555,16 @@ async function handleUpdate(ctx, core) {
477
555
  }
478
556
  async function handleRestart(ctx, core) {
479
557
  if (!core.requestRestart) {
480
- await ctx.reply("\u26A0\uFE0F Restart is not available (no restart handler registered).", { parse_mode: "HTML" });
558
+ await ctx.reply(
559
+ "\u26A0\uFE0F Restart is not available (no restart handler registered).",
560
+ { parse_mode: "HTML" }
561
+ );
481
562
  return;
482
563
  }
483
- await ctx.reply("\u{1F504} <b>Restarting OpenACP...</b>\nRebuilding and restarting. Be back shortly.", { parse_mode: "HTML" });
564
+ await ctx.reply(
565
+ "\u{1F504} <b>Restarting OpenACP...</b>\nRebuilding and restarting. Be back shortly.",
566
+ { parse_mode: "HTML" }
567
+ );
484
568
  await new Promise((r) => setTimeout(r, 500));
485
569
  await core.requestRestart();
486
570
  }
@@ -1183,26 +1267,13 @@ async function handleArchive(ctx, core) {
1183
1267
  const threadId = ctx.message?.message_thread_id;
1184
1268
  if (!threadId) return;
1185
1269
  const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
1186
- if (!session) {
1187
- await ctx.reply(
1188
- "\u2139\uFE0F <b>/archive</b> works in session topics \u2014 it recreates the topic with a clean chat view while keeping your agent session alive.\n\nGo to the session topic you want to archive and type /archive there.",
1189
- { parse_mode: "HTML" }
1190
- );
1191
- return;
1192
- }
1193
- if (session.status === "initializing") {
1194
- await ctx.reply("\u23F3 Please wait for session to be ready.", { parse_mode: "HTML" });
1195
- return;
1196
- }
1197
- if (session.status !== "active") {
1198
- await ctx.reply(`\u26A0\uFE0F Cannot archive \u2014 session is ${session.status}.`, { parse_mode: "HTML" });
1199
- return;
1200
- }
1270
+ const record = !session ? core.sessionManager.getRecordByThread("telegram", String(threadId)) : void 0;
1271
+ const identifier = session?.id ?? record?.sessionId ?? `topic:${threadId}`;
1201
1272
  await ctx.reply(
1202
- "\u26A0\uFE0F <b>Archive this session topic?</b>\n\nThis will permanently delete all messages in this topic and create a fresh one.\nYour agent session will continue \u2014 only the chat view is reset.\n\n<i>Note: links to messages in this topic will stop working.</i>",
1273
+ "\u26A0\uFE0F <b>Archive this session?</b>\n\nThis will:\n\u2022 Delete this topic and all messages\n\u2022 Stop the agent session (if running)\n\u2022 Remove the session record\n\n<i>This action cannot be undone.</i>",
1203
1274
  {
1204
1275
  parse_mode: "HTML",
1205
- reply_markup: new InlineKeyboard3().text("\u{1F5D1} Yes, archive", `ar:yes:${session.id}`).text("\u274C Cancel", `ar:no:${session.id}`)
1276
+ reply_markup: new InlineKeyboard3().text("\u{1F5D1} Yes, archive", `ar:yes:${identifier}`).text("\u274C Cancel", `ar:no:${identifier}`)
1206
1277
  }
1207
1278
  );
1208
1279
  }
@@ -1213,31 +1284,103 @@ async function handleArchiveConfirm(ctx, core, chatId) {
1213
1284
  await ctx.answerCallbackQuery();
1214
1285
  } catch {
1215
1286
  }
1216
- const [, action, sessionId] = data.split(":");
1287
+ const [, action, ...rest] = data.split(":");
1288
+ const identifier = rest.join(":");
1217
1289
  if (action === "no") {
1218
1290
  await ctx.editMessageText("Archive cancelled.", { parse_mode: "HTML" });
1219
1291
  return;
1220
1292
  }
1221
- await ctx.editMessageText("\u{1F504} Archiving topic...", { parse_mode: "HTML" });
1222
- const result = await core.archiveSession(sessionId);
1293
+ await ctx.editMessageText("\u{1F504} Archiving...", { parse_mode: "HTML" });
1294
+ if (identifier.startsWith("topic:")) {
1295
+ const topicId = Number(identifier.slice("topic:".length));
1296
+ try {
1297
+ await ctx.api.deleteForumTopic(chatId, topicId);
1298
+ core.notificationManager.notifyAll({
1299
+ sessionId: "system",
1300
+ sessionName: `Orphan topic #${topicId}`,
1301
+ type: "completed",
1302
+ summary: `Orphan topic #${topicId} archived and deleted.`
1303
+ });
1304
+ } catch (err) {
1305
+ core.notificationManager.notifyAll({
1306
+ sessionId: "system",
1307
+ sessionName: `Orphan topic #${topicId}`,
1308
+ type: "error",
1309
+ summary: `Failed to delete orphan topic #${topicId}: ${err.message}`
1310
+ });
1311
+ }
1312
+ return;
1313
+ }
1314
+ const result = await core.archiveSession(identifier);
1223
1315
  if (result.ok) {
1224
- const newTopicId = Number(result.newThreadId);
1225
- await ctx.api.sendMessage(chatId, "\u2705 Topic archived. Session continues.", {
1226
- message_thread_id: newTopicId,
1227
- parse_mode: "HTML"
1316
+ core.notificationManager.notifyAll({
1317
+ sessionId: identifier,
1318
+ type: "completed",
1319
+ summary: `Session archived and deleted.`
1228
1320
  });
1229
1321
  } else {
1230
1322
  try {
1231
1323
  await ctx.editMessageText(`\u274C Failed to archive: <code>${escapeHtml(result.error)}</code>`, { parse_mode: "HTML" });
1232
1324
  } catch {
1233
1325
  core.notificationManager.notifyAll({
1234
- sessionId,
1326
+ sessionId: identifier,
1235
1327
  type: "error",
1236
- summary: `Failed to recreate topic for session "${sessionId}": ${result.error}`
1328
+ summary: `Failed to archive session "${identifier}": ${result.error}`
1237
1329
  });
1238
1330
  }
1239
1331
  }
1240
1332
  }
1333
+ async function handleSummary(ctx, core) {
1334
+ const threadId = ctx.message?.message_thread_id;
1335
+ if (!threadId) return;
1336
+ const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
1337
+ const record = !session ? core.sessionManager.getRecordByThread("telegram", String(threadId)) : void 0;
1338
+ const sessionId = session?.id ?? record?.sessionId;
1339
+ if (!sessionId) {
1340
+ await ctx.reply(
1341
+ "\u2139\uFE0F <b>/summary</b> works in session topics \u2014 it asks the agent to summarize the session.\n\nGo to a session topic and type /summary there.",
1342
+ { parse_mode: "HTML" }
1343
+ );
1344
+ return;
1345
+ }
1346
+ await ctx.replyWithChatAction("typing");
1347
+ const result = await core.summarizeSession(sessionId);
1348
+ if (result.ok) {
1349
+ await ctx.reply(formatSummary(result.summary, session?.name ?? record?.name), { parse_mode: "HTML" });
1350
+ } else {
1351
+ await ctx.reply(`\u26A0\uFE0F ${escapeHtml(result.error)}`, { parse_mode: "HTML" });
1352
+ }
1353
+ }
1354
+ async function handleSummaryCallback(ctx, core, chatId) {
1355
+ const data = ctx.callbackQuery?.data;
1356
+ if (!data) return;
1357
+ const sessionId = data.replace("sm:summary:", "");
1358
+ try {
1359
+ await ctx.answerCallbackQuery();
1360
+ } catch {
1361
+ }
1362
+ const session = core.sessionManager.getSession(sessionId);
1363
+ const record = !session ? core.sessionManager.getSessionRecord(sessionId) : void 0;
1364
+ const threadId = session ? Number(session.threadId) : record?.platform?.topicId ?? 0;
1365
+ if (!threadId) return;
1366
+ await ctx.api.sendMessage(chatId, "\u{1F4CB} Generating summary...", {
1367
+ message_thread_id: threadId,
1368
+ parse_mode: "HTML"
1369
+ });
1370
+ const result = await core.summarizeSession(sessionId);
1371
+ const sessionName = session?.name ?? record?.name;
1372
+ if (result.ok) {
1373
+ await ctx.api.sendMessage(chatId, formatSummary(result.summary, sessionName), {
1374
+ message_thread_id: threadId,
1375
+ parse_mode: "HTML"
1376
+ });
1377
+ } else {
1378
+ await ctx.api.sendMessage(chatId, `\u26A0\uFE0F ${escapeHtml(result.error)}`, {
1379
+ message_thread_id: threadId,
1380
+ parse_mode: "HTML"
1381
+ });
1382
+ }
1383
+ }
1241
1384
 
1242
1385
  // src/adapters/telegram/commands/agents.ts
1243
1386
  import { InlineKeyboard as InlineKeyboard4 } from "grammy";
@@ -2396,7 +2539,9 @@ function setupCommands(bot, core, chatId, assistant) {
2396
2539
  bot.command("tunnel", (ctx) => handleTunnel(ctx, core));
2397
2540
  bot.command("tunnels", (ctx) => handleTunnels(ctx, core));
2398
2541
  bot.command("archive", (ctx) => handleArchive(ctx, core));
2542
+ bot.command("summary", (ctx) => handleSummary(ctx, core));
2399
2543
  bot.command("text_to_speech", (ctx) => handleTTS(ctx, core));
2544
+ bot.command("verbosity", (ctx) => handleVerbosity(ctx, core));
2400
2545
  bot.command("resume", (ctx) => handleResume(ctx, core, chatId, assistant));
2401
2546
  }
2402
2547
  function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSession) {
@@ -2410,9 +2555,16 @@ function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSessio
2410
2555
  bot.callbackQuery(/^na:/, async (ctx) => {
2411
2556
  const agentKey = ctx.callbackQuery.data.replace("na:", "");
2412
2557
  await ctx.answerCallbackQuery();
2413
- await createSessionDirect(ctx, core, chatId, agentKey, core.configManager.get().workspace.baseDir);
2558
+ await createSessionDirect(
2559
+ ctx,
2560
+ core,
2561
+ chatId,
2562
+ agentKey,
2563
+ core.configManager.get().workspace.baseDir
2564
+ );
2414
2565
  });
2415
2566
  bot.callbackQuery(/^ar:/, (ctx) => handleArchiveConfirm(ctx, core, chatId));
2567
+ bot.callbackQuery(/^sm:/, (ctx) => handleSummaryCallback(ctx, core, chatId));
2416
2568
  bot.callbackQuery(/^m:/, async (ctx) => {
2417
2569
  const data = ctx.callbackQuery.data;
2418
2570
  try {
@@ -2460,8 +2612,14 @@ var STATIC_COMMANDS = [
2460
2612
  { command: "install", description: "Install a new agent" },
2461
2613
  { command: "help", description: "Help" },
2462
2614
  { command: "menu", description: "Show menu" },
2463
- { command: "enable_dangerous", description: "Auto-approve all permission requests (session only)" },
2464
- { command: "disable_dangerous", description: "Restore normal permission prompts (session only)" },
2615
+ {
2616
+ command: "enable_dangerous",
2617
+ description: "Auto-approve all permission requests (session only)"
2618
+ },
2619
+ {
2620
+ command: "disable_dangerous",
2621
+ description: "Restore normal permission prompts (session only)"
2622
+ },
2465
2623
  { command: "integrate", description: "Manage agent integrations" },
2466
2624
  { command: "handoff", description: "Continue this session in your terminal" },
2467
2625
  { command: "clear", description: "Clear assistant history" },
@@ -2472,7 +2630,9 @@ var STATIC_COMMANDS = [
2472
2630
  { command: "tunnel", description: "Create/stop tunnel for a local port" },
2473
2631
  { command: "tunnels", description: "List active tunnels" },
2474
2632
  { command: "archive", description: "Archive session topic (recreate with clean history)" },
2633
+ { command: "summary", description: "Get AI summary of current session" },
2475
2634
  { command: "text_to_speech", description: "Toggle Text to Speech (/text_to_speech on, /text_to_speech off)" },
2635
+ { command: "verbosity", description: "Set display verbosity (/verbosity low|medium|high)" },
2476
2636
  { command: "resume", description: "Resume with conversation history from Entire checkpoints" }
2477
2637
  ];
2478
2638
 
@@ -3272,7 +3432,7 @@ var ToolCallTracker = class {
3272
3432
  this.sendQueue = sendQueue;
3273
3433
  }
3274
3434
  sessions = /* @__PURE__ */ new Map();
3275
- async trackNewCall(sessionId, threadId, meta) {
3435
+ async trackNewCall(sessionId, threadId, meta, verbosity = "medium") {
3276
3436
  if (!this.sessions.has(sessionId)) {
3277
3437
  this.sessions.set(sessionId, /* @__PURE__ */ new Map());
3278
3438
  }
@@ -3284,31 +3444,34 @@ var ToolCallTracker = class {
3284
3444
  msgId: 0,
3285
3445
  name: meta.name,
3286
3446
  kind: meta.kind,
3447
+ rawInput: meta.rawInput,
3287
3448
  viewerLinks: meta.viewerLinks,
3288
3449
  viewerFilePath: meta.viewerFilePath,
3289
3450
  ready
3290
3451
  });
3291
- const msg = await this.sendQueue.enqueue(
3292
- () => this.bot.api.sendMessage(
3293
- this.chatId,
3294
- formatToolCall(meta),
3295
- {
3452
+ try {
3453
+ const msg = await this.sendQueue.enqueue(
3454
+ () => this.bot.api.sendMessage(this.chatId, formatToolCall(meta, verbosity), {
3296
3455
  message_thread_id: threadId,
3297
3456
  parse_mode: "HTML",
3298
3457
  disable_notification: true
3299
- }
3300
- )
3301
- );
3302
- const toolEntry = this.sessions.get(sessionId).get(meta.id);
3303
- toolEntry.msgId = msg.message_id;
3304
- resolveReady();
3458
+ })
3459
+ );
3460
+ const toolEntry = this.sessions.get(sessionId).get(meta.id);
3461
+ toolEntry.msgId = msg.message_id;
3462
+ } finally {
3463
+ resolveReady();
3464
+ }
3305
3465
  }
3306
- async updateCall(sessionId, meta) {
3466
+ async updateCall(sessionId, meta, verbosity = "medium") {
3307
3467
  const toolState = this.sessions.get(sessionId)?.get(meta.id);
3308
3468
  if (!toolState) return;
3309
3469
  if (meta.viewerLinks) {
3310
3470
  toolState.viewerLinks = meta.viewerLinks;
3311
- log11.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
3471
+ log11.debug(
3472
+ { toolId: meta.id, viewerLinks: meta.viewerLinks },
3473
+ "Accumulated viewerLinks"
3474
+ );
3312
3475
  }
3313
3476
  if (meta.viewerFilePath) toolState.viewerFilePath = meta.viewerFilePath;
3314
3477
  if (meta.name) toolState.name = meta.name;
@@ -3331,10 +3494,11 @@ var ToolCallTracker = class {
3331
3494
  ...meta,
3332
3495
  name: toolState.name,
3333
3496
  kind: toolState.kind,
3497
+ rawInput: toolState.rawInput,
3334
3498
  viewerLinks: toolState.viewerLinks,
3335
3499
  viewerFilePath: toolState.viewerFilePath
3336
3500
  };
3337
- const formattedText = formatToolUpdate(merged);
3501
+ const formattedText = formatToolUpdate(merged, verbosity);
3338
3502
  try {
3339
3503
  await this.sendQueue.enqueue(
3340
3504
  () => this.bot.api.editMessageText(
@@ -3488,7 +3652,7 @@ var MessageDraft = class {
3488
3652
  } catch {
3489
3653
  }
3490
3654
  }
3491
- const mdChunks = splitMessage(this.buffer);
3655
+ const mdChunks = splitMessage2(this.buffer);
3492
3656
  const chunkPromises = [];
3493
3657
  for (let i = 0; i < mdChunks.length; i++) {
3494
3658
  const html = markdownToTelegramHtml(mdChunks[i]);
@@ -3740,6 +3904,12 @@ var TelegramAdapter = class extends ChannelAdapter {
3740
3904
  skillManager;
3741
3905
  fileService;
3742
3906
  sessionTrackers = /* @__PURE__ */ new Map();
3907
+ get verbosity() {
3908
+ const live = this.core.configManager.get().channels?.telegram;
3909
+ const v = live?.displayVerbosity ?? this.telegramConfig.displayVerbosity;
3910
+ if (v === "low" || v === "high") return v;
3911
+ return "medium";
3912
+ }
3743
3913
  getOrCreateTracker(sessionId, threadId) {
3744
3914
  let tracker = this.sessionTrackers.get(sessionId);
3745
3915
  if (!tracker) {
@@ -3765,8 +3935,16 @@ var TelegramAdapter = class extends ChannelAdapter {
3765
3935
  }
3766
3936
  });
3767
3937
  this.fileService = this.core.fileService;
3768
- this.toolTracker = new ToolCallTracker(this.bot, this.telegramConfig.chatId, this.sendQueue);
3769
- this.draftManager = new DraftManager(this.bot, this.telegramConfig.chatId, this.sendQueue);
3938
+ this.toolTracker = new ToolCallTracker(
3939
+ this.bot,
3940
+ this.telegramConfig.chatId,
3941
+ this.sendQueue
3942
+ );
3943
+ this.draftManager = new DraftManager(
3944
+ this.bot,
3945
+ this.telegramConfig.chatId,
3946
+ this.sendQueue
3947
+ );
3770
3948
  this.skillManager = new SkillCommandManager(
3771
3949
  this.bot,
3772
3950
  this.telegramConfig.chatId,
@@ -3785,7 +3963,11 @@ var TelegramAdapter = class extends ChannelAdapter {
3785
3963
  return result;
3786
3964
  }
3787
3965
  const retryAfter = (result.parameters?.retry_after ?? 5) + 1;
3788
- const rateLimitedMethods = ["sendMessage", "editMessageText", "editMessageReplyMarkup"];
3966
+ const rateLimitedMethods = [
3967
+ "sendMessage",
3968
+ "editMessageText",
3969
+ "editMessageReplyMarkup"
3970
+ ];
3789
3971
  if (rateLimitedMethods.includes(method)) {
3790
3972
  this.sendQueue.onRateLimited();
3791
3973
  }
@@ -3835,6 +4017,7 @@ var TelegramAdapter = class extends ChannelAdapter {
3835
4017
  );
3836
4018
  setupDangerousModeCallbacks(this.bot, this.core);
3837
4019
  setupTTSCallbacks(this.bot, this.core);
4020
+ setupVerbosityCallbacks(this.bot, this.core);
3838
4021
  setupActionCallbacks(
3839
4022
  this.bot,
3840
4023
  this.core,
@@ -3846,7 +4029,10 @@ var TelegramAdapter = class extends ChannelAdapter {
3846
4029
  this.bot,
3847
4030
  this.core,
3848
4031
  this.telegramConfig.chatId,
3849
- { notificationTopicId: this.notificationTopicId, assistantTopicId: this.assistantTopicId },
4032
+ {
4033
+ notificationTopicId: this.notificationTopicId,
4034
+ assistantTopicId: this.assistantTopicId
4035
+ },
3850
4036
  () => {
3851
4037
  if (!this.assistantSession) return void 0;
3852
4038
  return {
@@ -3890,8 +4076,14 @@ var TelegramAdapter = class extends ChannelAdapter {
3890
4076
  });
3891
4077
  return;
3892
4078
  }
3893
- const session = this.core.sessionManager.getSessionByThread("telegram", String(threadId));
3894
- const record = session ? void 0 : this.core.sessionManager.getRecordByThread("telegram", String(threadId));
4079
+ const session = this.core.sessionManager.getSessionByThread(
4080
+ "telegram",
4081
+ String(threadId)
4082
+ );
4083
+ const record = session ? void 0 : this.core.sessionManager.getRecordByThread(
4084
+ "telegram",
4085
+ String(threadId)
4086
+ );
3895
4087
  const agentName = session?.agentName ?? record?.agentName;
3896
4088
  const agentSessionId = session?.agentSessionId ?? record?.agentSessionId;
3897
4089
  if (!agentName || !agentSessionId) {
@@ -3932,7 +4124,9 @@ var TelegramAdapter = class extends ChannelAdapter {
3932
4124
  const agents = this.core.agentManager.getAvailableAgents();
3933
4125
  const allRecords = this.core.sessionManager.listRecords();
3934
4126
  const welcomeText = buildWelcomeMessage({
3935
- activeCount: allRecords.filter((r) => r.status === "active" || r.status === "initializing").length,
4127
+ activeCount: allRecords.filter(
4128
+ (r) => r.status === "active" || r.status === "initializing"
4129
+ ).length,
3936
4130
  errorCount: allRecords.filter((r) => r.status === "error").length,
3937
4131
  totalCount: allRecords.length,
3938
4132
  agents: agents.map((a) => a.name),
@@ -3955,10 +4149,16 @@ var TelegramAdapter = class extends ChannelAdapter {
3955
4149
  );
3956
4150
  this.assistantSession = session;
3957
4151
  this.assistantInitializing = true;
3958
- log13.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
4152
+ log13.info(
4153
+ { sessionId: session.id },
4154
+ "Assistant session ready, system prompt running in background"
4155
+ );
3959
4156
  ready.then(() => {
3960
4157
  this.assistantInitializing = false;
3961
- log13.info({ sessionId: session.id }, "Assistant ready for user messages");
4158
+ log13.info(
4159
+ { sessionId: session.id },
4160
+ "Assistant ready for user messages"
4161
+ );
3962
4162
  });
3963
4163
  } catch (err) {
3964
4164
  log13.error({ err }, "Failed to spawn assistant");
@@ -3983,10 +4183,20 @@ var TelegramAdapter = class extends ChannelAdapter {
3983
4183
  this.bot.on("message:text", async (ctx) => {
3984
4184
  const threadId = ctx.message.message_thread_id;
3985
4185
  const text = ctx.message.text;
3986
- if (await handlePendingWorkspaceInput(ctx, this.core, this.telegramConfig.chatId, this.assistantTopicId)) {
4186
+ if (await handlePendingWorkspaceInput(
4187
+ ctx,
4188
+ this.core,
4189
+ this.telegramConfig.chatId,
4190
+ this.assistantTopicId
4191
+ )) {
3987
4192
  return;
3988
4193
  }
3989
- if (await handlePendingResumeInput(ctx, this.core, this.telegramConfig.chatId, this.assistantTopicId)) {
4194
+ if (await handlePendingResumeInput(
4195
+ ctx,
4196
+ this.core,
4197
+ this.telegramConfig.chatId,
4198
+ this.assistantTopicId
4199
+ )) {
3990
4200
  return;
3991
4201
  }
3992
4202
  if (!threadId) {
@@ -4001,10 +4211,16 @@ var TelegramAdapter = class extends ChannelAdapter {
4001
4211
  const forwardText = text.startsWith("/") ? text.slice(1) : text;
4002
4212
  if (threadId === this.assistantTopicId) {
4003
4213
  if (!this.assistantSession) {
4004
- await ctx.reply("\u26A0\uFE0F Assistant is not available yet. Please try again shortly.", { parse_mode: "HTML" });
4214
+ await ctx.reply(
4215
+ "\u26A0\uFE0F Assistant is not available yet. Please try again shortly.",
4216
+ { parse_mode: "HTML" }
4217
+ );
4005
4218
  return;
4006
4219
  }
4007
- await this.draftManager.finalize(this.assistantSession.id, this.assistantSession.id);
4220
+ await this.draftManager.finalize(
4221
+ this.assistantSession.id,
4222
+ this.assistantSession.id
4223
+ );
4008
4224
  ctx.replyWithChatAction("typing").catch(() => {
4009
4225
  });
4010
4226
  handleAssistantMessage(this.assistantSession, forwardText).catch(
@@ -4012,8 +4228,12 @@ var TelegramAdapter = class extends ChannelAdapter {
4012
4228
  );
4013
4229
  return;
4014
4230
  }
4015
- const sessionId = this.core.sessionManager.getSessionByThread("telegram", String(threadId))?.id;
4016
- if (sessionId) await this.draftManager.finalize(sessionId, this.assistantSession?.id);
4231
+ const sessionId = this.core.sessionManager.getSessionByThread(
4232
+ "telegram",
4233
+ String(threadId)
4234
+ )?.id;
4235
+ if (sessionId)
4236
+ await this.draftManager.finalize(sessionId, this.assistantSession?.id);
4017
4237
  if (sessionId) {
4018
4238
  const tracker = this.sessionTrackers.get(sessionId);
4019
4239
  if (tracker) await tracker.onNewPrompt();
@@ -4112,25 +4332,57 @@ var TelegramAdapter = class extends ChannelAdapter {
4112
4332
  this.draftManager.appendText(ctx.sessionId, content.text);
4113
4333
  },
4114
4334
  onToolCall: async (ctx, content) => {
4335
+ const meta = content.metadata ?? {};
4336
+ const toolName = meta.name ?? content.text ?? "Tool";
4337
+ const toolKind = String(meta.kind ?? "other");
4338
+ const noiseAction = evaluateNoise(toolName, toolKind, meta.rawInput);
4339
+ if (noiseAction === "hide" && this.verbosity !== "high") return;
4340
+ if (noiseAction === "collapse" && this.verbosity === "low") return;
4115
4341
  const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.threadId);
4116
4342
  await tracker.onToolCall();
4117
- await this.draftManager.finalize(ctx.sessionId, this.assistantSession?.id);
4118
- const meta = content.metadata;
4119
- await this.toolTracker.trackNewCall(ctx.sessionId, ctx.threadId, {
4120
- ...meta
4121
- });
4343
+ await this.draftManager.finalize(
4344
+ ctx.sessionId,
4345
+ this.assistantSession?.id
4346
+ );
4347
+ await this.toolTracker.trackNewCall(
4348
+ ctx.sessionId,
4349
+ ctx.threadId,
4350
+ {
4351
+ id: meta.id ?? "",
4352
+ name: meta.name ?? content.text ?? "Tool",
4353
+ kind: meta.kind,
4354
+ status: meta.status,
4355
+ content: meta.content,
4356
+ rawInput: meta.rawInput,
4357
+ viewerLinks: meta.viewerLinks,
4358
+ viewerFilePath: meta.viewerFilePath
4359
+ },
4360
+ this.verbosity
4361
+ );
4122
4362
  },
4123
4363
  onToolUpdate: async (ctx, content) => {
4124
- const meta = content.metadata;
4125
- await this.toolTracker.updateCall(ctx.sessionId, {
4126
- ...meta
4127
- });
4364
+ const meta = content.metadata ?? {};
4365
+ await this.toolTracker.updateCall(
4366
+ ctx.sessionId,
4367
+ {
4368
+ id: meta.id ?? "",
4369
+ name: meta.name ?? content.text ?? "",
4370
+ kind: meta.kind,
4371
+ status: meta.status ?? "completed",
4372
+ content: meta.content,
4373
+ rawInput: meta.rawInput,
4374
+ viewerLinks: meta.viewerLinks,
4375
+ viewerFilePath: meta.viewerFilePath
4376
+ },
4377
+ this.verbosity
4378
+ );
4128
4379
  },
4129
4380
  onPlan: async (ctx, content) => {
4130
- const meta = content.metadata;
4381
+ const meta = content.metadata ?? {};
4382
+ const entries = meta.entries ?? [];
4131
4383
  const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.threadId);
4132
4384
  await tracker.onPlan(
4133
- meta.entries.map((e) => ({
4385
+ entries.map((e) => ({
4134
4386
  content: e.content,
4135
4387
  status: e.status,
4136
4388
  priority: e.priority ?? "medium"
@@ -4139,7 +4391,10 @@ var TelegramAdapter = class extends ChannelAdapter {
4139
4391
  },
4140
4392
  onUsage: async (ctx, content) => {
4141
4393
  const meta = content.metadata;
4142
- await this.draftManager.finalize(ctx.sessionId, this.assistantSession?.id);
4394
+ await this.draftManager.finalize(
4395
+ ctx.sessionId,
4396
+ this.assistantSession?.id
4397
+ );
4143
4398
  const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.threadId);
4144
4399
  await tracker.sendUsage(meta ?? {});
4145
4400
  if (this.notificationTopicId && ctx.sessionId !== this.assistantSession?.id) {
@@ -4167,7 +4422,14 @@ Task completed.
4167
4422
  if (!content.attachment) return;
4168
4423
  const { attachment } = content;
4169
4424
  if (attachment.size > 50 * 1024 * 1024) {
4170
- log13.warn({ sessionId: ctx.sessionId, fileName: attachment.fileName, size: attachment.size }, "File too large for Telegram (>50MB)");
4425
+ log13.warn(
4426
+ {
4427
+ sessionId: ctx.sessionId,
4428
+ fileName: attachment.fileName,
4429
+ size: attachment.size
4430
+ },
4431
+ "File too large for Telegram (>50MB)"
4432
+ );
4171
4433
  await this.sendQueue.enqueue(
4172
4434
  () => this.bot.api.sendMessage(
4173
4435
  this.telegramConfig.chatId,
@@ -4204,11 +4466,17 @@ Task completed.
4204
4466
  );
4205
4467
  }
4206
4468
  } catch (err) {
4207
- log13.error({ err, sessionId: ctx.sessionId, fileName: attachment.fileName }, "Failed to send attachment");
4469
+ log13.error(
4470
+ { err, sessionId: ctx.sessionId, fileName: attachment.fileName },
4471
+ "Failed to send attachment"
4472
+ );
4208
4473
  }
4209
4474
  },
4210
4475
  onSessionEnd: async (ctx, _content) => {
4211
- await this.draftManager.finalize(ctx.sessionId, this.assistantSession?.id);
4476
+ await this.draftManager.finalize(
4477
+ ctx.sessionId,
4478
+ this.assistantSession?.id
4479
+ );
4212
4480
  this.draftManager.cleanup(ctx.sessionId);
4213
4481
  this.toolTracker.cleanup(ctx.sessionId);
4214
4482
  await this.skillManager.cleanup(ctx.sessionId);
@@ -4232,7 +4500,10 @@ Task completed.
4232
4500
  }
4233
4501
  },
4234
4502
  onError: async (ctx, content) => {
4235
- await this.draftManager.finalize(ctx.sessionId, this.assistantSession?.id);
4503
+ await this.draftManager.finalize(
4504
+ ctx.sessionId,
4505
+ this.assistantSession?.id
4506
+ );
4236
4507
  const tracker = this.sessionTrackers.get(ctx.sessionId);
4237
4508
  if (tracker) {
4238
4509
  tracker.destroy();
@@ -4266,17 +4537,21 @@ Task completed.
4266
4537
  };
4267
4538
  // --- ChannelAdapter implementations ---
4268
4539
  async sendMessage(sessionId, content) {
4269
- if (this.assistantInitializing && sessionId === this.assistantSession?.id) return;
4540
+ if (this.assistantInitializing && sessionId === this.assistantSession?.id)
4541
+ return;
4270
4542
  const session = this.core.sessionManager.getSession(sessionId);
4271
4543
  if (!session) return;
4272
4544
  if (session.archiving) return;
4273
4545
  const threadId = Number(session.threadId);
4274
4546
  if (!threadId || isNaN(threadId)) {
4275
- log13.warn({ sessionId, threadId: session.threadId }, "Session has no valid threadId, skipping message");
4547
+ log13.warn(
4548
+ { sessionId, threadId: session.threadId },
4549
+ "Session has no valid threadId, skipping message"
4550
+ );
4276
4551
  return;
4277
4552
  }
4278
4553
  const ctx = { sessionId, threadId };
4279
- await dispatchMessage(this.messageHandlers, ctx, content);
4554
+ await dispatchMessage(this.messageHandlers, ctx, content, this.verbosity);
4280
4555
  }
4281
4556
  async sendPermissionRequest(sessionId, request) {
4282
4557
  log13.info({ sessionId, requestId: request.id }, "Permission request sent");
@@ -4303,7 +4578,9 @@ Task completed.
4303
4578
  `;
4304
4579
  text += escapeHtml(notification.summary);
4305
4580
  const deepLink = notification.deepLink ?? (() => {
4306
- const session = this.core.sessionManager.getSession(notification.sessionId);
4581
+ const session = this.core.sessionManager.getSession(
4582
+ notification.sessionId
4583
+ );
4307
4584
  const threadId = session?.threadId;
4308
4585
  if (!threadId) return void 0;
4309
4586
  const chatIdStr = String(this.telegramConfig.chatId);
@@ -4315,11 +4592,13 @@ Task completed.
4315
4592
 
4316
4593
  <a href="${deepLink}">\u2192 Go to topic</a>`;
4317
4594
  }
4595
+ const replyMarkup = notification.type === "completed" ? { inline_keyboard: [[{ text: "\u{1F4CB} Summary", callback_data: `sm:summary:${notification.sessionId}` }]] } : void 0;
4318
4596
  await this.sendQueue.enqueue(
4319
4597
  () => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
4320
4598
  message_thread_id: this.notificationTopicId,
4321
4599
  parse_mode: "HTML",
4322
- disable_notification: false
4600
+ disable_notification: false,
4601
+ reply_markup: replyMarkup
4323
4602
  })
4324
4603
  );
4325
4604
  }
@@ -4348,7 +4627,10 @@ Task completed.
4348
4627
  try {
4349
4628
  await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
4350
4629
  } catch (err) {
4351
- log13.warn({ err, sessionId, topicId }, "Failed to delete forum topic (may already be deleted)");
4630
+ log13.warn(
4631
+ { err, sessionId, topicId },
4632
+ "Failed to delete forum topic (may already be deleted)"
4633
+ );
4352
4634
  }
4353
4635
  }
4354
4636
  async sendSkillCommands(sessionId, commands) {
@@ -4360,7 +4642,10 @@ Task completed.
4360
4642
  await this.skillManager.send(sessionId, threadId, commands);
4361
4643
  }
4362
4644
  resolveSessionId(threadId) {
4363
- return this.core.sessionManager.getSessionByThread("telegram", String(threadId))?.id;
4645
+ return this.core.sessionManager.getSessionByThread(
4646
+ "telegram",
4647
+ String(threadId)
4648
+ )?.id;
4364
4649
  }
4365
4650
  async downloadTelegramFile(fileId) {
4366
4651
  try {
@@ -4383,7 +4668,12 @@ Task completed.
4383
4668
  let originalFilePath;
4384
4669
  const sessionId = this.resolveSessionId(threadId) || "unknown";
4385
4670
  if (convertOggToWav) {
4386
- const oggAtt = await this.fileService.saveFile(sessionId, "voice.ogg", downloaded.buffer, "audio/ogg");
4671
+ const oggAtt = await this.fileService.saveFile(
4672
+ sessionId,
4673
+ "voice.ogg",
4674
+ downloaded.buffer,
4675
+ "audio/ogg"
4676
+ );
4387
4677
  originalFilePath = oggAtt.filePath;
4388
4678
  try {
4389
4679
  buffer = await this.fileService.convertOggToWav(buffer);
@@ -4394,7 +4684,12 @@ Task completed.
4394
4684
  originalFilePath = void 0;
4395
4685
  }
4396
4686
  }
4397
- const att = await this.fileService.saveFile(sessionId, fileName, buffer, mimeType);
4687
+ const att = await this.fileService.saveFile(
4688
+ sessionId,
4689
+ fileName,
4690
+ buffer,
4691
+ mimeType
4692
+ );
4398
4693
  if (originalFilePath) {
4399
4694
  att.originalFilePath = originalFilePath;
4400
4695
  }
@@ -4422,10 +4717,9 @@ Task completed.
4422
4717
  async archiveSessionTopic(sessionId) {
4423
4718
  const core = this.core;
4424
4719
  const session = core.sessionManager.getSession(sessionId);
4425
- if (!session) return null;
4720
+ if (!session) return;
4426
4721
  const chatId = this.telegramConfig.chatId;
4427
4722
  const oldTopicId = Number(session.threadId);
4428
- const rawName = (session.name || `Session ${session.id.slice(0, 6)}`).replace(/^🔄\s*/, "");
4429
4723
  session.archiving = true;
4430
4724
  await this.draftManager.finalize(session.id, this.assistantSession?.id);
4431
4725
  this.draftManager.cleanup(session.id);
@@ -4437,32 +4731,10 @@ Task completed.
4437
4731
  this.sessionTrackers.delete(session.id);
4438
4732
  }
4439
4733
  await deleteSessionTopic(this.bot, chatId, oldTopicId);
4440
- let newTopicId;
4441
- try {
4442
- newTopicId = await createSessionTopic(this.bot, chatId, `\u{1F504} ${rawName}`);
4443
- } catch (createErr) {
4444
- session.archiving = false;
4445
- core.notificationManager.notifyAll({
4446
- sessionId: session.id,
4447
- sessionName: session.name,
4448
- type: "error",
4449
- summary: `Topic recreation failed for session "${rawName}". Session is orphaned. Error: ${createErr.message}`
4450
- });
4451
- throw createErr;
4452
- }
4453
- session.threadId = String(newTopicId);
4454
- const existingRecord = core.sessionManager.getSessionRecord(session.id);
4455
- const existingPlatform = { ...existingRecord?.platform ?? {} };
4456
- delete existingPlatform.skillMsgId;
4457
- await core.sessionManager.patchRecord(session.id, {
4458
- platform: { ...existingPlatform, topicId: newTopicId }
4459
- });
4460
- session.archiving = false;
4461
- return { newThreadId: String(newTopicId) };
4462
4734
  }
4463
4735
  };
4464
4736
 
4465
4737
  export {
4466
4738
  TelegramAdapter
4467
4739
  };
4468
- //# sourceMappingURL=chunk-AHPRT3RY.js.map
4740
+ //# sourceMappingURL=chunk-5SXG7X5D.js.map