@openacp/cli 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapter-YSEIZJBA.js → adapter-7GY3N4ZH.js} +9 -9
- package/dist/{admin-SCP25TN2.js → admin-2HAFXQBG.js} +6 -4
- package/dist/{chunk-WVMSP4AF.js → chunk-2J2RBYWN.js} +2 -2
- package/dist/{chunk-ZKTIZME6.js → chunk-47B7GNOE.js} +2 -2
- package/dist/{chunk-XVL6AGMG.js → chunk-5OVPEDUB.js} +2 -2
- package/dist/{chunk-FW6HM4VU.js → chunk-5SXG7X5D.js} +862 -303
- package/dist/chunk-5SXG7X5D.js.map +1 -0
- package/dist/{chunk-F3AICYO4.js → chunk-JHYXKVV2.js} +19 -1
- package/dist/chunk-JHYXKVV2.js.map +1 -0
- package/dist/{chunk-FCLGYYTY.js → chunk-JUYDFUSN.js} +224 -2
- package/dist/chunk-JUYDFUSN.js.map +1 -0
- package/dist/{chunk-774Y4RAK.js → chunk-KIRH7TUJ.js} +94 -24
- package/dist/chunk-KIRH7TUJ.js.map +1 -0
- package/dist/{chunk-4GQ3I65A.js → chunk-LBIKITQT.js} +1 -2
- package/dist/{chunk-4GQ3I65A.js.map → chunk-LBIKITQT.js.map} +1 -1
- package/dist/{chunk-FEWSQT3U.js → chunk-LO4Y5WQ7.js} +1014 -83
- package/dist/chunk-LO4Y5WQ7.js.map +1 -0
- package/dist/{chunk-3IRAWHMC.js → chunk-MZXWCDBU.js} +3 -3
- package/dist/{chunk-YQRF3IOR.js → chunk-O7CPGUAI.js} +2 -2
- package/dist/{chunk-7KZI2236.js → chunk-RHE2JSYE.js} +2 -2
- package/dist/{chunk-3ZO3MHZN.js → chunk-SHHMBGB3.js} +4 -3
- package/dist/chunk-SHHMBGB3.js.map +1 -0
- package/dist/{chunk-JV6XQRAE.js → chunk-XANPHG7W.js} +2 -2
- package/dist/{chunk-PJVKOZTR.js → chunk-YEOY2PBJ.js} +2 -2
- package/dist/cli.js +21 -21
- package/dist/{config-B26J3XXN.js → config-CQAS6YHR.js} +2 -2
- package/dist/{config-editor-QTGUK3CD.js → config-editor-37BM56WF.js} +4 -4
- package/dist/{config-registry-7I6GGDOY.js → config-registry-HDXFES2D.js} +2 -2
- package/dist/{daemon-5DS5BQXJ.js → daemon-K33ZPSEZ.js} +3 -3
- package/dist/{discord-QKT3JMRW.js → discord-VOHXRTCH.js} +113 -131
- package/dist/discord-VOHXRTCH.js.map +1 -0
- package/dist/{doctor-QQ3YZEYV.js → doctor-HASEBMUD.js} +4 -4
- package/dist/doctor-W4VGLDVM.js +9 -0
- package/dist/index.d.ts +105 -10
- package/dist/index.js +15 -11
- package/dist/{main-TSZR4HPP.js → main-DUXVFTDD.js} +19 -19
- package/dist/{new-session-K6UCWYOP.js → new-session-NHK7TOEW.js} +3 -3
- package/dist/{settings-RRF77IC4.js → settings-6TF4WIGJ.js} +3 -3
- package/dist/{setup-5ZKSUR26.js → setup-RJCEB6FS.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-3ZO3MHZN.js.map +0 -1
- package/dist/chunk-774Y4RAK.js.map +0 -1
- package/dist/chunk-F3AICYO4.js.map +0 -1
- package/dist/chunk-FCLGYYTY.js.map +0 -1
- package/dist/chunk-FEWSQT3U.js.map +0 -1
- package/dist/chunk-FW6HM4VU.js.map +0 -1
- package/dist/discord-QKT3JMRW.js.map +0 -1
- package/dist/doctor-6SUCVUZB.js +0 -9
- /package/dist/{adapter-YSEIZJBA.js.map → adapter-7GY3N4ZH.js.map} +0 -0
- /package/dist/{admin-SCP25TN2.js.map → admin-2HAFXQBG.js.map} +0 -0
- /package/dist/{chunk-WVMSP4AF.js.map → chunk-2J2RBYWN.js.map} +0 -0
- /package/dist/{chunk-ZKTIZME6.js.map → chunk-47B7GNOE.js.map} +0 -0
- /package/dist/{chunk-XVL6AGMG.js.map → chunk-5OVPEDUB.js.map} +0 -0
- /package/dist/{chunk-3IRAWHMC.js.map → chunk-MZXWCDBU.js.map} +0 -0
- /package/dist/{chunk-YQRF3IOR.js.map → chunk-O7CPGUAI.js.map} +0 -0
- /package/dist/{chunk-7KZI2236.js.map → chunk-RHE2JSYE.js.map} +0 -0
- /package/dist/{chunk-JV6XQRAE.js.map → chunk-XANPHG7W.js.map} +0 -0
- /package/dist/{chunk-PJVKOZTR.js.map → chunk-YEOY2PBJ.js.map} +0 -0
- /package/dist/{config-B26J3XXN.js.map → config-CQAS6YHR.js.map} +0 -0
- /package/dist/{config-editor-QTGUK3CD.js.map → config-editor-37BM56WF.js.map} +0 -0
- /package/dist/{config-registry-7I6GGDOY.js.map → config-registry-HDXFES2D.js.map} +0 -0
- /package/dist/{daemon-5DS5BQXJ.js.map → daemon-K33ZPSEZ.js.map} +0 -0
- /package/dist/{doctor-6SUCVUZB.js.map → doctor-HASEBMUD.js.map} +0 -0
- /package/dist/{doctor-QQ3YZEYV.js.map → doctor-W4VGLDVM.js.map} +0 -0
- /package/dist/{main-TSZR4HPP.js.map → main-DUXVFTDD.js.map} +0 -0
- /package/dist/{new-session-K6UCWYOP.js.map → new-session-NHK7TOEW.js.map} +0 -0
- /package/dist/{settings-RRF77IC4.js.map → settings-6TF4WIGJ.js.map} +0 -0
- /package/dist/{setup-5ZKSUR26.js.map → setup-RJCEB6FS.js.map} +0 -0
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRODUCT_GUIDE,
|
|
3
|
-
|
|
4
|
-
|
|
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-
|
|
17
|
+
} from "./chunk-YEOY2PBJ.js";
|
|
8
18
|
import {
|
|
9
19
|
buildMenuKeyboard,
|
|
10
20
|
buildSkillMessages,
|
|
@@ -12,15 +22,19 @@ import {
|
|
|
12
22
|
handleHelp,
|
|
13
23
|
handleMenu
|
|
14
24
|
} from "./chunk-7QJS2XBD.js";
|
|
25
|
+
import {
|
|
26
|
+
CheckpointReader,
|
|
27
|
+
DEFAULT_MAX_TOKENS
|
|
28
|
+
} from "./chunk-LO4Y5WQ7.js";
|
|
15
29
|
import {
|
|
16
30
|
ChannelAdapter
|
|
17
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-LBIKITQT.js";
|
|
18
32
|
import {
|
|
19
33
|
getConfigValue,
|
|
20
34
|
getSafeFields,
|
|
21
35
|
isHotReloadable,
|
|
22
36
|
resolveOptions
|
|
23
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-JHYXKVV2.js";
|
|
24
38
|
import {
|
|
25
39
|
createChildLogger
|
|
26
40
|
} from "./chunk-GAK6PIBW.js";
|
|
@@ -76,13 +90,16 @@ function escapeHtml(text) {
|
|
|
76
90
|
function markdownToTelegramHtml(md) {
|
|
77
91
|
const codeBlocks = [];
|
|
78
92
|
const inlineCodes = [];
|
|
79
|
-
let text = md.replace(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
+
);
|
|
86
103
|
text = text.replace(/`([^`]+)`/g, (_match, code) => {
|
|
87
104
|
const index = inlineCodes.length;
|
|
88
105
|
inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
|
|
@@ -100,72 +117,32 @@ function markdownToTelegramHtml(md) {
|
|
|
100
117
|
});
|
|
101
118
|
return text;
|
|
102
119
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
109
|
-
var KIND_ICON = {
|
|
110
|
-
read: "\u{1F4D6}",
|
|
111
|
-
edit: "\u270F\uFE0F",
|
|
112
|
-
delete: "\u{1F5D1}\uFE0F",
|
|
113
|
-
execute: "\u25B6\uFE0F",
|
|
114
|
-
search: "\u{1F50D}",
|
|
115
|
-
fetch: "\u{1F310}",
|
|
116
|
-
think: "\u{1F9E0}",
|
|
117
|
-
move: "\u{1F4E6}",
|
|
118
|
-
other: "\u{1F6E0}\uFE0F"
|
|
119
|
-
};
|
|
120
|
-
function extractContentText(content, depth = 0) {
|
|
121
|
-
if (!content || depth > 5) return "";
|
|
122
|
-
if (typeof content === "string") return content;
|
|
123
|
-
if (Array.isArray(content)) {
|
|
124
|
-
return content.map((c) => extractContentText(c, depth + 1)).filter(Boolean).join("\n");
|
|
125
|
-
}
|
|
126
|
-
if (typeof content === "object" && content !== null) {
|
|
127
|
-
const c = content;
|
|
128
|
-
if (c.type === "text" && typeof c.text === "string") return c.text;
|
|
129
|
-
if (typeof c.text === "string") return c.text;
|
|
130
|
-
if (typeof c.content === "string") return c.content;
|
|
131
|
-
if (c.content && typeof c.content === "object") return extractContentText(c.content, depth + 1);
|
|
132
|
-
if (c.input) return extractContentText(c.input, depth + 1);
|
|
133
|
-
if (c.output) return extractContentText(c.output, depth + 1);
|
|
134
|
-
const keys = Object.keys(c).filter((k) => k !== "type");
|
|
135
|
-
if (keys.length === 0) return "";
|
|
136
|
-
return JSON.stringify(c, null, 2);
|
|
137
|
-
}
|
|
138
|
-
return String(content);
|
|
139
|
-
}
|
|
140
|
-
function truncateContent(text, maxLen = 3800) {
|
|
141
|
-
if (text.length <= maxLen) return text;
|
|
142
|
-
return text.slice(0, maxLen) + "\n\u2026 (truncated)";
|
|
143
|
-
}
|
|
144
|
-
function formatToolCall(tool) {
|
|
145
|
-
const si = STATUS_ICON[tool.status || ""] || "\u{1F527}";
|
|
146
|
-
const ki = KIND_ICON[tool.kind || ""] || "\u{1F6E0}\uFE0F";
|
|
147
|
-
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>`;
|
|
148
125
|
text += formatViewerLinks(tool.viewerLinks, tool.viewerFilePath);
|
|
149
|
-
if (!tool.viewerLinks) {
|
|
150
|
-
const details = extractContentText(tool.content);
|
|
126
|
+
if (verbosity === "high" || verbosity === "medium" && !tool.viewerLinks) {
|
|
127
|
+
const details = stripCodeFences(extractContentText(tool.content));
|
|
151
128
|
if (details) {
|
|
152
129
|
text += `
|
|
153
|
-
<pre>${escapeHtml(truncateContent(details))}</pre>`;
|
|
130
|
+
<pre>${escapeHtml(truncateContent(details, 3800))}</pre>`;
|
|
154
131
|
}
|
|
155
132
|
}
|
|
156
133
|
return text;
|
|
157
134
|
}
|
|
158
|
-
function formatToolUpdate(update) {
|
|
159
|
-
const si =
|
|
160
|
-
const ki = KIND_ICON[update.kind || ""] || "\u{1F6E0}\uFE0F";
|
|
135
|
+
function formatToolUpdate(update, verbosity = "medium") {
|
|
136
|
+
const si = STATUS_ICONS[update.status] || "\u{1F527}";
|
|
161
137
|
const name = update.name || "Tool";
|
|
162
|
-
|
|
138
|
+
const label = verbosity === "low" ? formatToolTitle(name, update.rawInput) : formatToolSummary(name, update.rawInput);
|
|
139
|
+
let text = `${si} <b>${escapeHtml(label)}</b>`;
|
|
163
140
|
text += formatViewerLinks(update.viewerLinks, update.viewerFilePath);
|
|
164
|
-
if (!update.viewerLinks) {
|
|
165
|
-
const details = extractContentText(update.content);
|
|
141
|
+
if (verbosity === "high" || verbosity === "medium" && !update.viewerLinks) {
|
|
142
|
+
const details = stripCodeFences(extractContentText(update.content));
|
|
166
143
|
if (details) {
|
|
167
144
|
text += `
|
|
168
|
-
<pre>${escapeHtml(truncateContent(details))}</pre>`;
|
|
145
|
+
<pre>${escapeHtml(truncateContent(details, 3800))}</pre>`;
|
|
169
146
|
}
|
|
170
147
|
}
|
|
171
148
|
return text;
|
|
@@ -174,19 +151,14 @@ function formatViewerLinks(links, filePath) {
|
|
|
174
151
|
if (!links) return "";
|
|
175
152
|
const fileName = filePath ? filePath.split("/").pop() || filePath : "";
|
|
176
153
|
let text = "\n";
|
|
177
|
-
if (links.file)
|
|
154
|
+
if (links.file)
|
|
155
|
+
text += `
|
|
178
156
|
\u{1F4C4} <a href="${escapeHtml(links.file)}">View ${escapeHtml(fileName || "file")}</a>`;
|
|
179
|
-
if (links.diff)
|
|
157
|
+
if (links.diff)
|
|
158
|
+
text += `
|
|
180
159
|
\u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
|
|
181
160
|
return text;
|
|
182
161
|
}
|
|
183
|
-
function formatTokens(n) {
|
|
184
|
-
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
185
|
-
}
|
|
186
|
-
function progressBar(ratio) {
|
|
187
|
-
const filled = Math.round(Math.min(ratio, 1) * 10);
|
|
188
|
-
return "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
189
|
-
}
|
|
190
162
|
function formatUsage(usage) {
|
|
191
163
|
const { tokensUsed, contextSize } = usage;
|
|
192
164
|
if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
|
|
@@ -213,49 +185,30 @@ function formatUsageReport(summaries, budgetStatus) {
|
|
|
213
185
|
const lines = ["\u{1F4CA} <b>Usage Report</b>"];
|
|
214
186
|
for (const summary of summaries) {
|
|
215
187
|
lines.push("");
|
|
216
|
-
lines.push(
|
|
188
|
+
lines.push(
|
|
189
|
+
`\u2500\u2500 <b>${PERIOD_LABEL[summary.period] ?? summary.period}</b> \u2500\u2500`
|
|
190
|
+
);
|
|
217
191
|
lines.push(
|
|
218
192
|
`\u{1F4B0} ${formatCost(summary.totalCost)} \xB7 \u{1F524} ${formatTokens(summary.totalTokens)} tokens \xB7 \u{1F4CB} ${summary.sessionCount} sessions`
|
|
219
193
|
);
|
|
220
194
|
if (summary.period === "month" && budgetStatus.budget > 0) {
|
|
221
195
|
const bar = progressBar(budgetStatus.used / budgetStatus.budget);
|
|
222
|
-
lines.push(
|
|
196
|
+
lines.push(
|
|
197
|
+
`Budget: ${formatCost(budgetStatus.used)} / ${formatCost(budgetStatus.budget)} (${budgetStatus.percent}%)`
|
|
198
|
+
);
|
|
223
199
|
lines.push(`${bar} ${budgetStatus.percent}%`);
|
|
224
200
|
}
|
|
225
201
|
}
|
|
226
202
|
return lines.join("\n");
|
|
227
203
|
}
|
|
228
|
-
function
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
const wouldLeaveSmall = remaining.length < maxLength * 1.3;
|
|
238
|
-
const searchLimit = wouldLeaveSmall ? Math.floor(remaining.length / 2) + 300 : maxLength;
|
|
239
|
-
let splitAt = remaining.lastIndexOf("\n\n", searchLimit);
|
|
240
|
-
if (splitAt === -1 || splitAt < searchLimit * 0.2) {
|
|
241
|
-
splitAt = remaining.lastIndexOf("\n", searchLimit);
|
|
242
|
-
}
|
|
243
|
-
if (splitAt === -1 || splitAt < searchLimit * 0.2) {
|
|
244
|
-
splitAt = searchLimit;
|
|
245
|
-
}
|
|
246
|
-
const candidate = remaining.slice(0, splitAt);
|
|
247
|
-
const fences = candidate.match(/```/g);
|
|
248
|
-
if (fences && fences.length % 2 !== 0) {
|
|
249
|
-
const closingFence = remaining.indexOf("```", splitAt);
|
|
250
|
-
if (closingFence !== -1) {
|
|
251
|
-
const afterFence = remaining.indexOf("\n", closingFence + 3);
|
|
252
|
-
splitAt = afterFence !== -1 ? afterFence + 1 : closingFence + 3;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
chunks.push(remaining.slice(0, splitAt));
|
|
256
|
-
remaining = remaining.slice(splitAt).replace(/^\n+/, "");
|
|
257
|
-
}
|
|
258
|
-
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);
|
|
259
212
|
}
|
|
260
213
|
|
|
261
214
|
// src/adapters/telegram/commands/admin.ts
|
|
@@ -267,7 +220,10 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
267
220
|
const session = core.sessionManager.getSession(sessionId);
|
|
268
221
|
if (session) {
|
|
269
222
|
session.dangerousMode = !session.dangerousMode;
|
|
270
|
-
log.info(
|
|
223
|
+
log.info(
|
|
224
|
+
{ sessionId, dangerousMode: session.dangerousMode },
|
|
225
|
+
"Dangerous mode toggled via button"
|
|
226
|
+
);
|
|
271
227
|
core.sessionManager.patchRecord(sessionId, { dangerousMode: session.dangerousMode }).catch(() => {
|
|
272
228
|
});
|
|
273
229
|
const toastText2 = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
@@ -277,7 +233,11 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
277
233
|
}
|
|
278
234
|
try {
|
|
279
235
|
await ctx.editMessageReplyMarkup({
|
|
280
|
-
reply_markup: buildSessionControlKeyboard(
|
|
236
|
+
reply_markup: buildSessionControlKeyboard(
|
|
237
|
+
sessionId,
|
|
238
|
+
session.dangerousMode,
|
|
239
|
+
session.voiceMode === "on"
|
|
240
|
+
)
|
|
281
241
|
});
|
|
282
242
|
} catch {
|
|
283
243
|
}
|
|
@@ -286,7 +246,9 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
286
246
|
const record = core.sessionManager.getSessionRecord(sessionId);
|
|
287
247
|
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
288
248
|
try {
|
|
289
|
-
await ctx.answerCallbackQuery({
|
|
249
|
+
await ctx.answerCallbackQuery({
|
|
250
|
+
text: "\u26A0\uFE0F Session not found or already ended."
|
|
251
|
+
});
|
|
290
252
|
} catch {
|
|
291
253
|
}
|
|
292
254
|
return;
|
|
@@ -294,7 +256,10 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
294
256
|
const newDangerousMode = !(record.dangerousMode ?? false);
|
|
295
257
|
core.sessionManager.patchRecord(sessionId, { dangerousMode: newDangerousMode }).catch(() => {
|
|
296
258
|
});
|
|
297
|
-
log.info(
|
|
259
|
+
log.info(
|
|
260
|
+
{ sessionId, dangerousMode: newDangerousMode },
|
|
261
|
+
"Dangerous mode toggled via button (store-only, session not in memory)"
|
|
262
|
+
);
|
|
298
263
|
const toastText = newDangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
299
264
|
try {
|
|
300
265
|
await ctx.answerCallbackQuery({ text: toastText });
|
|
@@ -302,7 +267,11 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
302
267
|
}
|
|
303
268
|
try {
|
|
304
269
|
await ctx.editMessageReplyMarkup({
|
|
305
|
-
reply_markup: buildSessionControlKeyboard(
|
|
270
|
+
reply_markup: buildSessionControlKeyboard(
|
|
271
|
+
sessionId,
|
|
272
|
+
newDangerousMode,
|
|
273
|
+
false
|
|
274
|
+
)
|
|
306
275
|
});
|
|
307
276
|
} catch {
|
|
308
277
|
}
|
|
@@ -311,26 +280,40 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
311
280
|
async function handleEnableDangerous(ctx, core) {
|
|
312
281
|
const threadId = ctx.message?.message_thread_id;
|
|
313
282
|
if (!threadId) {
|
|
314
|
-
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
|
|
283
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
|
|
284
|
+
parse_mode: "HTML"
|
|
285
|
+
});
|
|
315
286
|
return;
|
|
316
287
|
}
|
|
317
|
-
const session = core.sessionManager.getSessionByThread(
|
|
288
|
+
const session = core.sessionManager.getSessionByThread(
|
|
289
|
+
"telegram",
|
|
290
|
+
String(threadId)
|
|
291
|
+
);
|
|
318
292
|
if (session) {
|
|
319
293
|
if (session.dangerousMode) {
|
|
320
|
-
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", {
|
|
294
|
+
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", {
|
|
295
|
+
parse_mode: "HTML"
|
|
296
|
+
});
|
|
321
297
|
return;
|
|
322
298
|
}
|
|
323
299
|
session.dangerousMode = true;
|
|
324
300
|
core.sessionManager.patchRecord(session.id, { dangerousMode: true }).catch(() => {
|
|
325
301
|
});
|
|
326
302
|
} else {
|
|
327
|
-
const record = core.sessionManager.getRecordByThread(
|
|
303
|
+
const record = core.sessionManager.getRecordByThread(
|
|
304
|
+
"telegram",
|
|
305
|
+
String(threadId)
|
|
306
|
+
);
|
|
328
307
|
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
329
|
-
await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
|
|
308
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
|
|
309
|
+
parse_mode: "HTML"
|
|
310
|
+
});
|
|
330
311
|
return;
|
|
331
312
|
}
|
|
332
313
|
if (record.dangerousMode) {
|
|
333
|
-
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", {
|
|
314
|
+
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", {
|
|
315
|
+
parse_mode: "HTML"
|
|
316
|
+
});
|
|
334
317
|
return;
|
|
335
318
|
}
|
|
336
319
|
core.sessionManager.patchRecord(record.sessionId, { dangerousMode: true }).catch(() => {
|
|
@@ -348,32 +331,49 @@ Use /disable_dangerous to restore normal behaviour.`,
|
|
|
348
331
|
async function handleDisableDangerous(ctx, core) {
|
|
349
332
|
const threadId = ctx.message?.message_thread_id;
|
|
350
333
|
if (!threadId) {
|
|
351
|
-
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
|
|
334
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
|
|
335
|
+
parse_mode: "HTML"
|
|
336
|
+
});
|
|
352
337
|
return;
|
|
353
338
|
}
|
|
354
|
-
const session = core.sessionManager.getSessionByThread(
|
|
339
|
+
const session = core.sessionManager.getSessionByThread(
|
|
340
|
+
"telegram",
|
|
341
|
+
String(threadId)
|
|
342
|
+
);
|
|
355
343
|
if (session) {
|
|
356
344
|
if (!session.dangerousMode) {
|
|
357
|
-
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", {
|
|
345
|
+
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", {
|
|
346
|
+
parse_mode: "HTML"
|
|
347
|
+
});
|
|
358
348
|
return;
|
|
359
349
|
}
|
|
360
350
|
session.dangerousMode = false;
|
|
361
351
|
core.sessionManager.patchRecord(session.id, { dangerousMode: false }).catch(() => {
|
|
362
352
|
});
|
|
363
353
|
} else {
|
|
364
|
-
const record = core.sessionManager.getRecordByThread(
|
|
354
|
+
const record = core.sessionManager.getRecordByThread(
|
|
355
|
+
"telegram",
|
|
356
|
+
String(threadId)
|
|
357
|
+
);
|
|
365
358
|
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
366
|
-
await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
|
|
359
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
|
|
360
|
+
parse_mode: "HTML"
|
|
361
|
+
});
|
|
367
362
|
return;
|
|
368
363
|
}
|
|
369
364
|
if (!record.dangerousMode) {
|
|
370
|
-
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", {
|
|
365
|
+
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", {
|
|
366
|
+
parse_mode: "HTML"
|
|
367
|
+
});
|
|
371
368
|
return;
|
|
372
369
|
}
|
|
373
370
|
core.sessionManager.patchRecord(record.sessionId, { dangerousMode: false }).catch(() => {
|
|
374
371
|
});
|
|
375
372
|
}
|
|
376
|
-
await ctx.reply(
|
|
373
|
+
await ctx.reply(
|
|
374
|
+
"\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.",
|
|
375
|
+
{ parse_mode: "HTML" }
|
|
376
|
+
);
|
|
377
377
|
}
|
|
378
378
|
function buildSessionControlKeyboard(sessionId, dangerousMode, voiceMode) {
|
|
379
379
|
return new InlineKeyboard().text(
|
|
@@ -390,7 +390,9 @@ function setupTTSCallbacks(bot, core) {
|
|
|
390
390
|
const session = core.sessionManager.getSession(sessionId);
|
|
391
391
|
if (!session) {
|
|
392
392
|
try {
|
|
393
|
-
await ctx.answerCallbackQuery({
|
|
393
|
+
await ctx.answerCallbackQuery({
|
|
394
|
+
text: "\u26A0\uFE0F Session not found or not active."
|
|
395
|
+
});
|
|
394
396
|
} catch {
|
|
395
397
|
}
|
|
396
398
|
return;
|
|
@@ -404,7 +406,11 @@ function setupTTSCallbacks(bot, core) {
|
|
|
404
406
|
}
|
|
405
407
|
try {
|
|
406
408
|
await ctx.editMessageReplyMarkup({
|
|
407
|
-
reply_markup: buildSessionControlKeyboard(
|
|
409
|
+
reply_markup: buildSessionControlKeyboard(
|
|
410
|
+
sessionId,
|
|
411
|
+
session.dangerousMode,
|
|
412
|
+
newMode === "on"
|
|
413
|
+
)
|
|
408
414
|
});
|
|
409
415
|
} catch {
|
|
410
416
|
}
|
|
@@ -413,42 +419,113 @@ function setupTTSCallbacks(bot, core) {
|
|
|
413
419
|
async function handleTTS(ctx, core) {
|
|
414
420
|
const threadId = ctx.message?.message_thread_id;
|
|
415
421
|
if (!threadId) {
|
|
416
|
-
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
|
|
422
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", {
|
|
423
|
+
parse_mode: "HTML"
|
|
424
|
+
});
|
|
417
425
|
return;
|
|
418
426
|
}
|
|
419
427
|
const session = await core.getOrResumeSession("telegram", String(threadId));
|
|
420
428
|
if (!session) {
|
|
421
|
-
await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
|
|
429
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", {
|
|
430
|
+
parse_mode: "HTML"
|
|
431
|
+
});
|
|
422
432
|
return;
|
|
423
433
|
}
|
|
424
434
|
const args = ctx.message?.text?.split(/\s+/).slice(1) ?? [];
|
|
425
435
|
const arg = args[0]?.toLowerCase();
|
|
426
436
|
if (arg === "on") {
|
|
427
437
|
session.setVoiceMode("on");
|
|
428
|
-
await ctx.reply("\u{1F50A} Text to Speech enabled for this session.", {
|
|
438
|
+
await ctx.reply("\u{1F50A} Text to Speech enabled for this session.", {
|
|
439
|
+
parse_mode: "HTML"
|
|
440
|
+
});
|
|
429
441
|
} else if (arg === "off") {
|
|
430
442
|
session.setVoiceMode("off");
|
|
431
443
|
await ctx.reply("\u{1F507} Text to Speech disabled.", { parse_mode: "HTML" });
|
|
432
444
|
} else {
|
|
433
445
|
session.setVoiceMode("next");
|
|
434
|
-
await ctx.reply("\u{1F50A} Text to Speech enabled for the next message.", {
|
|
446
|
+
await ctx.reply("\u{1F50A} Text to Speech enabled for the next message.", {
|
|
447
|
+
parse_mode: "HTML"
|
|
448
|
+
});
|
|
449
|
+
}
|
|
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
|
+
);
|
|
435
480
|
}
|
|
436
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
|
+
}
|
|
437
498
|
async function handleUpdate(ctx, core) {
|
|
438
499
|
if (!core.requestRestart) {
|
|
439
|
-
await ctx.reply(
|
|
500
|
+
await ctx.reply(
|
|
501
|
+
"\u26A0\uFE0F Update is not available (no restart handler registered).",
|
|
502
|
+
{ parse_mode: "HTML" }
|
|
503
|
+
);
|
|
440
504
|
return;
|
|
441
505
|
}
|
|
442
506
|
const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-AXXV6IV2.js");
|
|
443
507
|
const current = getCurrentVersion();
|
|
444
|
-
const statusMsg = await ctx.reply(
|
|
508
|
+
const statusMsg = await ctx.reply(
|
|
509
|
+
`\u{1F50D} Checking for updates... (current: v${escapeHtml(current)})`,
|
|
510
|
+
{ parse_mode: "HTML" }
|
|
511
|
+
);
|
|
445
512
|
const latest = await getLatestVersion();
|
|
446
513
|
if (!latest) {
|
|
447
|
-
await ctx.api.editMessageText(
|
|
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
|
+
);
|
|
448
520
|
return;
|
|
449
521
|
}
|
|
450
522
|
if (compareVersions(current, latest) >= 0) {
|
|
451
|
-
await ctx.api.editMessageText(
|
|
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
|
+
);
|
|
452
529
|
return;
|
|
453
530
|
}
|
|
454
531
|
await ctx.api.editMessageText(
|
|
@@ -459,7 +536,12 @@ async function handleUpdate(ctx, core) {
|
|
|
459
536
|
);
|
|
460
537
|
const ok = await runUpdate();
|
|
461
538
|
if (!ok) {
|
|
462
|
-
await ctx.api.editMessageText(
|
|
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
|
+
);
|
|
463
545
|
return;
|
|
464
546
|
}
|
|
465
547
|
await ctx.api.editMessageText(
|
|
@@ -473,10 +555,16 @@ async function handleUpdate(ctx, core) {
|
|
|
473
555
|
}
|
|
474
556
|
async function handleRestart(ctx, core) {
|
|
475
557
|
if (!core.requestRestart) {
|
|
476
|
-
await ctx.reply(
|
|
558
|
+
await ctx.reply(
|
|
559
|
+
"\u26A0\uFE0F Restart is not available (no restart handler registered).",
|
|
560
|
+
{ parse_mode: "HTML" }
|
|
561
|
+
);
|
|
477
562
|
return;
|
|
478
563
|
}
|
|
479
|
-
await ctx.reply(
|
|
564
|
+
await ctx.reply(
|
|
565
|
+
"\u{1F504} <b>Restarting OpenACP...</b>\nRebuilding and restarting. Be back shortly.",
|
|
566
|
+
{ parse_mode: "HTML" }
|
|
567
|
+
);
|
|
480
568
|
await new Promise((r) => setTimeout(r, 500));
|
|
481
569
|
await core.requestRestart();
|
|
482
570
|
}
|
|
@@ -1179,26 +1267,13 @@ async function handleArchive(ctx, core) {
|
|
|
1179
1267
|
const threadId = ctx.message?.message_thread_id;
|
|
1180
1268
|
if (!threadId) return;
|
|
1181
1269
|
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
"\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.",
|
|
1185
|
-
{ parse_mode: "HTML" }
|
|
1186
|
-
);
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
if (session.status === "initializing") {
|
|
1190
|
-
await ctx.reply("\u23F3 Please wait for session to be ready.", { parse_mode: "HTML" });
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
if (session.status !== "active") {
|
|
1194
|
-
await ctx.reply(`\u26A0\uFE0F Cannot archive \u2014 session is ${session.status}.`, { parse_mode: "HTML" });
|
|
1195
|
-
return;
|
|
1196
|
-
}
|
|
1270
|
+
const record = !session ? core.sessionManager.getRecordByThread("telegram", String(threadId)) : void 0;
|
|
1271
|
+
const identifier = session?.id ?? record?.sessionId ?? `topic:${threadId}`;
|
|
1197
1272
|
await ctx.reply(
|
|
1198
|
-
"\u26A0\uFE0F <b>Archive this session
|
|
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>",
|
|
1199
1274
|
{
|
|
1200
1275
|
parse_mode: "HTML",
|
|
1201
|
-
reply_markup: new InlineKeyboard3().text("\u{1F5D1} Yes, archive", `ar:yes:${
|
|
1276
|
+
reply_markup: new InlineKeyboard3().text("\u{1F5D1} Yes, archive", `ar:yes:${identifier}`).text("\u274C Cancel", `ar:no:${identifier}`)
|
|
1202
1277
|
}
|
|
1203
1278
|
);
|
|
1204
1279
|
}
|
|
@@ -1209,31 +1284,103 @@ async function handleArchiveConfirm(ctx, core, chatId) {
|
|
|
1209
1284
|
await ctx.answerCallbackQuery();
|
|
1210
1285
|
} catch {
|
|
1211
1286
|
}
|
|
1212
|
-
const [, action,
|
|
1287
|
+
const [, action, ...rest] = data.split(":");
|
|
1288
|
+
const identifier = rest.join(":");
|
|
1213
1289
|
if (action === "no") {
|
|
1214
1290
|
await ctx.editMessageText("Archive cancelled.", { parse_mode: "HTML" });
|
|
1215
1291
|
return;
|
|
1216
1292
|
}
|
|
1217
|
-
await ctx.editMessageText("\u{1F504} Archiving
|
|
1218
|
-
|
|
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);
|
|
1219
1315
|
if (result.ok) {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1316
|
+
core.notificationManager.notifyAll({
|
|
1317
|
+
sessionId: identifier,
|
|
1318
|
+
type: "completed",
|
|
1319
|
+
summary: `Session archived and deleted.`
|
|
1224
1320
|
});
|
|
1225
1321
|
} else {
|
|
1226
1322
|
try {
|
|
1227
1323
|
await ctx.editMessageText(`\u274C Failed to archive: <code>${escapeHtml(result.error)}</code>`, { parse_mode: "HTML" });
|
|
1228
1324
|
} catch {
|
|
1229
1325
|
core.notificationManager.notifyAll({
|
|
1230
|
-
sessionId,
|
|
1326
|
+
sessionId: identifier,
|
|
1231
1327
|
type: "error",
|
|
1232
|
-
summary: `Failed to
|
|
1328
|
+
summary: `Failed to archive session "${identifier}": ${result.error}`
|
|
1233
1329
|
});
|
|
1234
1330
|
}
|
|
1235
1331
|
}
|
|
1236
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
|
+
}
|
|
1237
1384
|
|
|
1238
1385
|
// src/adapters/telegram/commands/agents.ts
|
|
1239
1386
|
import { InlineKeyboard as InlineKeyboard4 } from "grammy";
|
|
@@ -1622,13 +1769,290 @@ ${resultText}`,
|
|
|
1622
1769
|
});
|
|
1623
1770
|
}
|
|
1624
1771
|
|
|
1625
|
-
// src/adapters/telegram/commands/
|
|
1772
|
+
// src/adapters/telegram/commands/resume.ts
|
|
1773
|
+
import * as fs from "fs";
|
|
1774
|
+
import * as path from "path";
|
|
1775
|
+
import * as os from "os";
|
|
1626
1776
|
import { InlineKeyboard as InlineKeyboard6 } from "grammy";
|
|
1627
|
-
var log4 = createChildLogger({ module: "telegram-
|
|
1777
|
+
var log4 = createChildLogger({ module: "telegram-cmd-resume" });
|
|
1778
|
+
var PENDING_TIMEOUT_MS2 = 5 * 60 * 1e3;
|
|
1779
|
+
function botFromCtx2(ctx) {
|
|
1780
|
+
return { api: ctx.api };
|
|
1781
|
+
}
|
|
1782
|
+
var pendingResumes = /* @__PURE__ */ new Map();
|
|
1783
|
+
function cleanupPending2(userId) {
|
|
1784
|
+
const pending = pendingResumes.get(userId);
|
|
1785
|
+
if (pending) {
|
|
1786
|
+
clearTimeout(pending.timer);
|
|
1787
|
+
pendingResumes.delete(userId);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
function parseResumeArgs(matchStr) {
|
|
1791
|
+
const args = matchStr.split(" ").filter(Boolean);
|
|
1792
|
+
if (args.length === 0) return { query: { type: "latest", value: "5" } };
|
|
1793
|
+
const first = args[0];
|
|
1794
|
+
if (first === "pr") return args[1] ? { query: { type: "pr", value: args[1] } } : null;
|
|
1795
|
+
if (first === "branch") return args[1] ? { query: { type: "branch", value: args[1] } } : null;
|
|
1796
|
+
if (first === "commit") return args[1] ? { query: { type: "commit", value: args[1] } } : null;
|
|
1797
|
+
if (CheckpointReader.isCheckpointId(first)) return { query: { type: "checkpoint", value: first } };
|
|
1798
|
+
if (CheckpointReader.isSessionId(first)) return { query: { type: "session", value: first } };
|
|
1799
|
+
if (first.includes("/pull/")) {
|
|
1800
|
+
const prMatch = first.match(/\/pull\/(\d+)/);
|
|
1801
|
+
return prMatch ? { query: { type: "pr", value: prMatch[1] } } : null;
|
|
1802
|
+
}
|
|
1803
|
+
const ghCommitMatch = first.match(/github\.com\/[^/]+\/[^/]+\/commit\/([0-9a-f]+)/);
|
|
1804
|
+
if (ghCommitMatch) return { query: { type: "commit", value: ghCommitMatch[1] } };
|
|
1805
|
+
const ghBranchMatch = first.match(/github\.com\/[^/]+\/[^/]+\/tree\/(.+?)(?:\?|#|$)/);
|
|
1806
|
+
if (ghBranchMatch) return { query: { type: "branch", value: ghBranchMatch[1] } };
|
|
1807
|
+
const ghCompareMatch = first.match(/github\.com\/[^/]+\/[^/]+\/compare\/(?:[^.]+\.{2,3})(.+?)(?:\?|#|$)/);
|
|
1808
|
+
if (ghCompareMatch) return { query: { type: "branch", value: ghCompareMatch[1] } };
|
|
1809
|
+
if (first.match(/github\.com\/[^/]+\/[^/]+\/?$/) && !first.includes("/tree/") && !first.includes("/pull/") && !first.includes("/commit/") && !first.includes("/compare/")) {
|
|
1810
|
+
return { query: { type: "latest", value: "5" } };
|
|
1811
|
+
}
|
|
1812
|
+
const entireCheckpointMatch = first.match(/entire\.io\/gh\/[^/]+\/[^/]+\/checkpoints\/[^/]+\/([0-9a-f]{12})/);
|
|
1813
|
+
if (entireCheckpointMatch) return { query: { type: "checkpoint", value: entireCheckpointMatch[1] } };
|
|
1814
|
+
const entireCommitMatch = first.match(/entire\.io\/gh\/[^/]+\/[^/]+\/commit\/([0-9a-f]+)/);
|
|
1815
|
+
if (entireCommitMatch) return { query: { type: "commit", value: entireCommitMatch[1] } };
|
|
1816
|
+
return { query: { type: "latest", value: "5" } };
|
|
1817
|
+
}
|
|
1818
|
+
function looksLikePath(text) {
|
|
1819
|
+
return text.startsWith("/") || text.startsWith("~") || text.startsWith(".");
|
|
1820
|
+
}
|
|
1821
|
+
function listWorkspaceDirs(baseDir, maxItems = 10) {
|
|
1822
|
+
const resolved = baseDir.replace(/^~/, os.homedir());
|
|
1823
|
+
try {
|
|
1824
|
+
if (!fs.existsSync(resolved)) return [];
|
|
1825
|
+
return fs.readdirSync(resolved, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).sort().slice(0, maxItems);
|
|
1826
|
+
} catch {
|
|
1827
|
+
return [];
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
async function showWorkspacePicker(ctx, core, chatId, userId, query) {
|
|
1831
|
+
const config = core.configManager.get();
|
|
1832
|
+
const baseDir = config.workspace.baseDir;
|
|
1833
|
+
const resolvedBase = baseDir.replace(/^~/, os.homedir());
|
|
1834
|
+
const subdirs = listWorkspaceDirs(baseDir);
|
|
1835
|
+
const keyboard = new InlineKeyboard6();
|
|
1836
|
+
for (const dir of subdirs) {
|
|
1837
|
+
const fullPath = path.join(resolvedBase, dir);
|
|
1838
|
+
keyboard.text(`\u{1F4C1} ${dir}`, `m:resume:ws:${dir}`).row();
|
|
1839
|
+
}
|
|
1840
|
+
keyboard.text(`\u{1F4C1} Use ${baseDir}`, "m:resume:ws:default").row();
|
|
1841
|
+
keyboard.text("\u270F\uFE0F Enter project path", "m:resume:ws:custom");
|
|
1842
|
+
const queryLabel = query.type === "latest" ? "latest sessions" : `${query.type}: ${query.value}`;
|
|
1843
|
+
const text = `\u{1F4C1} <b>Select project directory for resume</b>
|
|
1844
|
+
|
|
1845
|
+
Query: <code>${escapeHtml(queryLabel)}</code>
|
|
1846
|
+
|
|
1847
|
+
Choose the repo that has Entire checkpoints enabled:`;
|
|
1848
|
+
const msg = await ctx.reply(text, { parse_mode: "HTML", reply_markup: keyboard });
|
|
1849
|
+
cleanupPending2(userId);
|
|
1850
|
+
pendingResumes.set(userId, {
|
|
1851
|
+
query,
|
|
1852
|
+
step: "workspace",
|
|
1853
|
+
messageId: msg.message_id,
|
|
1854
|
+
threadId: ctx.message?.message_thread_id,
|
|
1855
|
+
timer: setTimeout(() => pendingResumes.delete(userId), PENDING_TIMEOUT_MS2)
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
async function executeResume(ctx, core, chatId, query, repoPath) {
|
|
1859
|
+
const provider = await core.contextManager.getProvider(repoPath);
|
|
1860
|
+
if (!provider) {
|
|
1861
|
+
await ctx.reply(
|
|
1862
|
+
`\u26A0\uFE0F <b>Entire not enabled in <code>${escapeHtml(repoPath)}</code></b>
|
|
1863
|
+
|
|
1864
|
+
To enable conversation history tracking:
|
|
1865
|
+
<code>cd ${escapeHtml(repoPath)} && npx entire enable</code>
|
|
1866
|
+
|
|
1867
|
+
Learn more: https://docs.entire.io/getting-started`,
|
|
1868
|
+
{ parse_mode: "HTML" }
|
|
1869
|
+
);
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
const fullQuery = { ...query, repoPath };
|
|
1873
|
+
await ctx.reply(`\u{1F50D} Scanning ${query.type === "latest" ? "latest sessions" : `${query.type}: ${escapeHtml(query.value)}`}...`, { parse_mode: "HTML" });
|
|
1874
|
+
const listResult = await core.contextManager.listSessions(fullQuery);
|
|
1875
|
+
if (!listResult || listResult.sessions.length === 0) {
|
|
1876
|
+
await ctx.reply(
|
|
1877
|
+
`\u{1F50D} <b>No sessions found</b>
|
|
1878
|
+
|
|
1879
|
+
Query: <code>${escapeHtml(query.type)}: ${escapeHtml(query.value)}</code>
|
|
1880
|
+
Repo: <code>${escapeHtml(repoPath)}</code>`,
|
|
1881
|
+
{ parse_mode: "HTML" }
|
|
1882
|
+
);
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
const config = core.configManager.get();
|
|
1886
|
+
const agentName = config.defaultAgent;
|
|
1887
|
+
let threadId;
|
|
1888
|
+
try {
|
|
1889
|
+
const queryLabel = query.type === "latest" ? "latest" : `${query.type}: ${query.value.slice(0, 20)}`;
|
|
1890
|
+
const topicName = `\u{1F4DC} Resume \u2014 ${queryLabel}`;
|
|
1891
|
+
threadId = await createSessionTopic(botFromCtx2(ctx), chatId, topicName);
|
|
1892
|
+
await ctx.api.sendMessage(chatId, `\u23F3 Loading context and starting session...`, {
|
|
1893
|
+
message_thread_id: threadId,
|
|
1894
|
+
parse_mode: "HTML"
|
|
1895
|
+
});
|
|
1896
|
+
const { session, contextResult } = await core.createSessionWithContext({
|
|
1897
|
+
channelId: "telegram",
|
|
1898
|
+
agentName,
|
|
1899
|
+
workingDirectory: repoPath,
|
|
1900
|
+
contextQuery: fullQuery,
|
|
1901
|
+
contextOptions: { maxTokens: DEFAULT_MAX_TOKENS }
|
|
1902
|
+
});
|
|
1903
|
+
session.threadId = String(threadId);
|
|
1904
|
+
await core.sessionManager.patchRecord(session.id, { platform: { topicId: threadId } });
|
|
1905
|
+
const sessionCount = contextResult?.sessionCount ?? listResult.sessions.length;
|
|
1906
|
+
const mode = contextResult?.mode ?? "full";
|
|
1907
|
+
const tokens = contextResult?.tokenEstimate ?? listResult.estimatedTokens;
|
|
1908
|
+
const topicLink = buildDeepLink(chatId, threadId);
|
|
1909
|
+
const replyTarget = ctx.message?.message_thread_id;
|
|
1910
|
+
if (replyTarget !== threadId) {
|
|
1911
|
+
await ctx.reply(
|
|
1912
|
+
`\u2705 Session resumed \u2192 <a href="${topicLink}">Open topic</a>`,
|
|
1913
|
+
{ parse_mode: "HTML" }
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
await ctx.api.sendMessage(
|
|
1917
|
+
chatId,
|
|
1918
|
+
`\u2705 <b>Session resumed with context</b>
|
|
1919
|
+
<b>Agent:</b> ${escapeHtml(session.agentName)}
|
|
1920
|
+
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>
|
|
1921
|
+
<b>Sessions loaded:</b> ${sessionCount}
|
|
1922
|
+
<b>Mode:</b> ${escapeHtml(mode)}
|
|
1923
|
+
<b>~Tokens:</b> ${tokens.toLocaleString()}
|
|
1924
|
+
|
|
1925
|
+
Context is ready \u2014 chat here to continue working with the agent.`,
|
|
1926
|
+
{
|
|
1927
|
+
message_thread_id: threadId,
|
|
1928
|
+
parse_mode: "HTML",
|
|
1929
|
+
reply_markup: buildSessionControlKeyboard(session.id, false, false)
|
|
1930
|
+
}
|
|
1931
|
+
);
|
|
1932
|
+
session.warmup().catch((err) => log4.error({ err }, "Warm-up error"));
|
|
1933
|
+
} catch (err) {
|
|
1934
|
+
log4.error({ err }, "Resume session creation failed");
|
|
1935
|
+
if (threadId) {
|
|
1936
|
+
try {
|
|
1937
|
+
await ctx.api.deleteForumTopic(chatId, threadId);
|
|
1938
|
+
} catch {
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
const message = err instanceof Error ? err.message : typeof err === "object" ? JSON.stringify(err) : String(err);
|
|
1942
|
+
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
async function handleResume(ctx, core, chatId, assistant) {
|
|
1946
|
+
const rawMatch = ctx.match;
|
|
1947
|
+
const matchStr = typeof rawMatch === "string" ? rawMatch : "";
|
|
1948
|
+
const parsed = parseResumeArgs(matchStr);
|
|
1949
|
+
if (!parsed) {
|
|
1950
|
+
await ctx.reply(
|
|
1951
|
+
`\u274C <b>Invalid arguments.</b>
|
|
1952
|
+
|
|
1953
|
+
Usage examples:
|
|
1954
|
+
\u2022 <code>/resume</code> \u2014 latest 5 sessions
|
|
1955
|
+
\u2022 <code>/resume pr 19</code>
|
|
1956
|
+
\u2022 <code>/resume branch main</code>
|
|
1957
|
+
\u2022 <code>/resume commit e0dd2fa4</code>
|
|
1958
|
+
\u2022 <code>/resume f634acf05138</code> \u2014 checkpoint ID
|
|
1959
|
+
\u2022 <code>/resume https://entire.io/gh/.../checkpoints/.../2e884e2c402a</code>
|
|
1960
|
+
\u2022 <code>/resume https://entire.io/gh/.../commit/e0dd2fa4...</code>`,
|
|
1961
|
+
{ parse_mode: "HTML" }
|
|
1962
|
+
);
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
const { query } = parsed;
|
|
1966
|
+
const userId = ctx.from?.id;
|
|
1967
|
+
if (!userId) return;
|
|
1968
|
+
await showWorkspacePicker(ctx, core, chatId, userId, query);
|
|
1969
|
+
}
|
|
1970
|
+
async function handlePendingResumeInput(ctx, core, chatId, assistantTopicId) {
|
|
1971
|
+
const userId = ctx.from?.id;
|
|
1972
|
+
if (!userId) return false;
|
|
1973
|
+
const pending = pendingResumes.get(userId);
|
|
1974
|
+
if (!pending || !ctx.message?.text) return false;
|
|
1975
|
+
if (pending.step !== "workspace_input" && pending.step !== "workspace") return false;
|
|
1976
|
+
const threadId = ctx.message.message_thread_id;
|
|
1977
|
+
if (threadId && threadId !== assistantTopicId) return false;
|
|
1978
|
+
if (pending.step === "workspace" && !looksLikePath(ctx.message.text.trim())) return false;
|
|
1979
|
+
let workspace = ctx.message.text.trim();
|
|
1980
|
+
if (!workspace) {
|
|
1981
|
+
await ctx.reply("\u26A0\uFE0F Please enter a valid directory path.", { parse_mode: "HTML" });
|
|
1982
|
+
return true;
|
|
1983
|
+
}
|
|
1984
|
+
if (!workspace.startsWith("/") && !workspace.startsWith("~")) {
|
|
1985
|
+
const baseDir = core.configManager.get().workspace.baseDir;
|
|
1986
|
+
workspace = `${baseDir.replace(/\/$/, "")}/${workspace}`;
|
|
1987
|
+
}
|
|
1988
|
+
const resolved = core.configManager.resolveWorkspace(workspace);
|
|
1989
|
+
cleanupPending2(userId);
|
|
1990
|
+
await executeResume(ctx, core, chatId, pending.query, resolved);
|
|
1991
|
+
return true;
|
|
1992
|
+
}
|
|
1993
|
+
function setupResumeCallbacks(bot, core, chatId) {
|
|
1994
|
+
bot.callbackQuery(/^m:resume:/, async (ctx) => {
|
|
1995
|
+
const data = ctx.callbackQuery.data;
|
|
1996
|
+
const userId = ctx.from?.id;
|
|
1997
|
+
if (!userId) return;
|
|
1998
|
+
try {
|
|
1999
|
+
await ctx.answerCallbackQuery();
|
|
2000
|
+
} catch {
|
|
2001
|
+
}
|
|
2002
|
+
const pending = pendingResumes.get(userId);
|
|
2003
|
+
if (!pending) return;
|
|
2004
|
+
if (data === "m:resume:ws:default") {
|
|
2005
|
+
const baseDir = core.configManager.get().workspace.baseDir;
|
|
2006
|
+
const resolved = core.configManager.resolveWorkspace(baseDir);
|
|
2007
|
+
cleanupPending2(userId);
|
|
2008
|
+
try {
|
|
2009
|
+
await ctx.api.editMessageText(chatId, pending.messageId, `\u23F3 Using <code>${escapeHtml(resolved)}</code>...`, { parse_mode: "HTML" });
|
|
2010
|
+
} catch {
|
|
2011
|
+
}
|
|
2012
|
+
await executeResume(ctx, core, chatId, pending.query, resolved);
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
if (data === "m:resume:ws:custom") {
|
|
2016
|
+
try {
|
|
2017
|
+
await ctx.api.editMessageText(
|
|
2018
|
+
chatId,
|
|
2019
|
+
pending.messageId,
|
|
2020
|
+
`\u270F\uFE0F <b>Enter project path:</b>
|
|
2021
|
+
|
|
2022
|
+
Full path like <code>~/code/my-project</code>
|
|
2023
|
+
Or just the folder name (will use workspace baseDir)`,
|
|
2024
|
+
{ parse_mode: "HTML" }
|
|
2025
|
+
);
|
|
2026
|
+
} catch {
|
|
2027
|
+
await ctx.reply(`\u270F\uFE0F <b>Enter project path:</b>`, { parse_mode: "HTML" });
|
|
2028
|
+
}
|
|
2029
|
+
clearTimeout(pending.timer);
|
|
2030
|
+
pending.step = "workspace_input";
|
|
2031
|
+
pending.timer = setTimeout(() => pendingResumes.delete(userId), PENDING_TIMEOUT_MS2);
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
if (data.startsWith("m:resume:ws:")) {
|
|
2035
|
+
const dirName = data.replace("m:resume:ws:", "");
|
|
2036
|
+
const baseDir = core.configManager.get().workspace.baseDir;
|
|
2037
|
+
const resolved = core.configManager.resolveWorkspace(path.join(baseDir.replace(/^~/, os.homedir()), dirName));
|
|
2038
|
+
cleanupPending2(userId);
|
|
2039
|
+
try {
|
|
2040
|
+
await ctx.api.editMessageText(chatId, pending.messageId, `\u23F3 Using <code>${escapeHtml(resolved)}</code>...`, { parse_mode: "HTML" });
|
|
2041
|
+
} catch {
|
|
2042
|
+
}
|
|
2043
|
+
await executeResume(ctx, core, chatId, pending.query, resolved);
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// src/adapters/telegram/commands/settings.ts
|
|
2050
|
+
import { InlineKeyboard as InlineKeyboard7 } from "grammy";
|
|
2051
|
+
var log5 = createChildLogger({ module: "telegram-settings" });
|
|
1628
2052
|
function buildSettingsKeyboard(core) {
|
|
1629
2053
|
const config = core.configManager.get();
|
|
1630
2054
|
const fields = getSafeFields();
|
|
1631
|
-
const kb = new
|
|
2055
|
+
const kb = new InlineKeyboard7();
|
|
1632
2056
|
for (const field of fields) {
|
|
1633
2057
|
const value = getConfigValue(config, field.path);
|
|
1634
2058
|
const label = formatFieldLabel(field, value);
|
|
@@ -1687,7 +2111,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
|
|
|
1687
2111
|
} catch {
|
|
1688
2112
|
}
|
|
1689
2113
|
} catch (err) {
|
|
1690
|
-
|
|
2114
|
+
log5.error({ err, fieldPath }, "Failed to toggle config");
|
|
1691
2115
|
try {
|
|
1692
2116
|
await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
|
|
1693
2117
|
} catch {
|
|
@@ -1701,7 +2125,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
|
|
|
1701
2125
|
if (!fieldDef) return;
|
|
1702
2126
|
const options = resolveOptions(fieldDef, config) ?? [];
|
|
1703
2127
|
const currentValue = getConfigValue(config, fieldPath);
|
|
1704
|
-
const kb = new
|
|
2128
|
+
const kb = new InlineKeyboard7();
|
|
1705
2129
|
for (const opt of options) {
|
|
1706
2130
|
const marker = opt === String(currentValue) ? " \u2713" : "";
|
|
1707
2131
|
kb.text(`${opt}${marker}`, `s:pick:${fieldPath}:${opt}`).row();
|
|
@@ -1761,7 +2185,7 @@ Tap to change:`, {
|
|
|
1761
2185
|
} catch {
|
|
1762
2186
|
}
|
|
1763
2187
|
} catch (err) {
|
|
1764
|
-
|
|
2188
|
+
log5.error({ err, fieldPath }, "Failed to set config");
|
|
1765
2189
|
try {
|
|
1766
2190
|
await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
|
|
1767
2191
|
} catch {
|
|
@@ -1832,8 +2256,8 @@ function buildNestedUpdate(dotPath, value) {
|
|
|
1832
2256
|
}
|
|
1833
2257
|
|
|
1834
2258
|
// src/adapters/telegram/commands/doctor.ts
|
|
1835
|
-
import { InlineKeyboard as
|
|
1836
|
-
var
|
|
2259
|
+
import { InlineKeyboard as InlineKeyboard8 } from "grammy";
|
|
2260
|
+
var log6 = createChildLogger({ module: "telegram-cmd-doctor" });
|
|
1837
2261
|
var pendingFixesStore = /* @__PURE__ */ new Map();
|
|
1838
2262
|
function renderReport(report) {
|
|
1839
2263
|
const icons = { pass: "\u2705", warn: "\u26A0\uFE0F", fail: "\u274C" };
|
|
@@ -1850,7 +2274,7 @@ function renderReport(report) {
|
|
|
1850
2274
|
lines.push(`<b>Result:</b> ${passed} passed, ${warnings} warnings, ${failed} failed${fixedStr}`);
|
|
1851
2275
|
let keyboard;
|
|
1852
2276
|
if (report.pendingFixes.length > 0) {
|
|
1853
|
-
keyboard = new
|
|
2277
|
+
keyboard = new InlineKeyboard8();
|
|
1854
2278
|
for (let i = 0; i < report.pendingFixes.length; i++) {
|
|
1855
2279
|
const label = `\u{1F527} Fix: ${report.pendingFixes[i].message.slice(0, 30)}`;
|
|
1856
2280
|
keyboard.text(label, `m:doctor:fix:${i}`).row();
|
|
@@ -1876,7 +2300,7 @@ async function handleDoctor(ctx) {
|
|
|
1876
2300
|
reply_markup: keyboard
|
|
1877
2301
|
});
|
|
1878
2302
|
} catch (err) {
|
|
1879
|
-
|
|
2303
|
+
log6.error({ err }, "Doctor command failed");
|
|
1880
2304
|
await ctx.api.editMessageText(
|
|
1881
2305
|
ctx.chat.id,
|
|
1882
2306
|
statusMsg.message_id,
|
|
@@ -1925,7 +2349,7 @@ function setupDoctorCallbacks(bot) {
|
|
|
1925
2349
|
}
|
|
1926
2350
|
}
|
|
1927
2351
|
} catch (err) {
|
|
1928
|
-
|
|
2352
|
+
log6.error({ err, index }, "Doctor fix callback failed");
|
|
1929
2353
|
}
|
|
1930
2354
|
});
|
|
1931
2355
|
bot.callbackQuery("m:doctor", async (ctx) => {
|
|
@@ -1938,8 +2362,8 @@ function setupDoctorCallbacks(bot) {
|
|
|
1938
2362
|
}
|
|
1939
2363
|
|
|
1940
2364
|
// src/adapters/telegram/commands/tunnel.ts
|
|
1941
|
-
import { InlineKeyboard as
|
|
1942
|
-
var
|
|
2365
|
+
import { InlineKeyboard as InlineKeyboard9 } from "grammy";
|
|
2366
|
+
var log7 = createChildLogger({ module: "telegram-cmd-tunnel" });
|
|
1943
2367
|
async function handleTunnel(ctx, core) {
|
|
1944
2368
|
if (!core.tunnelService) {
|
|
1945
2369
|
await ctx.reply("\u274C Tunnel service is not enabled.", { parse_mode: "HTML" });
|
|
@@ -2026,7 +2450,7 @@ async function handleTunnels(ctx, core) {
|
|
|
2026
2450
|
\u2192 <a href="${escapeHtml(e.publicUrl)}">${escapeHtml(e.publicUrl)}</a>` : "";
|
|
2027
2451
|
return `${status} Port <b>${e.port}</b>${label}${url}`;
|
|
2028
2452
|
});
|
|
2029
|
-
const keyboard = new
|
|
2453
|
+
const keyboard = new InlineKeyboard9();
|
|
2030
2454
|
for (const e of entries) {
|
|
2031
2455
|
keyboard.text(`\u{1F50C} Stop ${e.port}${e.label ? ` (${e.label})` : ""}`, `tn:stop:${e.port}`).row();
|
|
2032
2456
|
}
|
|
@@ -2066,7 +2490,7 @@ function setupTunnelCallbacks(bot, core) {
|
|
|
2066
2490
|
if (remaining.length === 0) {
|
|
2067
2491
|
await ctx.editMessageText("\u{1F50C} All tunnels stopped.", { parse_mode: "HTML" });
|
|
2068
2492
|
} else {
|
|
2069
|
-
const kb = new
|
|
2493
|
+
const kb = new InlineKeyboard9();
|
|
2070
2494
|
for (const e of remaining) {
|
|
2071
2495
|
kb.text(`\u{1F50C} Stop ${e.port}${e.label ? ` (${e.label})` : ""}`, `tn:stop:${e.port}`).row();
|
|
2072
2496
|
}
|
|
@@ -2115,10 +2539,14 @@ function setupCommands(bot, core, chatId, assistant) {
|
|
|
2115
2539
|
bot.command("tunnel", (ctx) => handleTunnel(ctx, core));
|
|
2116
2540
|
bot.command("tunnels", (ctx) => handleTunnels(ctx, core));
|
|
2117
2541
|
bot.command("archive", (ctx) => handleArchive(ctx, core));
|
|
2542
|
+
bot.command("summary", (ctx) => handleSummary(ctx, core));
|
|
2118
2543
|
bot.command("text_to_speech", (ctx) => handleTTS(ctx, core));
|
|
2544
|
+
bot.command("verbosity", (ctx) => handleVerbosity(ctx, core));
|
|
2545
|
+
bot.command("resume", (ctx) => handleResume(ctx, core, chatId, assistant));
|
|
2119
2546
|
}
|
|
2120
2547
|
function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSession) {
|
|
2121
2548
|
setupNewSessionCallbacks(bot, core, chatId);
|
|
2549
|
+
setupResumeCallbacks(bot, core, chatId);
|
|
2122
2550
|
setupSessionCallbacks(bot, core, chatId, systemTopicIds);
|
|
2123
2551
|
setupSettingsCallbacks(bot, core, getAssistantSession ?? (() => void 0));
|
|
2124
2552
|
setupDoctorCallbacks(bot);
|
|
@@ -2127,9 +2555,16 @@ function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSessio
|
|
|
2127
2555
|
bot.callbackQuery(/^na:/, async (ctx) => {
|
|
2128
2556
|
const agentKey = ctx.callbackQuery.data.replace("na:", "");
|
|
2129
2557
|
await ctx.answerCallbackQuery();
|
|
2130
|
-
await createSessionDirect(
|
|
2558
|
+
await createSessionDirect(
|
|
2559
|
+
ctx,
|
|
2560
|
+
core,
|
|
2561
|
+
chatId,
|
|
2562
|
+
agentKey,
|
|
2563
|
+
core.configManager.get().workspace.baseDir
|
|
2564
|
+
);
|
|
2131
2565
|
});
|
|
2132
2566
|
bot.callbackQuery(/^ar:/, (ctx) => handleArchiveConfirm(ctx, core, chatId));
|
|
2567
|
+
bot.callbackQuery(/^sm:/, (ctx) => handleSummaryCallback(ctx, core, chatId));
|
|
2133
2568
|
bot.callbackQuery(/^m:/, async (ctx) => {
|
|
2134
2569
|
const data = ctx.callbackQuery.data;
|
|
2135
2570
|
try {
|
|
@@ -2177,8 +2612,14 @@ var STATIC_COMMANDS = [
|
|
|
2177
2612
|
{ command: "install", description: "Install a new agent" },
|
|
2178
2613
|
{ command: "help", description: "Help" },
|
|
2179
2614
|
{ command: "menu", description: "Show menu" },
|
|
2180
|
-
{
|
|
2181
|
-
|
|
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
|
+
},
|
|
2182
2623
|
{ command: "integrate", description: "Manage agent integrations" },
|
|
2183
2624
|
{ command: "handoff", description: "Continue this session in your terminal" },
|
|
2184
2625
|
{ command: "clear", description: "Clear assistant history" },
|
|
@@ -2189,13 +2630,16 @@ var STATIC_COMMANDS = [
|
|
|
2189
2630
|
{ command: "tunnel", description: "Create/stop tunnel for a local port" },
|
|
2190
2631
|
{ command: "tunnels", description: "List active tunnels" },
|
|
2191
2632
|
{ command: "archive", description: "Archive session topic (recreate with clean history)" },
|
|
2192
|
-
{ command: "
|
|
2633
|
+
{ command: "summary", description: "Get AI summary of current session" },
|
|
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)" },
|
|
2636
|
+
{ command: "resume", description: "Resume with conversation history from Entire checkpoints" }
|
|
2193
2637
|
];
|
|
2194
2638
|
|
|
2195
2639
|
// src/adapters/telegram/permissions.ts
|
|
2196
|
-
import { InlineKeyboard as
|
|
2640
|
+
import { InlineKeyboard as InlineKeyboard10 } from "grammy";
|
|
2197
2641
|
import { nanoid } from "nanoid";
|
|
2198
|
-
var
|
|
2642
|
+
var log8 = createChildLogger({ module: "telegram-permissions" });
|
|
2199
2643
|
var PermissionHandler = class {
|
|
2200
2644
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
2201
2645
|
this.bot = bot;
|
|
@@ -2212,7 +2656,7 @@ var PermissionHandler = class {
|
|
|
2212
2656
|
requestId: request.id,
|
|
2213
2657
|
options: request.options.map((o) => ({ id: o.id, isAllow: o.isAllow }))
|
|
2214
2658
|
});
|
|
2215
|
-
const keyboard = new
|
|
2659
|
+
const keyboard = new InlineKeyboard10();
|
|
2216
2660
|
for (const option of request.options) {
|
|
2217
2661
|
const emoji = option.isAllow ? "\u2705" : "\u274C";
|
|
2218
2662
|
keyboard.text(`${emoji} ${option.label}`, `p:${callbackKey}:${option.id}`);
|
|
@@ -2255,7 +2699,7 @@ ${escapeHtml(request.description)}`,
|
|
|
2255
2699
|
}
|
|
2256
2700
|
const session = this.getSession(pending.sessionId);
|
|
2257
2701
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
2258
|
-
|
|
2702
|
+
log8.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
2259
2703
|
if (session?.permissionGate.requestId === pending.requestId) {
|
|
2260
2704
|
session.permissionGate.resolve(optionId);
|
|
2261
2705
|
}
|
|
@@ -2273,10 +2717,10 @@ ${escapeHtml(request.description)}`,
|
|
|
2273
2717
|
};
|
|
2274
2718
|
|
|
2275
2719
|
// src/adapters/telegram/assistant.ts
|
|
2276
|
-
var
|
|
2720
|
+
var log9 = createChildLogger({ module: "telegram-assistant" });
|
|
2277
2721
|
async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
2278
2722
|
const config = core.configManager.get();
|
|
2279
|
-
|
|
2723
|
+
log9.info({ agent: config.defaultAgent }, "Creating assistant session...");
|
|
2280
2724
|
const session = await core.createSession({
|
|
2281
2725
|
channelId: "telegram",
|
|
2282
2726
|
agentName: config.defaultAgent,
|
|
@@ -2285,7 +2729,7 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
2285
2729
|
// Prevent auto-naming from triggering after system prompt
|
|
2286
2730
|
});
|
|
2287
2731
|
session.threadId = String(assistantTopicId);
|
|
2288
|
-
|
|
2732
|
+
log9.info({ sessionId: session.id }, "Assistant agent spawned");
|
|
2289
2733
|
const allRecords = core.sessionManager.listRecords();
|
|
2290
2734
|
const activeCount = allRecords.filter((r) => r.status === "active" || r.status === "initializing").length;
|
|
2291
2735
|
const statusCounts = /* @__PURE__ */ new Map();
|
|
@@ -2306,9 +2750,9 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
2306
2750
|
};
|
|
2307
2751
|
const systemPrompt = buildAssistantSystemPrompt(ctx);
|
|
2308
2752
|
const ready = session.enqueuePrompt(systemPrompt).then(() => {
|
|
2309
|
-
|
|
2753
|
+
log9.info({ sessionId: session.id }, "Assistant system prompt completed");
|
|
2310
2754
|
}).catch((err) => {
|
|
2311
|
-
|
|
2755
|
+
log9.warn({ err }, "Assistant system prompt failed");
|
|
2312
2756
|
});
|
|
2313
2757
|
return { session, ready };
|
|
2314
2758
|
}
|
|
@@ -2474,7 +2918,7 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
2474
2918
|
}
|
|
2475
2919
|
|
|
2476
2920
|
// src/adapters/telegram/activity.ts
|
|
2477
|
-
var
|
|
2921
|
+
var log10 = createChildLogger({ module: "telegram:activity" });
|
|
2478
2922
|
var THINKING_REFRESH_MS = 15e3;
|
|
2479
2923
|
var THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
2480
2924
|
var ThinkingIndicator = class {
|
|
@@ -2506,7 +2950,7 @@ var ThinkingIndicator = class {
|
|
|
2506
2950
|
this.startRefreshTimer();
|
|
2507
2951
|
}
|
|
2508
2952
|
} catch (err) {
|
|
2509
|
-
|
|
2953
|
+
log10.warn({ err }, "ThinkingIndicator.show() failed");
|
|
2510
2954
|
} finally {
|
|
2511
2955
|
this.sending = false;
|
|
2512
2956
|
}
|
|
@@ -2579,7 +3023,7 @@ var UsageMessage = class {
|
|
|
2579
3023
|
if (result) this.msgId = result.message_id;
|
|
2580
3024
|
}
|
|
2581
3025
|
} catch (err) {
|
|
2582
|
-
|
|
3026
|
+
log10.warn({ err }, "UsageMessage.send() failed");
|
|
2583
3027
|
}
|
|
2584
3028
|
}
|
|
2585
3029
|
getMsgId() {
|
|
@@ -2592,7 +3036,7 @@ var UsageMessage = class {
|
|
|
2592
3036
|
try {
|
|
2593
3037
|
await this.sendQueue.enqueue(() => this.api.deleteMessage(this.chatId, id));
|
|
2594
3038
|
} catch (err) {
|
|
2595
|
-
|
|
3039
|
+
log10.warn({ err }, "UsageMessage.delete() failed");
|
|
2596
3040
|
}
|
|
2597
3041
|
}
|
|
2598
3042
|
};
|
|
@@ -2678,7 +3122,7 @@ var PlanCard = class {
|
|
|
2678
3122
|
if (result) this.msgId = result.message_id;
|
|
2679
3123
|
}
|
|
2680
3124
|
} catch (err) {
|
|
2681
|
-
|
|
3125
|
+
log10.warn({ err }, "PlanCard flush failed");
|
|
2682
3126
|
}
|
|
2683
3127
|
}
|
|
2684
3128
|
};
|
|
@@ -2741,7 +3185,7 @@ var ActivityTracker = class {
|
|
|
2741
3185
|
})
|
|
2742
3186
|
);
|
|
2743
3187
|
} catch (err) {
|
|
2744
|
-
|
|
3188
|
+
log10.warn({ err }, "ActivityTracker.onComplete() Done send failed");
|
|
2745
3189
|
}
|
|
2746
3190
|
}
|
|
2747
3191
|
}
|
|
@@ -2824,7 +3268,7 @@ var TelegramSendQueue = class {
|
|
|
2824
3268
|
|
|
2825
3269
|
// src/adapters/telegram/action-detect.ts
|
|
2826
3270
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2827
|
-
import { InlineKeyboard as
|
|
3271
|
+
import { InlineKeyboard as InlineKeyboard11 } from "grammy";
|
|
2828
3272
|
var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
|
|
2829
3273
|
var CMD_CANCEL_RE = /\/cancel\b/;
|
|
2830
3274
|
var KW_NEW_RE = /(?:create|new)\s+session/i;
|
|
@@ -2871,7 +3315,7 @@ function removeAction(id) {
|
|
|
2871
3315
|
actionMap.delete(id);
|
|
2872
3316
|
}
|
|
2873
3317
|
function buildActionKeyboard(actionId, action) {
|
|
2874
|
-
const keyboard = new
|
|
3318
|
+
const keyboard = new InlineKeyboard11();
|
|
2875
3319
|
if (action.action === "new_session") {
|
|
2876
3320
|
keyboard.text("\u2705 Create session", `a:${actionId}`);
|
|
2877
3321
|
keyboard.text("\u274C Cancel", `a:dismiss:${actionId}`);
|
|
@@ -2980,7 +3424,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2980
3424
|
}
|
|
2981
3425
|
|
|
2982
3426
|
// src/adapters/telegram/tool-call-tracker.ts
|
|
2983
|
-
var
|
|
3427
|
+
var log11 = createChildLogger({ module: "tool-call-tracker" });
|
|
2984
3428
|
var ToolCallTracker = class {
|
|
2985
3429
|
constructor(bot, chatId, sendQueue) {
|
|
2986
3430
|
this.bot = bot;
|
|
@@ -2988,7 +3432,7 @@ var ToolCallTracker = class {
|
|
|
2988
3432
|
this.sendQueue = sendQueue;
|
|
2989
3433
|
}
|
|
2990
3434
|
sessions = /* @__PURE__ */ new Map();
|
|
2991
|
-
async trackNewCall(sessionId, threadId, meta) {
|
|
3435
|
+
async trackNewCall(sessionId, threadId, meta, verbosity = "medium") {
|
|
2992
3436
|
if (!this.sessions.has(sessionId)) {
|
|
2993
3437
|
this.sessions.set(sessionId, /* @__PURE__ */ new Map());
|
|
2994
3438
|
}
|
|
@@ -3000,31 +3444,34 @@ var ToolCallTracker = class {
|
|
|
3000
3444
|
msgId: 0,
|
|
3001
3445
|
name: meta.name,
|
|
3002
3446
|
kind: meta.kind,
|
|
3447
|
+
rawInput: meta.rawInput,
|
|
3003
3448
|
viewerLinks: meta.viewerLinks,
|
|
3004
3449
|
viewerFilePath: meta.viewerFilePath,
|
|
3005
3450
|
ready
|
|
3006
3451
|
});
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
this.chatId,
|
|
3010
|
-
formatToolCall(meta),
|
|
3011
|
-
{
|
|
3452
|
+
try {
|
|
3453
|
+
const msg = await this.sendQueue.enqueue(
|
|
3454
|
+
() => this.bot.api.sendMessage(this.chatId, formatToolCall(meta, verbosity), {
|
|
3012
3455
|
message_thread_id: threadId,
|
|
3013
3456
|
parse_mode: "HTML",
|
|
3014
3457
|
disable_notification: true
|
|
3015
|
-
}
|
|
3016
|
-
)
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3458
|
+
})
|
|
3459
|
+
);
|
|
3460
|
+
const toolEntry = this.sessions.get(sessionId).get(meta.id);
|
|
3461
|
+
toolEntry.msgId = msg.message_id;
|
|
3462
|
+
} finally {
|
|
3463
|
+
resolveReady();
|
|
3464
|
+
}
|
|
3021
3465
|
}
|
|
3022
|
-
async updateCall(sessionId, meta) {
|
|
3466
|
+
async updateCall(sessionId, meta, verbosity = "medium") {
|
|
3023
3467
|
const toolState = this.sessions.get(sessionId)?.get(meta.id);
|
|
3024
3468
|
if (!toolState) return;
|
|
3025
3469
|
if (meta.viewerLinks) {
|
|
3026
3470
|
toolState.viewerLinks = meta.viewerLinks;
|
|
3027
|
-
|
|
3471
|
+
log11.debug(
|
|
3472
|
+
{ toolId: meta.id, viewerLinks: meta.viewerLinks },
|
|
3473
|
+
"Accumulated viewerLinks"
|
|
3474
|
+
);
|
|
3028
3475
|
}
|
|
3029
3476
|
if (meta.viewerFilePath) toolState.viewerFilePath = meta.viewerFilePath;
|
|
3030
3477
|
if (meta.name) toolState.name = meta.name;
|
|
@@ -3032,7 +3479,7 @@ var ToolCallTracker = class {
|
|
|
3032
3479
|
const isTerminal = meta.status === "completed" || meta.status === "failed";
|
|
3033
3480
|
if (!isTerminal) return;
|
|
3034
3481
|
await toolState.ready;
|
|
3035
|
-
|
|
3482
|
+
log11.debug(
|
|
3036
3483
|
{
|
|
3037
3484
|
toolId: meta.id,
|
|
3038
3485
|
status: meta.status,
|
|
@@ -3047,10 +3494,11 @@ var ToolCallTracker = class {
|
|
|
3047
3494
|
...meta,
|
|
3048
3495
|
name: toolState.name,
|
|
3049
3496
|
kind: toolState.kind,
|
|
3497
|
+
rawInput: toolState.rawInput,
|
|
3050
3498
|
viewerLinks: toolState.viewerLinks,
|
|
3051
3499
|
viewerFilePath: toolState.viewerFilePath
|
|
3052
3500
|
};
|
|
3053
|
-
const formattedText = formatToolUpdate(merged);
|
|
3501
|
+
const formattedText = formatToolUpdate(merged, verbosity);
|
|
3054
3502
|
try {
|
|
3055
3503
|
await this.sendQueue.enqueue(
|
|
3056
3504
|
() => this.bot.api.editMessageText(
|
|
@@ -3061,7 +3509,7 @@ var ToolCallTracker = class {
|
|
|
3061
3509
|
)
|
|
3062
3510
|
);
|
|
3063
3511
|
} catch (err) {
|
|
3064
|
-
|
|
3512
|
+
log11.warn(
|
|
3065
3513
|
{
|
|
3066
3514
|
err,
|
|
3067
3515
|
msgId: toolState.msgId,
|
|
@@ -3204,7 +3652,7 @@ var MessageDraft = class {
|
|
|
3204
3652
|
} catch {
|
|
3205
3653
|
}
|
|
3206
3654
|
}
|
|
3207
|
-
const mdChunks =
|
|
3655
|
+
const mdChunks = splitMessage2(this.buffer);
|
|
3208
3656
|
const chunkPromises = [];
|
|
3209
3657
|
for (let i = 0; i < mdChunks.length; i++) {
|
|
3210
3658
|
const html = markdownToTelegramHtml(mdChunks[i]);
|
|
@@ -3332,7 +3780,7 @@ var DraftManager = class {
|
|
|
3332
3780
|
};
|
|
3333
3781
|
|
|
3334
3782
|
// src/adapters/telegram/skill-command-manager.ts
|
|
3335
|
-
var
|
|
3783
|
+
var log12 = createChildLogger({ module: "skill-commands" });
|
|
3336
3784
|
var SkillCommandManager = class {
|
|
3337
3785
|
// sessionId → pinned msgId
|
|
3338
3786
|
constructor(bot, chatId, sendQueue, sessionManager) {
|
|
@@ -3398,7 +3846,7 @@ var SkillCommandManager = class {
|
|
|
3398
3846
|
disable_notification: true
|
|
3399
3847
|
});
|
|
3400
3848
|
} catch (err) {
|
|
3401
|
-
|
|
3849
|
+
log12.error({ err, sessionId }, "Failed to send skill commands");
|
|
3402
3850
|
}
|
|
3403
3851
|
}
|
|
3404
3852
|
async cleanup(sessionId) {
|
|
@@ -3427,7 +3875,7 @@ var SkillCommandManager = class {
|
|
|
3427
3875
|
};
|
|
3428
3876
|
|
|
3429
3877
|
// src/adapters/telegram/adapter.ts
|
|
3430
|
-
var
|
|
3878
|
+
var log13 = createChildLogger({ module: "telegram" });
|
|
3431
3879
|
function patchedFetch(input, init) {
|
|
3432
3880
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
3433
3881
|
const nativeController = new AbortController();
|
|
@@ -3456,6 +3904,12 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3456
3904
|
skillManager;
|
|
3457
3905
|
fileService;
|
|
3458
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
|
+
}
|
|
3459
3913
|
getOrCreateTracker(sessionId, threadId) {
|
|
3460
3914
|
let tracker = this.sessionTrackers.get(sessionId);
|
|
3461
3915
|
if (!tracker) {
|
|
@@ -3481,8 +3935,16 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3481
3935
|
}
|
|
3482
3936
|
});
|
|
3483
3937
|
this.fileService = this.core.fileService;
|
|
3484
|
-
this.toolTracker = new ToolCallTracker(
|
|
3485
|
-
|
|
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
|
+
);
|
|
3486
3948
|
this.skillManager = new SkillCommandManager(
|
|
3487
3949
|
this.bot,
|
|
3488
3950
|
this.telegramConfig.chatId,
|
|
@@ -3491,7 +3953,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3491
3953
|
);
|
|
3492
3954
|
this.bot.catch((err) => {
|
|
3493
3955
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
3494
|
-
|
|
3956
|
+
log13.error({ err: rootCause }, "Telegram bot error");
|
|
3495
3957
|
});
|
|
3496
3958
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
3497
3959
|
const maxRetries = 3;
|
|
@@ -3501,11 +3963,15 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3501
3963
|
return result;
|
|
3502
3964
|
}
|
|
3503
3965
|
const retryAfter = (result.parameters?.retry_after ?? 5) + 1;
|
|
3504
|
-
const rateLimitedMethods = [
|
|
3966
|
+
const rateLimitedMethods = [
|
|
3967
|
+
"sendMessage",
|
|
3968
|
+
"editMessageText",
|
|
3969
|
+
"editMessageReplyMarkup"
|
|
3970
|
+
];
|
|
3505
3971
|
if (rateLimitedMethods.includes(method)) {
|
|
3506
3972
|
this.sendQueue.onRateLimited();
|
|
3507
3973
|
}
|
|
3508
|
-
|
|
3974
|
+
log13.warn(
|
|
3509
3975
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
3510
3976
|
"Rate limited by Telegram, retrying"
|
|
3511
3977
|
);
|
|
@@ -3551,6 +4017,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3551
4017
|
);
|
|
3552
4018
|
setupDangerousModeCallbacks(this.bot, this.core);
|
|
3553
4019
|
setupTTSCallbacks(this.bot, this.core);
|
|
4020
|
+
setupVerbosityCallbacks(this.bot, this.core);
|
|
3554
4021
|
setupActionCallbacks(
|
|
3555
4022
|
this.bot,
|
|
3556
4023
|
this.core,
|
|
@@ -3562,7 +4029,10 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3562
4029
|
this.bot,
|
|
3563
4030
|
this.core,
|
|
3564
4031
|
this.telegramConfig.chatId,
|
|
3565
|
-
{
|
|
4032
|
+
{
|
|
4033
|
+
notificationTopicId: this.notificationTopicId,
|
|
4034
|
+
assistantTopicId: this.assistantTopicId
|
|
4035
|
+
},
|
|
3566
4036
|
() => {
|
|
3567
4037
|
if (!this.assistantSession) return void 0;
|
|
3568
4038
|
return {
|
|
@@ -3606,8 +4076,14 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3606
4076
|
});
|
|
3607
4077
|
return;
|
|
3608
4078
|
}
|
|
3609
|
-
const session = this.core.sessionManager.getSessionByThread(
|
|
3610
|
-
|
|
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
|
+
);
|
|
3611
4087
|
const agentName = session?.agentName ?? record?.agentName;
|
|
3612
4088
|
const agentSessionId = session?.agentSessionId ?? record?.agentSessionId;
|
|
3613
4089
|
if (!agentName || !agentSessionId) {
|
|
@@ -3638,7 +4114,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3638
4114
|
this.setupRoutes();
|
|
3639
4115
|
this.bot.start({
|
|
3640
4116
|
allowed_updates: ["message", "callback_query"],
|
|
3641
|
-
onStart: () =>
|
|
4117
|
+
onStart: () => log13.info(
|
|
3642
4118
|
{ chatId: this.telegramConfig.chatId },
|
|
3643
4119
|
"Telegram bot started"
|
|
3644
4120
|
)
|
|
@@ -3648,7 +4124,9 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3648
4124
|
const agents = this.core.agentManager.getAvailableAgents();
|
|
3649
4125
|
const allRecords = this.core.sessionManager.listRecords();
|
|
3650
4126
|
const welcomeText = buildWelcomeMessage({
|
|
3651
|
-
activeCount: allRecords.filter(
|
|
4127
|
+
activeCount: allRecords.filter(
|
|
4128
|
+
(r) => r.status === "active" || r.status === "initializing"
|
|
4129
|
+
).length,
|
|
3652
4130
|
errorCount: allRecords.filter((r) => r.status === "error").length,
|
|
3653
4131
|
totalCount: allRecords.length,
|
|
3654
4132
|
agents: agents.map((a) => a.name),
|
|
@@ -3660,10 +4138,10 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3660
4138
|
reply_markup: buildMenuKeyboard()
|
|
3661
4139
|
});
|
|
3662
4140
|
} catch (err) {
|
|
3663
|
-
|
|
4141
|
+
log13.warn({ err }, "Failed to send welcome message");
|
|
3664
4142
|
}
|
|
3665
4143
|
try {
|
|
3666
|
-
|
|
4144
|
+
log13.info("Spawning assistant session...");
|
|
3667
4145
|
const { session, ready } = await spawnAssistant(
|
|
3668
4146
|
this.core,
|
|
3669
4147
|
this,
|
|
@@ -3671,13 +4149,19 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3671
4149
|
);
|
|
3672
4150
|
this.assistantSession = session;
|
|
3673
4151
|
this.assistantInitializing = true;
|
|
3674
|
-
|
|
4152
|
+
log13.info(
|
|
4153
|
+
{ sessionId: session.id },
|
|
4154
|
+
"Assistant session ready, system prompt running in background"
|
|
4155
|
+
);
|
|
3675
4156
|
ready.then(() => {
|
|
3676
4157
|
this.assistantInitializing = false;
|
|
3677
|
-
|
|
4158
|
+
log13.info(
|
|
4159
|
+
{ sessionId: session.id },
|
|
4160
|
+
"Assistant ready for user messages"
|
|
4161
|
+
);
|
|
3678
4162
|
});
|
|
3679
4163
|
} catch (err) {
|
|
3680
|
-
|
|
4164
|
+
log13.error({ err }, "Failed to spawn assistant");
|
|
3681
4165
|
this.bot.api.sendMessage(
|
|
3682
4166
|
this.telegramConfig.chatId,
|
|
3683
4167
|
`\u26A0\uFE0F <b>Failed to start assistant session.</b>
|
|
@@ -3693,13 +4177,26 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3693
4177
|
await this.assistantSession.destroy();
|
|
3694
4178
|
}
|
|
3695
4179
|
await this.bot.stop();
|
|
3696
|
-
|
|
4180
|
+
log13.info("Telegram bot stopped");
|
|
3697
4181
|
}
|
|
3698
4182
|
setupRoutes() {
|
|
3699
4183
|
this.bot.on("message:text", async (ctx) => {
|
|
3700
4184
|
const threadId = ctx.message.message_thread_id;
|
|
3701
4185
|
const text = ctx.message.text;
|
|
3702
|
-
if (await handlePendingWorkspaceInput(
|
|
4186
|
+
if (await handlePendingWorkspaceInput(
|
|
4187
|
+
ctx,
|
|
4188
|
+
this.core,
|
|
4189
|
+
this.telegramConfig.chatId,
|
|
4190
|
+
this.assistantTopicId
|
|
4191
|
+
)) {
|
|
4192
|
+
return;
|
|
4193
|
+
}
|
|
4194
|
+
if (await handlePendingResumeInput(
|
|
4195
|
+
ctx,
|
|
4196
|
+
this.core,
|
|
4197
|
+
this.telegramConfig.chatId,
|
|
4198
|
+
this.assistantTopicId
|
|
4199
|
+
)) {
|
|
3703
4200
|
return;
|
|
3704
4201
|
}
|
|
3705
4202
|
if (!threadId) {
|
|
@@ -3714,19 +4211,29 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3714
4211
|
const forwardText = text.startsWith("/") ? text.slice(1) : text;
|
|
3715
4212
|
if (threadId === this.assistantTopicId) {
|
|
3716
4213
|
if (!this.assistantSession) {
|
|
3717
|
-
await ctx.reply(
|
|
4214
|
+
await ctx.reply(
|
|
4215
|
+
"\u26A0\uFE0F Assistant is not available yet. Please try again shortly.",
|
|
4216
|
+
{ parse_mode: "HTML" }
|
|
4217
|
+
);
|
|
3718
4218
|
return;
|
|
3719
4219
|
}
|
|
3720
|
-
await this.draftManager.finalize(
|
|
4220
|
+
await this.draftManager.finalize(
|
|
4221
|
+
this.assistantSession.id,
|
|
4222
|
+
this.assistantSession.id
|
|
4223
|
+
);
|
|
3721
4224
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
3722
4225
|
});
|
|
3723
4226
|
handleAssistantMessage(this.assistantSession, forwardText).catch(
|
|
3724
|
-
(err) =>
|
|
4227
|
+
(err) => log13.error({ err }, "Assistant error")
|
|
3725
4228
|
);
|
|
3726
4229
|
return;
|
|
3727
4230
|
}
|
|
3728
|
-
const sessionId = this.core.sessionManager.getSessionByThread(
|
|
3729
|
-
|
|
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);
|
|
3730
4237
|
if (sessionId) {
|
|
3731
4238
|
const tracker = this.sessionTrackers.get(sessionId);
|
|
3732
4239
|
if (tracker) await tracker.onNewPrompt();
|
|
@@ -3738,7 +4245,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3738
4245
|
threadId: String(threadId),
|
|
3739
4246
|
userId: String(ctx.from.id),
|
|
3740
4247
|
text: forwardText
|
|
3741
|
-
}).catch((err) =>
|
|
4248
|
+
}).catch((err) => log13.error({ err }, "handleMessage error"));
|
|
3742
4249
|
});
|
|
3743
4250
|
this.bot.on("message:photo", async (ctx) => {
|
|
3744
4251
|
const threadId = ctx.message.message_thread_id;
|
|
@@ -3825,25 +4332,57 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3825
4332
|
this.draftManager.appendText(ctx.sessionId, content.text);
|
|
3826
4333
|
},
|
|
3827
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;
|
|
3828
4341
|
const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.threadId);
|
|
3829
4342
|
await tracker.onToolCall();
|
|
3830
|
-
await this.draftManager.finalize(
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
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
|
+
);
|
|
3835
4362
|
},
|
|
3836
4363
|
onToolUpdate: async (ctx, content) => {
|
|
3837
|
-
const meta = content.metadata;
|
|
3838
|
-
await this.toolTracker.updateCall(
|
|
3839
|
-
|
|
3840
|
-
|
|
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
|
+
);
|
|
3841
4379
|
},
|
|
3842
4380
|
onPlan: async (ctx, content) => {
|
|
3843
|
-
const meta = content.metadata;
|
|
4381
|
+
const meta = content.metadata ?? {};
|
|
4382
|
+
const entries = meta.entries ?? [];
|
|
3844
4383
|
const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.threadId);
|
|
3845
4384
|
await tracker.onPlan(
|
|
3846
|
-
|
|
4385
|
+
entries.map((e) => ({
|
|
3847
4386
|
content: e.content,
|
|
3848
4387
|
status: e.status,
|
|
3849
4388
|
priority: e.priority ?? "medium"
|
|
@@ -3852,7 +4391,10 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3852
4391
|
},
|
|
3853
4392
|
onUsage: async (ctx, content) => {
|
|
3854
4393
|
const meta = content.metadata;
|
|
3855
|
-
await this.draftManager.finalize(
|
|
4394
|
+
await this.draftManager.finalize(
|
|
4395
|
+
ctx.sessionId,
|
|
4396
|
+
this.assistantSession?.id
|
|
4397
|
+
);
|
|
3856
4398
|
const tracker = this.getOrCreateTracker(ctx.sessionId, ctx.threadId);
|
|
3857
4399
|
await tracker.sendUsage(meta ?? {});
|
|
3858
4400
|
if (this.notificationTopicId && ctx.sessionId !== this.assistantSession?.id) {
|
|
@@ -3880,7 +4422,14 @@ Task completed.
|
|
|
3880
4422
|
if (!content.attachment) return;
|
|
3881
4423
|
const { attachment } = content;
|
|
3882
4424
|
if (attachment.size > 50 * 1024 * 1024) {
|
|
3883
|
-
|
|
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
|
+
);
|
|
3884
4433
|
await this.sendQueue.enqueue(
|
|
3885
4434
|
() => this.bot.api.sendMessage(
|
|
3886
4435
|
this.telegramConfig.chatId,
|
|
@@ -3917,11 +4466,17 @@ Task completed.
|
|
|
3917
4466
|
);
|
|
3918
4467
|
}
|
|
3919
4468
|
} catch (err) {
|
|
3920
|
-
|
|
4469
|
+
log13.error(
|
|
4470
|
+
{ err, sessionId: ctx.sessionId, fileName: attachment.fileName },
|
|
4471
|
+
"Failed to send attachment"
|
|
4472
|
+
);
|
|
3921
4473
|
}
|
|
3922
4474
|
},
|
|
3923
4475
|
onSessionEnd: async (ctx, _content) => {
|
|
3924
|
-
await this.draftManager.finalize(
|
|
4476
|
+
await this.draftManager.finalize(
|
|
4477
|
+
ctx.sessionId,
|
|
4478
|
+
this.assistantSession?.id
|
|
4479
|
+
);
|
|
3925
4480
|
this.draftManager.cleanup(ctx.sessionId);
|
|
3926
4481
|
this.toolTracker.cleanup(ctx.sessionId);
|
|
3927
4482
|
await this.skillManager.cleanup(ctx.sessionId);
|
|
@@ -3945,7 +4500,10 @@ Task completed.
|
|
|
3945
4500
|
}
|
|
3946
4501
|
},
|
|
3947
4502
|
onError: async (ctx, content) => {
|
|
3948
|
-
await this.draftManager.finalize(
|
|
4503
|
+
await this.draftManager.finalize(
|
|
4504
|
+
ctx.sessionId,
|
|
4505
|
+
this.assistantSession?.id
|
|
4506
|
+
);
|
|
3949
4507
|
const tracker = this.sessionTrackers.get(ctx.sessionId);
|
|
3950
4508
|
if (tracker) {
|
|
3951
4509
|
tracker.destroy();
|
|
@@ -3979,20 +4537,24 @@ Task completed.
|
|
|
3979
4537
|
};
|
|
3980
4538
|
// --- ChannelAdapter implementations ---
|
|
3981
4539
|
async sendMessage(sessionId, content) {
|
|
3982
|
-
if (this.assistantInitializing && sessionId === this.assistantSession?.id)
|
|
4540
|
+
if (this.assistantInitializing && sessionId === this.assistantSession?.id)
|
|
4541
|
+
return;
|
|
3983
4542
|
const session = this.core.sessionManager.getSession(sessionId);
|
|
3984
4543
|
if (!session) return;
|
|
3985
4544
|
if (session.archiving) return;
|
|
3986
4545
|
const threadId = Number(session.threadId);
|
|
3987
4546
|
if (!threadId || isNaN(threadId)) {
|
|
3988
|
-
|
|
4547
|
+
log13.warn(
|
|
4548
|
+
{ sessionId, threadId: session.threadId },
|
|
4549
|
+
"Session has no valid threadId, skipping message"
|
|
4550
|
+
);
|
|
3989
4551
|
return;
|
|
3990
4552
|
}
|
|
3991
4553
|
const ctx = { sessionId, threadId };
|
|
3992
|
-
await dispatchMessage(this.messageHandlers, ctx, content);
|
|
4554
|
+
await dispatchMessage(this.messageHandlers, ctx, content, this.verbosity);
|
|
3993
4555
|
}
|
|
3994
4556
|
async sendPermissionRequest(sessionId, request) {
|
|
3995
|
-
|
|
4557
|
+
log13.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
3996
4558
|
const session = this.core.sessionManager.getSession(sessionId);
|
|
3997
4559
|
if (!session) return;
|
|
3998
4560
|
await this.sendQueue.enqueue(
|
|
@@ -4001,7 +4563,7 @@ Task completed.
|
|
|
4001
4563
|
}
|
|
4002
4564
|
async sendNotification(notification) {
|
|
4003
4565
|
if (notification.sessionId === this.assistantSession?.id) return;
|
|
4004
|
-
|
|
4566
|
+
log13.info(
|
|
4005
4567
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
4006
4568
|
"Notification sent"
|
|
4007
4569
|
);
|
|
@@ -4016,7 +4578,9 @@ Task completed.
|
|
|
4016
4578
|
`;
|
|
4017
4579
|
text += escapeHtml(notification.summary);
|
|
4018
4580
|
const deepLink = notification.deepLink ?? (() => {
|
|
4019
|
-
const session = this.core.sessionManager.getSession(
|
|
4581
|
+
const session = this.core.sessionManager.getSession(
|
|
4582
|
+
notification.sessionId
|
|
4583
|
+
);
|
|
4020
4584
|
const threadId = session?.threadId;
|
|
4021
4585
|
if (!threadId) return void 0;
|
|
4022
4586
|
const chatIdStr = String(this.telegramConfig.chatId);
|
|
@@ -4028,16 +4592,18 @@ Task completed.
|
|
|
4028
4592
|
|
|
4029
4593
|
<a href="${deepLink}">\u2192 Go to topic</a>`;
|
|
4030
4594
|
}
|
|
4595
|
+
const replyMarkup = notification.type === "completed" ? { inline_keyboard: [[{ text: "\u{1F4CB} Summary", callback_data: `sm:summary:${notification.sessionId}` }]] } : void 0;
|
|
4031
4596
|
await this.sendQueue.enqueue(
|
|
4032
4597
|
() => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
|
|
4033
4598
|
message_thread_id: this.notificationTopicId,
|
|
4034
4599
|
parse_mode: "HTML",
|
|
4035
|
-
disable_notification: false
|
|
4600
|
+
disable_notification: false,
|
|
4601
|
+
reply_markup: replyMarkup
|
|
4036
4602
|
})
|
|
4037
4603
|
);
|
|
4038
4604
|
}
|
|
4039
4605
|
async createSessionThread(sessionId, name) {
|
|
4040
|
-
|
|
4606
|
+
log13.info({ sessionId, name }, "Session topic created");
|
|
4041
4607
|
return String(
|
|
4042
4608
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
4043
4609
|
);
|
|
@@ -4061,7 +4627,10 @@ Task completed.
|
|
|
4061
4627
|
try {
|
|
4062
4628
|
await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
|
|
4063
4629
|
} catch (err) {
|
|
4064
|
-
|
|
4630
|
+
log13.warn(
|
|
4631
|
+
{ err, sessionId, topicId },
|
|
4632
|
+
"Failed to delete forum topic (may already be deleted)"
|
|
4633
|
+
);
|
|
4065
4634
|
}
|
|
4066
4635
|
}
|
|
4067
4636
|
async sendSkillCommands(sessionId, commands) {
|
|
@@ -4073,7 +4642,10 @@ Task completed.
|
|
|
4073
4642
|
await this.skillManager.send(sessionId, threadId, commands);
|
|
4074
4643
|
}
|
|
4075
4644
|
resolveSessionId(threadId) {
|
|
4076
|
-
return this.core.sessionManager.getSessionByThread(
|
|
4645
|
+
return this.core.sessionManager.getSessionByThread(
|
|
4646
|
+
"telegram",
|
|
4647
|
+
String(threadId)
|
|
4648
|
+
)?.id;
|
|
4077
4649
|
}
|
|
4078
4650
|
async downloadTelegramFile(fileId) {
|
|
4079
4651
|
try {
|
|
@@ -4085,7 +4657,7 @@ Task completed.
|
|
|
4085
4657
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
4086
4658
|
return { buffer, filePath: file.file_path };
|
|
4087
4659
|
} catch (err) {
|
|
4088
|
-
|
|
4660
|
+
log13.error({ err }, "Failed to download file from Telegram");
|
|
4089
4661
|
return null;
|
|
4090
4662
|
}
|
|
4091
4663
|
}
|
|
@@ -4096,18 +4668,28 @@ Task completed.
|
|
|
4096
4668
|
let originalFilePath;
|
|
4097
4669
|
const sessionId = this.resolveSessionId(threadId) || "unknown";
|
|
4098
4670
|
if (convertOggToWav) {
|
|
4099
|
-
const oggAtt = await this.fileService.saveFile(
|
|
4671
|
+
const oggAtt = await this.fileService.saveFile(
|
|
4672
|
+
sessionId,
|
|
4673
|
+
"voice.ogg",
|
|
4674
|
+
downloaded.buffer,
|
|
4675
|
+
"audio/ogg"
|
|
4676
|
+
);
|
|
4100
4677
|
originalFilePath = oggAtt.filePath;
|
|
4101
4678
|
try {
|
|
4102
4679
|
buffer = await this.fileService.convertOggToWav(buffer);
|
|
4103
4680
|
} catch (err) {
|
|
4104
|
-
|
|
4681
|
+
log13.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
|
|
4105
4682
|
fileName = "voice.ogg";
|
|
4106
4683
|
mimeType = "audio/ogg";
|
|
4107
4684
|
originalFilePath = void 0;
|
|
4108
4685
|
}
|
|
4109
4686
|
}
|
|
4110
|
-
const att = await this.fileService.saveFile(
|
|
4687
|
+
const att = await this.fileService.saveFile(
|
|
4688
|
+
sessionId,
|
|
4689
|
+
fileName,
|
|
4690
|
+
buffer,
|
|
4691
|
+
mimeType
|
|
4692
|
+
);
|
|
4111
4693
|
if (originalFilePath) {
|
|
4112
4694
|
att.originalFilePath = originalFilePath;
|
|
4113
4695
|
}
|
|
@@ -4127,7 +4709,7 @@ Task completed.
|
|
|
4127
4709
|
userId: String(userId),
|
|
4128
4710
|
text,
|
|
4129
4711
|
attachments: [att]
|
|
4130
|
-
}).catch((err) =>
|
|
4712
|
+
}).catch((err) => log13.error({ err }, "handleMessage error"));
|
|
4131
4713
|
}
|
|
4132
4714
|
async cleanupSkillCommands(sessionId) {
|
|
4133
4715
|
await this.skillManager.cleanup(sessionId);
|
|
@@ -4135,10 +4717,9 @@ Task completed.
|
|
|
4135
4717
|
async archiveSessionTopic(sessionId) {
|
|
4136
4718
|
const core = this.core;
|
|
4137
4719
|
const session = core.sessionManager.getSession(sessionId);
|
|
4138
|
-
if (!session) return
|
|
4720
|
+
if (!session) return;
|
|
4139
4721
|
const chatId = this.telegramConfig.chatId;
|
|
4140
4722
|
const oldTopicId = Number(session.threadId);
|
|
4141
|
-
const rawName = (session.name || `Session ${session.id.slice(0, 6)}`).replace(/^🔄\s*/, "");
|
|
4142
4723
|
session.archiving = true;
|
|
4143
4724
|
await this.draftManager.finalize(session.id, this.assistantSession?.id);
|
|
4144
4725
|
this.draftManager.cleanup(session.id);
|
|
@@ -4150,32 +4731,10 @@ Task completed.
|
|
|
4150
4731
|
this.sessionTrackers.delete(session.id);
|
|
4151
4732
|
}
|
|
4152
4733
|
await deleteSessionTopic(this.bot, chatId, oldTopicId);
|
|
4153
|
-
let newTopicId;
|
|
4154
|
-
try {
|
|
4155
|
-
newTopicId = await createSessionTopic(this.bot, chatId, `\u{1F504} ${rawName}`);
|
|
4156
|
-
} catch (createErr) {
|
|
4157
|
-
session.archiving = false;
|
|
4158
|
-
core.notificationManager.notifyAll({
|
|
4159
|
-
sessionId: session.id,
|
|
4160
|
-
sessionName: session.name,
|
|
4161
|
-
type: "error",
|
|
4162
|
-
summary: `Topic recreation failed for session "${rawName}". Session is orphaned. Error: ${createErr.message}`
|
|
4163
|
-
});
|
|
4164
|
-
throw createErr;
|
|
4165
|
-
}
|
|
4166
|
-
session.threadId = String(newTopicId);
|
|
4167
|
-
const existingRecord = core.sessionManager.getSessionRecord(session.id);
|
|
4168
|
-
const existingPlatform = { ...existingRecord?.platform ?? {} };
|
|
4169
|
-
delete existingPlatform.skillMsgId;
|
|
4170
|
-
await core.sessionManager.patchRecord(session.id, {
|
|
4171
|
-
platform: { ...existingPlatform, topicId: newTopicId }
|
|
4172
|
-
});
|
|
4173
|
-
session.archiving = false;
|
|
4174
|
-
return { newThreadId: String(newTopicId) };
|
|
4175
4734
|
}
|
|
4176
4735
|
};
|
|
4177
4736
|
|
|
4178
4737
|
export {
|
|
4179
4738
|
TelegramAdapter
|
|
4180
4739
|
};
|
|
4181
|
-
//# sourceMappingURL=chunk-
|
|
4740
|
+
//# sourceMappingURL=chunk-5SXG7X5D.js.map
|