@pixelbyte-software/pixcode 1.53.5 → 1.53.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/assets/{index-CyeODv14.js → index-DM-KfWAm.js} +3 -3
- package/dist/index.html +1 -1
- package/dist-server/server/claude-sdk.js +17 -15
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +14 -12
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +14 -12
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/openai-codex.js +30 -22
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +14 -12
- package/dist-server/server/opencode-cli.js.map +1 -1
- package/dist-server/server/qwen-code-cli.js +14 -12
- package/dist-server/server/qwen-code-cli.js.map +1 -1
- package/dist-server/server/routes/agent.js +24 -7
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/services/telegram/control-center.js +593 -82
- package/dist-server/server/services/telegram/control-center.js.map +1 -1
- package/dist-server/server/services/telegram/telegram-gateway.js +111 -45
- package/dist-server/server/services/telegram/telegram-gateway.js.map +1 -1
- package/dist-server/server/services/telegram/translations.js +60 -0
- package/dist-server/server/services/telegram/translations.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/telegram-control.mjs +40 -1
- package/server/claude-sdk.js +3 -3
- package/server/cursor-cli.js +3 -3
- package/server/gemini-cli.js +3 -3
- package/server/openai-codex.js +10 -4
- package/server/opencode-cli.js +3 -3
- package/server/qwen-code-cli.js +3 -3
- package/server/routes/agent.js +24 -7
- package/server/services/telegram/control-center.js +593 -80
- package/server/services/telegram/telegram-gateway.js +115 -51
- package/server/services/telegram/translations.js +60 -0
|
@@ -8,11 +8,12 @@ import {
|
|
|
8
8
|
TELEGRAM_CONTROL_SCOPES,
|
|
9
9
|
TELEGRAM_PROVIDERS,
|
|
10
10
|
buildTelegramAgentPrompt,
|
|
11
|
-
|
|
11
|
+
buildTelegramIntentPrompt,
|
|
12
12
|
clearTelegramConfirmation,
|
|
13
13
|
consumeTelegramConfirmation,
|
|
14
14
|
createTelegramConfirmation,
|
|
15
15
|
enqueueTelegramJob,
|
|
16
|
+
parseTelegramAiIntentResponse,
|
|
16
17
|
resolveTelegramModel,
|
|
17
18
|
resolveTelegramProvider,
|
|
18
19
|
retryWithBackoff,
|
|
@@ -26,6 +27,9 @@ const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'canceled']);
|
|
|
26
27
|
const CALLBACK_TTL_MS = 10 * 60 * 1000;
|
|
27
28
|
const MAX_CALLBACK_ACTIONS = 1000;
|
|
28
29
|
const MAX_TELEGRAM_TEXT = 3600;
|
|
30
|
+
const ACTIVITY_EDIT_THROTTLE_MS = 1200;
|
|
31
|
+
const ACTIVITY_HEARTBEAT_MS = 8000;
|
|
32
|
+
const INTENT_ROUTER_TIMEOUT_MS = 45_000;
|
|
29
33
|
const callbackActions = new Map();
|
|
30
34
|
const runMonitors = new Map();
|
|
31
35
|
|
|
@@ -230,16 +234,23 @@ function getOrCreateTelegramApiKey(userId) {
|
|
|
230
234
|
return apiKeysDb.createApiKey(userId, 'Telegram Control', TELEGRAM_CONTROL_SCOPES).apiKey;
|
|
231
235
|
}
|
|
232
236
|
|
|
233
|
-
async function localApi(userId, path, { method = 'GET', body } = {}) {
|
|
237
|
+
async function localApi(userId, path, { method = 'GET', body, timeoutMs = 0 } = {}) {
|
|
234
238
|
const apiKey = getOrCreateTelegramApiKey(userId);
|
|
235
239
|
const response = await retryWithBackoff(async () => {
|
|
240
|
+
const controller = timeoutMs > 0 ? new AbortController() : null;
|
|
241
|
+
const timeoutId = controller
|
|
242
|
+
? setTimeout(() => controller.abort(new Error(`HTTP request timed out after ${timeoutMs}ms`)), timeoutMs)
|
|
243
|
+
: null;
|
|
236
244
|
const res = await fetch(`${localApiBase()}${path}`, {
|
|
237
245
|
method,
|
|
238
246
|
headers: {
|
|
239
247
|
'Content-Type': 'application/json',
|
|
240
248
|
'X-API-Key': apiKey,
|
|
241
249
|
},
|
|
250
|
+
signal: controller?.signal,
|
|
242
251
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
252
|
+
}).finally(() => {
|
|
253
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
243
254
|
});
|
|
244
255
|
if ([408, 429, 500, 502, 503, 504].includes(res.status)) {
|
|
245
256
|
const error = new Error(`HTTP ${res.status}`);
|
|
@@ -256,10 +267,223 @@ async function localApi(userId, path, { method = 'GET', body } = {}) {
|
|
|
256
267
|
return data?.data ?? data;
|
|
257
268
|
}
|
|
258
269
|
|
|
270
|
+
async function localAgentStream(userId, body, onEvent) {
|
|
271
|
+
const apiKey = getOrCreateTelegramApiKey(userId);
|
|
272
|
+
const response = await fetch(`${localApiBase()}/api/agent`, {
|
|
273
|
+
method: 'POST',
|
|
274
|
+
headers: {
|
|
275
|
+
'Content-Type': 'application/json',
|
|
276
|
+
'X-API-Key': apiKey,
|
|
277
|
+
},
|
|
278
|
+
body: JSON.stringify({
|
|
279
|
+
...body,
|
|
280
|
+
stream: true,
|
|
281
|
+
}),
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
const data = await response.json().catch(() => ({}));
|
|
286
|
+
const error = data?.error?.message || data?.error || `HTTP ${response.status}`;
|
|
287
|
+
throw new Error(typeof error === 'string' ? error : JSON.stringify(error));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!response.body) return;
|
|
291
|
+
|
|
292
|
+
const reader = response.body.getReader();
|
|
293
|
+
const decoder = new TextDecoder();
|
|
294
|
+
let buffer = '';
|
|
295
|
+
|
|
296
|
+
const consumeBlock = async (block) => {
|
|
297
|
+
const lines = String(block || '').split('\n');
|
|
298
|
+
for (const line of lines) {
|
|
299
|
+
if (!line.startsWith('data:')) continue;
|
|
300
|
+
const payload = line.slice(5).trim();
|
|
301
|
+
if (!payload) continue;
|
|
302
|
+
let event;
|
|
303
|
+
try {
|
|
304
|
+
event = JSON.parse(payload);
|
|
305
|
+
} catch {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
await onEvent?.(event);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
for (;;) {
|
|
313
|
+
const { done, value } = await reader.read();
|
|
314
|
+
if (done) break;
|
|
315
|
+
buffer += decoder.decode(value, { stream: true });
|
|
316
|
+
let boundary = buffer.indexOf('\n\n');
|
|
317
|
+
while (boundary !== -1) {
|
|
318
|
+
const block = buffer.slice(0, boundary);
|
|
319
|
+
buffer = buffer.slice(boundary + 2);
|
|
320
|
+
await consumeBlock(block);
|
|
321
|
+
boundary = buffer.indexOf('\n\n');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const rest = `${buffer}${decoder.decode()}`;
|
|
326
|
+
if (rest.trim()) await consumeBlock(rest);
|
|
327
|
+
}
|
|
328
|
+
|
|
259
329
|
function checked(label) {
|
|
260
330
|
return `${label} ✓`;
|
|
261
331
|
}
|
|
262
332
|
|
|
333
|
+
function formatElapsed(startedAt) {
|
|
334
|
+
const seconds = Math.max(0, Math.round((Date.now() - startedAt) / 1000));
|
|
335
|
+
if (seconds < 60) return `${seconds}s`;
|
|
336
|
+
const minutes = Math.floor(seconds / 60);
|
|
337
|
+
const rest = seconds % 60;
|
|
338
|
+
return `${minutes}m ${rest}s`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function projectLabel(state) {
|
|
342
|
+
return state.selectedProjectName || state.selectedProjectPath || '-';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function createActivityState({ lang, type = 'agent', provider, project, prompt, mode = 'final' }) {
|
|
346
|
+
return {
|
|
347
|
+
lang,
|
|
348
|
+
type,
|
|
349
|
+
status: 'starting',
|
|
350
|
+
phase: t(lang, 'control.activity.starting'),
|
|
351
|
+
provider,
|
|
352
|
+
project,
|
|
353
|
+
prompt: compact(prompt, 120),
|
|
354
|
+
mode,
|
|
355
|
+
startedAt: Date.now(),
|
|
356
|
+
lastEditAt: 0,
|
|
357
|
+
messageId: null,
|
|
358
|
+
sessionId: null,
|
|
359
|
+
runId: null,
|
|
360
|
+
workflowId: null,
|
|
361
|
+
events: [],
|
|
362
|
+
output: '',
|
|
363
|
+
error: null,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function pushActivityEvent(activity, text) {
|
|
368
|
+
const value = String(text || '').trim();
|
|
369
|
+
if (!value) return;
|
|
370
|
+
if (activity.events.at(-1) === value) return;
|
|
371
|
+
activity.events.push(value);
|
|
372
|
+
if (activity.events.length > 8) activity.events.splice(0, activity.events.length - 8);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function activityTitle(activity) {
|
|
376
|
+
if (activity.type === 'workflow') return t(activity.lang, 'control.activity.workflowTitle');
|
|
377
|
+
if (activity.type === 'router') return t(activity.lang, 'control.activity.routerTitle');
|
|
378
|
+
return t(activity.lang, 'control.activity.agentTitle');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function trimTelegramOutput(text, max, suffix = '') {
|
|
382
|
+
const value = String(text || '').trim();
|
|
383
|
+
const ending = String(suffix || '').trim();
|
|
384
|
+
if (value.length <= max) return value;
|
|
385
|
+
const room = Math.max(300, max - ending.length - 4);
|
|
386
|
+
return `${value.slice(0, room).trim()}\n\n${ending}`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function renderActivity(activity, { finalText = null } = {}) {
|
|
390
|
+
const output = finalText || activity.output;
|
|
391
|
+
if (activity.type === 'agent' && output && !activity.error) {
|
|
392
|
+
if (activity.status === 'done') {
|
|
393
|
+
return trimTelegramOutput(
|
|
394
|
+
output,
|
|
395
|
+
3400,
|
|
396
|
+
t(activity.lang, 'control.activity.outputTooLong'),
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const footer = `⏳ ${t(activity.lang, 'control.activity.liveFooter', { elapsed: formatElapsed(activity.startedAt) })}`;
|
|
401
|
+
const body = trimTelegramOutput(
|
|
402
|
+
output,
|
|
403
|
+
3200,
|
|
404
|
+
t(activity.lang, 'control.activity.outputShortened'),
|
|
405
|
+
);
|
|
406
|
+
return truncate(`${body}\n\n${footer}`, 3400);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const lines = [
|
|
410
|
+
`${activity.status === 'failed' ? '❌' : activity.status === 'done' ? '✅' : activity.status === 'running' ? '🔧' : '⏳'} ${activityTitle(activity)}`,
|
|
411
|
+
'',
|
|
412
|
+
`🤖 ${t(activity.lang, 'control.activity.provider')}: ${activity.provider || '-'}`,
|
|
413
|
+
`📁 ${t(activity.lang, 'control.activity.project')}: ${compact(activity.project, 90)}`,
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
if (activity.sessionId) lines.push(`🧵 ${t(activity.lang, 'control.activity.session')}: ${activity.sessionId}`);
|
|
417
|
+
if (activity.runId) lines.push(`🧭 ${t(activity.lang, 'control.activity.run')}: ${activity.runId}`);
|
|
418
|
+
if (activity.workflowId) lines.push(`🧩 ${t(activity.lang, 'control.activity.workflow')}: ${activity.workflowId}`);
|
|
419
|
+
lines.push(`⏱ ${t(activity.lang, 'control.activity.elapsed')}: ${formatElapsed(activity.startedAt)}`);
|
|
420
|
+
lines.push(`📌 ${t(activity.lang, 'control.activity.status')}: ${activity.phase}`);
|
|
421
|
+
|
|
422
|
+
const visibleEvents = activity.mode === 'final'
|
|
423
|
+
? activity.events.slice(-4)
|
|
424
|
+
: activity.events;
|
|
425
|
+
if (visibleEvents.length > 0) {
|
|
426
|
+
lines.push('', `🛠 ${t(activity.lang, 'control.activity.work')}:`);
|
|
427
|
+
for (const event of visibleEvents) lines.push(`• ${event}`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (activity.error) {
|
|
431
|
+
lines.push('', `⚠️ ${truncate(activity.error, 700)}`);
|
|
432
|
+
} else if (output) {
|
|
433
|
+
lines.push('', `💬 ${t(activity.lang, 'control.activity.output')}:`);
|
|
434
|
+
lines.push(truncate(output, 1800));
|
|
435
|
+
} else if (activity.prompt) {
|
|
436
|
+
lines.push('', `📝 ${compact(activity.prompt, 220)}`);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return truncate(lines.join('\n'), 3400);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function createTelegramActivity({ bot, chatId, link, type = 'agent', prompt = '', phase = null }) {
|
|
443
|
+
const lang = languageFor(link);
|
|
444
|
+
const state = getState(link.user_id);
|
|
445
|
+
const activity = createActivityState({
|
|
446
|
+
lang,
|
|
447
|
+
type,
|
|
448
|
+
provider: resolveTelegramProvider(state),
|
|
449
|
+
project: projectLabel(state),
|
|
450
|
+
prompt,
|
|
451
|
+
mode: state.progressMode,
|
|
452
|
+
});
|
|
453
|
+
if (phase) activity.phase = phase;
|
|
454
|
+
const sent = await send(bot, chatId, renderActivity(activity), { parse_mode: undefined });
|
|
455
|
+
activity.messageId = sent?.message_id || sent?.message?.message_id || null;
|
|
456
|
+
activity.lastEditAt = Date.now();
|
|
457
|
+
return activity;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async function editTelegramActivity({ bot, chatId, activity, force = false, reply_markup }) {
|
|
461
|
+
if (!activity) return null;
|
|
462
|
+
const now = Date.now();
|
|
463
|
+
if (!force && now - activity.lastEditAt < ACTIVITY_EDIT_THROTTLE_MS) return null;
|
|
464
|
+
const sent = await send(bot, chatId, renderActivity(activity), {
|
|
465
|
+
editMessageId: activity.messageId,
|
|
466
|
+
parse_mode: undefined,
|
|
467
|
+
reply_markup,
|
|
468
|
+
});
|
|
469
|
+
activity.messageId = sent?.message_id || sent?.message?.message_id || activity.messageId;
|
|
470
|
+
activity.lastEditAt = now;
|
|
471
|
+
return sent;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function startActivityHeartbeat({ bot, chatId, activity }) {
|
|
475
|
+
return setInterval(() => {
|
|
476
|
+
if (!activity || activity.status === 'done' || activity.status === 'failed') return;
|
|
477
|
+
if (activity.status === 'starting') {
|
|
478
|
+
activity.status = 'running';
|
|
479
|
+
activity.phase = t(activity.lang, 'control.activity.thinking');
|
|
480
|
+
}
|
|
481
|
+
editTelegramActivity({ bot, chatId, activity }).catch((error) => {
|
|
482
|
+
console.warn('[telegram-control] activity heartbeat failed:', error?.message || error);
|
|
483
|
+
});
|
|
484
|
+
}, ACTIVITY_HEARTBEAT_MS);
|
|
485
|
+
}
|
|
486
|
+
|
|
263
487
|
function stateSummary(lang, state) {
|
|
264
488
|
return [
|
|
265
489
|
`${t(lang, 'control.summary.project')}: ${state.selectedProjectName || t(lang, 'control.notSelected')}`,
|
|
@@ -310,11 +534,12 @@ async function listProjects() {
|
|
|
310
534
|
return projects.slice(0, 20);
|
|
311
535
|
}
|
|
312
536
|
|
|
313
|
-
async function showProjectMenu({ bot, chatId, link, editMessageId }) {
|
|
537
|
+
async function showProjectMenu({ bot, chatId, link, editMessageId, notice }) {
|
|
314
538
|
const lang = languageFor(link);
|
|
315
539
|
const projects = await listProjects();
|
|
316
540
|
if (projects.length === 0) {
|
|
317
|
-
|
|
541
|
+
const prefix = notice ? `${notice}\n\n` : '';
|
|
542
|
+
await send(bot, chatId, `${prefix}${t(lang, 'control.noProjects')}`, { editMessageId });
|
|
318
543
|
return;
|
|
319
544
|
}
|
|
320
545
|
|
|
@@ -327,7 +552,8 @@ async function showProjectMenu({ bot, chatId, link, editMessageId }) {
|
|
|
327
552
|
displayName: project.displayName || project.name,
|
|
328
553
|
},
|
|
329
554
|
));
|
|
330
|
-
|
|
555
|
+
const prefix = notice ? `${notice}\n\n` : '';
|
|
556
|
+
await send(bot, chatId, `${prefix}${t(lang, 'control.pickProject')}`, {
|
|
331
557
|
editMessageId,
|
|
332
558
|
reply_markup: { inline_keyboard: rows(buttons, 1) },
|
|
333
559
|
});
|
|
@@ -539,6 +765,119 @@ function extractAssistantText(response) {
|
|
|
539
765
|
return chunks.join('\n\n').trim();
|
|
540
766
|
}
|
|
541
767
|
|
|
768
|
+
function extractTextFromEvent(event) {
|
|
769
|
+
if (!event || typeof event !== 'object') return '';
|
|
770
|
+
if (typeof event.content === 'string') return event.content;
|
|
771
|
+
if (Array.isArray(event.content)) {
|
|
772
|
+
return event.content
|
|
773
|
+
.map((part) => (typeof part === 'string' ? part : part?.text || ''))
|
|
774
|
+
.join('');
|
|
775
|
+
}
|
|
776
|
+
const legacyContent = event.data?.message?.content || event.message?.content;
|
|
777
|
+
if (Array.isArray(legacyContent)) {
|
|
778
|
+
return legacyContent
|
|
779
|
+
.map((part) => (typeof part === 'string' ? part : part?.text || ''))
|
|
780
|
+
.join('');
|
|
781
|
+
}
|
|
782
|
+
if (typeof legacyContent === 'string') return legacyContent;
|
|
783
|
+
return '';
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function describeToolInput(toolName, input) {
|
|
787
|
+
if (!input || typeof input !== 'object') return '';
|
|
788
|
+
if (toolName === 'Bash' && typeof input.command === 'string') {
|
|
789
|
+
return compact(input.command, 110);
|
|
790
|
+
}
|
|
791
|
+
if (toolName === 'WebSearch' && typeof input.query === 'string') {
|
|
792
|
+
return compact(input.query, 110);
|
|
793
|
+
}
|
|
794
|
+
if (toolName === 'FileChanges') {
|
|
795
|
+
return compact(JSON.stringify(input), 110);
|
|
796
|
+
}
|
|
797
|
+
const keys = Object.keys(input).slice(0, 3);
|
|
798
|
+
if (keys.length === 0) return '';
|
|
799
|
+
return compact(keys.map((key) => `${key}: ${String(input[key])}`).join(', '), 110);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function applyAgentStreamEvent(activity, event) {
|
|
803
|
+
if (!event || typeof event !== 'object') return;
|
|
804
|
+
|
|
805
|
+
const sessionId = event.actualSessionId || event.newSessionId || event.sessionId || event.threadId;
|
|
806
|
+
if (sessionId) activity.sessionId = sessionId;
|
|
807
|
+
|
|
808
|
+
if (event.type === 'status' && event.message) {
|
|
809
|
+
activity.status = 'running';
|
|
810
|
+
activity.phase = compact(event.message, 120);
|
|
811
|
+
pushActivityEvent(activity, `⏳ ${compact(event.message, 120)}`);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
if (event.type === 'done' || event.kind === 'complete' || event.kind === 'stream_end') {
|
|
815
|
+
activity.status = 'done';
|
|
816
|
+
activity.phase = t(activity.lang, 'control.activity.done');
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (event.type === 'error' || event.kind === 'error') {
|
|
820
|
+
activity.status = 'failed';
|
|
821
|
+
activity.phase = t(activity.lang, 'control.activity.failed');
|
|
822
|
+
activity.error = event.error || event.message || event.content || t(activity.lang, 'error.generic');
|
|
823
|
+
pushActivityEvent(activity, `❌ ${compact(activity.error, 120)}`);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (event.kind === 'session_created') {
|
|
827
|
+
activity.status = 'running';
|
|
828
|
+
activity.phase = t(activity.lang, 'control.activity.sessionStarted');
|
|
829
|
+
pushActivityEvent(activity, `🧵 ${t(activity.lang, 'control.activity.sessionStarted')}`);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (event.kind === 'thinking') {
|
|
833
|
+
activity.status = 'running';
|
|
834
|
+
activity.phase = t(activity.lang, 'control.activity.thinking');
|
|
835
|
+
const thought = extractTextFromEvent(event);
|
|
836
|
+
if (thought) pushActivityEvent(activity, `🧠 ${compact(thought, 120)}`);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
if (event.kind === 'stream_delta' || event.kind === 'text') {
|
|
840
|
+
activity.status = 'running';
|
|
841
|
+
activity.phase = t(activity.lang, 'control.activity.responding');
|
|
842
|
+
activity.output = `${activity.output}${extractTextFromEvent(event)}`;
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
if (event.type === 'claude-response' && event.data?.type === 'assistant') {
|
|
846
|
+
activity.status = 'running';
|
|
847
|
+
activity.phase = t(activity.lang, 'control.activity.responding');
|
|
848
|
+
activity.output = `${activity.output}${extractTextFromEvent(event)}`;
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (event.kind === 'tool_use') {
|
|
852
|
+
activity.status = 'running';
|
|
853
|
+
activity.phase = t(activity.lang, 'control.activity.working');
|
|
854
|
+
const toolName = event.toolName || 'Tool';
|
|
855
|
+
const input = describeToolInput(toolName, event.toolInput);
|
|
856
|
+
const icon = toolName === 'Bash'
|
|
857
|
+
? '💻'
|
|
858
|
+
: toolName === 'FileChanges'
|
|
859
|
+
? '📝'
|
|
860
|
+
: toolName === 'WebSearch'
|
|
861
|
+
? '🔎'
|
|
862
|
+
: '🔧';
|
|
863
|
+
pushActivityEvent(activity, `${icon} ${toolName}${input ? `: ${input}` : ''}`);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (event.kind === 'tool_result') {
|
|
867
|
+
activity.status = 'running';
|
|
868
|
+
activity.phase = t(activity.lang, 'control.activity.working');
|
|
869
|
+
const label = event.isError
|
|
870
|
+
? t(activity.lang, 'control.activity.toolFailed')
|
|
871
|
+
: t(activity.lang, 'control.activity.toolDone');
|
|
872
|
+
pushActivityEvent(activity, `${event.isError ? '⚠️' : '✅'} ${label}`);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
if (event.kind === 'status' && event.text === 'token_budget') {
|
|
876
|
+
const used = event.tokenBudget?.used;
|
|
877
|
+
if (used) pushActivityEvent(activity, `📊 ${t(activity.lang, 'control.activity.tokens')}: ${used}`);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
542
881
|
function confirmationLabel(lang, action, payload = {}) {
|
|
543
882
|
if (action === 'install_provider') {
|
|
544
883
|
return t(lang, 'control.confirmInstall', { provider: payload.provider || '' });
|
|
@@ -572,78 +911,106 @@ async function sendToolFailure({ bot, chatId, link, result, editMessageId }) {
|
|
|
572
911
|
await send(bot, chatId, message, { editMessageId });
|
|
573
912
|
}
|
|
574
913
|
|
|
575
|
-
async function runAgent({ bot, chatId, link, prompt }) {
|
|
914
|
+
async function runAgent({ bot, chatId, link, prompt, activity = null }) {
|
|
576
915
|
const lang = languageFor(link);
|
|
577
916
|
const state = getState(link.user_id);
|
|
578
917
|
if (!state.remoteControlEnabled) {
|
|
579
|
-
await send(bot, chatId, t(lang, 'control.disabled'));
|
|
918
|
+
await send(bot, chatId, t(lang, 'control.disabled'), { editMessageId: activity?.messageId });
|
|
580
919
|
return;
|
|
581
920
|
}
|
|
582
921
|
if (!state.selectedProjectPath) {
|
|
583
|
-
await
|
|
584
|
-
await showProjectMenu({ bot, chatId, link });
|
|
922
|
+
await showProjectMenu({ bot, chatId, link, editMessageId: activity?.messageId, notice: t(lang, 'control.selectProjectFirst') });
|
|
585
923
|
return;
|
|
586
924
|
}
|
|
587
925
|
|
|
588
926
|
const provider = resolveTelegramProvider(state);
|
|
589
927
|
const model = resolveTelegramModel(state);
|
|
590
|
-
|
|
591
|
-
|
|
928
|
+
const active = activity || await createTelegramActivity({
|
|
929
|
+
bot,
|
|
930
|
+
chatId,
|
|
931
|
+
link,
|
|
932
|
+
type: 'agent',
|
|
933
|
+
prompt,
|
|
934
|
+
phase: t(lang, 'control.activity.startingProvider', { provider }),
|
|
935
|
+
});
|
|
936
|
+
active.type = 'agent';
|
|
937
|
+
active.provider = provider;
|
|
938
|
+
active.project = projectLabel(state);
|
|
939
|
+
active.status = 'running';
|
|
940
|
+
active.phase = t(lang, 'control.activity.startingProvider', { provider });
|
|
941
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
942
|
+
|
|
943
|
+
const heartbeat = startActivityHeartbeat({ bot, chatId, activity: active });
|
|
944
|
+
let streamFailed = null;
|
|
945
|
+
try {
|
|
946
|
+
await localAgentStream(link.user_id, {
|
|
947
|
+
projectPath: state.selectedProjectPath,
|
|
592
948
|
provider,
|
|
593
|
-
|
|
594
|
-
|
|
949
|
+
model: model || undefined,
|
|
950
|
+
message: buildTelegramAgentPrompt(prompt, state),
|
|
951
|
+
cleanup: false,
|
|
952
|
+
permissionMode: 'default',
|
|
953
|
+
suppressNotifications: true,
|
|
954
|
+
}, async (event) => {
|
|
955
|
+
applyAgentStreamEvent(active, event);
|
|
956
|
+
await editTelegramActivity({ bot, chatId, activity: active });
|
|
957
|
+
});
|
|
958
|
+
} catch (error) {
|
|
959
|
+
streamFailed = error;
|
|
960
|
+
} finally {
|
|
961
|
+
clearInterval(heartbeat);
|
|
595
962
|
}
|
|
596
963
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
method: 'POST',
|
|
603
|
-
body: {
|
|
604
|
-
projectPath: state.selectedProjectPath,
|
|
605
|
-
provider,
|
|
606
|
-
model: model || undefined,
|
|
607
|
-
message: buildTelegramAgentPrompt(prompt, state),
|
|
608
|
-
cleanup: false,
|
|
609
|
-
stream: false,
|
|
610
|
-
},
|
|
611
|
-
}),
|
|
612
|
-
});
|
|
613
|
-
if (!result.ok) {
|
|
614
|
-
await sendToolFailure({ bot, chatId, link, result });
|
|
964
|
+
if (streamFailed) {
|
|
965
|
+
active.status = 'failed';
|
|
966
|
+
active.phase = t(lang, 'control.activity.failed');
|
|
967
|
+
active.error = streamFailed.message || t(lang, 'error.generic');
|
|
968
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
615
969
|
return;
|
|
616
970
|
}
|
|
617
971
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
: t(lang, 'control.agentDone', { provider, session: response.sessionId ? ` (${response.sessionId})` : '' });
|
|
623
|
-
await send(bot, chatId, `${statusLine}\n\n${assistantText || response.error || t(lang, 'control.noAssistantText')}`);
|
|
972
|
+
active.status = active.error ? 'failed' : 'done';
|
|
973
|
+
active.phase = active.error ? t(lang, 'control.activity.failed') : t(lang, 'control.activity.done');
|
|
974
|
+
if (!active.output && !active.error) active.output = t(lang, 'control.noAssistantText');
|
|
975
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
624
976
|
}
|
|
625
977
|
|
|
626
|
-
export async function runWorkflow({ bot, chatId, link, input }) {
|
|
978
|
+
export async function runWorkflow({ bot, chatId, link, input, activity = null }) {
|
|
627
979
|
const lang = languageFor(link);
|
|
628
980
|
const state = getState(link.user_id);
|
|
629
981
|
const workflowId = state.selectedWorkflowId;
|
|
630
982
|
if (!state.remoteControlEnabled) {
|
|
631
|
-
await send(bot, chatId, t(lang, 'control.disabled'));
|
|
983
|
+
await send(bot, chatId, t(lang, 'control.disabled'), { editMessageId: activity?.messageId });
|
|
632
984
|
return;
|
|
633
985
|
}
|
|
634
986
|
if (!workflowId) {
|
|
635
|
-
await send(bot, chatId, t(lang, 'control.selectWorkflowFirst'));
|
|
636
|
-
await showWorkflowMenu({ bot, chatId, link });
|
|
987
|
+
await send(bot, chatId, t(lang, 'control.selectWorkflowFirst'), { editMessageId: activity?.messageId });
|
|
988
|
+
await showWorkflowMenu({ bot, chatId, link, editMessageId: activity?.messageId });
|
|
637
989
|
return;
|
|
638
990
|
}
|
|
639
991
|
if (!state.selectedProjectPath) {
|
|
640
|
-
await
|
|
641
|
-
await showProjectMenu({ bot, chatId, link });
|
|
992
|
+
await showProjectMenu({ bot, chatId, link, editMessageId: activity?.messageId, notice: t(lang, 'control.selectProjectFirst') });
|
|
642
993
|
return;
|
|
643
994
|
}
|
|
644
995
|
|
|
645
996
|
const provider = resolveTelegramProvider(state);
|
|
646
997
|
const model = resolveTelegramModel(state);
|
|
998
|
+
const active = activity || await createTelegramActivity({
|
|
999
|
+
bot,
|
|
1000
|
+
chatId,
|
|
1001
|
+
link,
|
|
1002
|
+
type: 'workflow',
|
|
1003
|
+
prompt: input,
|
|
1004
|
+
phase: t(lang, 'control.activity.startingWorkflow'),
|
|
1005
|
+
});
|
|
1006
|
+
active.type = 'workflow';
|
|
1007
|
+
active.provider = provider;
|
|
1008
|
+
active.project = projectLabel(state);
|
|
1009
|
+
active.workflowId = workflowId;
|
|
1010
|
+
active.status = 'running';
|
|
1011
|
+
active.phase = t(lang, 'control.activity.startingWorkflow');
|
|
1012
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
1013
|
+
|
|
647
1014
|
const result = await runTelegramTool({
|
|
648
1015
|
userId: link.user_id,
|
|
649
1016
|
action: 'run_workflow',
|
|
@@ -664,12 +1031,15 @@ export async function runWorkflow({ bot, chatId, link, input }) {
|
|
|
664
1031
|
}),
|
|
665
1032
|
});
|
|
666
1033
|
if (!result.ok) {
|
|
667
|
-
await sendToolFailure({ bot, chatId, link, result });
|
|
1034
|
+
await sendToolFailure({ bot, chatId, link, result, editMessageId: active.messageId });
|
|
668
1035
|
return;
|
|
669
1036
|
}
|
|
670
1037
|
const run = result.data;
|
|
671
|
-
|
|
672
|
-
|
|
1038
|
+
active.runId = run.id;
|
|
1039
|
+
active.phase = t(lang, 'control.activity.workflowRunning');
|
|
1040
|
+
pushActivityEvent(active, `🧭 ${t(lang, 'control.workflowStarted', { runId: run.id, workflowId }).replace('\n', ' ')}`);
|
|
1041
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
1042
|
+
monitorWorkflowRun({ bot, chatId, link, runId: run.id, activity: active }).catch((error) => {
|
|
673
1043
|
console.warn('[telegram-control] workflow monitor failed:', error?.message || error);
|
|
674
1044
|
});
|
|
675
1045
|
}
|
|
@@ -750,21 +1120,97 @@ async function findProjectByQuery(query) {
|
|
|
750
1120
|
}) || null;
|
|
751
1121
|
}
|
|
752
1122
|
|
|
753
|
-
async function
|
|
1123
|
+
async function listWorkflowSummaries(userId) {
|
|
1124
|
+
try {
|
|
1125
|
+
const data = await localApi(userId, '/api/orchestration/workflows', { timeoutMs: 10_000 });
|
|
1126
|
+
if (Array.isArray(data)) return data;
|
|
1127
|
+
if (Array.isArray(data?.workflows)) return data.workflows;
|
|
1128
|
+
} catch {
|
|
1129
|
+
// Workflow context is helpful for routing but should never block chat input.
|
|
1130
|
+
}
|
|
1131
|
+
return [];
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
async function resolveTelegramAiIntent({ bot, chatId, link, text, activity }) {
|
|
754
1135
|
const state = getState(link.user_id);
|
|
755
|
-
|
|
756
|
-
|
|
1136
|
+
if (state.routerEnabled === false || state.routerMode !== 'hybrid') {
|
|
1137
|
+
return { action: 'agent_prompt', prompt: text, confidence: 1 };
|
|
1138
|
+
}
|
|
757
1139
|
|
|
758
|
-
|
|
759
|
-
|
|
1140
|
+
const provider = resolveTelegramProvider(state);
|
|
1141
|
+
const model = resolveTelegramModel(state);
|
|
1142
|
+
if (activity) {
|
|
1143
|
+
activity.type = 'router';
|
|
1144
|
+
activity.provider = provider;
|
|
1145
|
+
activity.project = projectLabel(state);
|
|
1146
|
+
activity.status = 'running';
|
|
1147
|
+
activity.phase = t(activity.lang, 'control.activity.interpreting');
|
|
1148
|
+
pushActivityEvent(activity, `🧠 ${t(activity.lang, 'control.activity.interpreting')}`);
|
|
1149
|
+
await editTelegramActivity({ bot, chatId, activity, force: true });
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
try {
|
|
1153
|
+
const projects = await listProjects().catch(() => []);
|
|
1154
|
+
const workflows = await listWorkflowSummaries(link.user_id);
|
|
1155
|
+
const response = await localApi(link.user_id, '/api/agent', {
|
|
1156
|
+
method: 'POST',
|
|
1157
|
+
timeoutMs: INTENT_ROUTER_TIMEOUT_MS,
|
|
1158
|
+
body: {
|
|
1159
|
+
projectPath: state.selectedProjectPath || process.cwd(),
|
|
1160
|
+
provider,
|
|
1161
|
+
model: model || undefined,
|
|
1162
|
+
message: buildTelegramIntentPrompt(text, state, { projects, workflows }),
|
|
1163
|
+
cleanup: false,
|
|
1164
|
+
stream: false,
|
|
1165
|
+
permissionMode: 'plan',
|
|
1166
|
+
suppressNotifications: true,
|
|
1167
|
+
},
|
|
1168
|
+
});
|
|
1169
|
+
const assistantText = extractAssistantText(response);
|
|
1170
|
+
const intent = parseTelegramAiIntentResponse(assistantText, text);
|
|
1171
|
+
if (activity) {
|
|
1172
|
+
pushActivityEvent(activity, `🧭 ${intent.action} (${Math.round(intent.confidence * 100)}%)`);
|
|
1173
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1174
|
+
}
|
|
1175
|
+
return intent;
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
if (activity) {
|
|
1178
|
+
pushActivityEvent(activity, `⚠️ ${t(activity.lang, 'control.activity.routerFallback')}`);
|
|
1179
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1180
|
+
}
|
|
1181
|
+
console.warn('[telegram-control] AI intent router fallback:', error?.message || error);
|
|
1182
|
+
return { action: 'agent_prompt', prompt: text, confidence: 0 };
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
async function handleRoutedIntent({ bot, chatId, link, text, activity }) {
|
|
1187
|
+
const intent = await resolveTelegramAiIntent({ bot, chatId, link, text, activity });
|
|
1188
|
+
const editMessageId = activity?.messageId;
|
|
1189
|
+
|
|
1190
|
+
if (intent.action === 'agent_prompt') {
|
|
1191
|
+
await runAgent({ bot, chatId, link, prompt: intent.prompt || text, activity });
|
|
760
1192
|
return true;
|
|
761
1193
|
}
|
|
762
|
-
if (intent.action === '
|
|
763
|
-
|
|
1194
|
+
if (intent.action === 'show_menu') {
|
|
1195
|
+
await showMainMenu({ bot, chatId, link, editMessageId });
|
|
1196
|
+
return true;
|
|
1197
|
+
}
|
|
1198
|
+
if (intent.action === 'show_projects') {
|
|
1199
|
+
await showProjectMenu({ bot, chatId, link, editMessageId });
|
|
1200
|
+
return true;
|
|
1201
|
+
}
|
|
1202
|
+
if (intent.action === 'select_project') {
|
|
1203
|
+
const query = intent.projectQuery || intent.prompt || text;
|
|
1204
|
+
const project = await findProjectByQuery(query);
|
|
764
1205
|
const lang = languageFor(link);
|
|
765
1206
|
if (!project) {
|
|
766
|
-
await
|
|
767
|
-
|
|
1207
|
+
await showProjectMenu({
|
|
1208
|
+
bot,
|
|
1209
|
+
chatId,
|
|
1210
|
+
link,
|
|
1211
|
+
editMessageId,
|
|
1212
|
+
notice: t(lang, 'control.projectNotFound', { query }),
|
|
1213
|
+
});
|
|
768
1214
|
return true;
|
|
769
1215
|
}
|
|
770
1216
|
updateTelegramControlState(link.user_id, {
|
|
@@ -775,6 +1221,7 @@ async function handleRoutedIntent({ bot, chatId, link, text }) {
|
|
|
775
1221
|
bot,
|
|
776
1222
|
chatId,
|
|
777
1223
|
link,
|
|
1224
|
+
editMessageId,
|
|
778
1225
|
notice: t(lang, 'control.projectSelected', {
|
|
779
1226
|
project: project.displayName || project.name,
|
|
780
1227
|
path: project.fullPath || project.path,
|
|
@@ -782,36 +1229,56 @@ async function handleRoutedIntent({ bot, chatId, link, text }) {
|
|
|
782
1229
|
});
|
|
783
1230
|
return true;
|
|
784
1231
|
}
|
|
785
|
-
if (intent.action === '
|
|
786
|
-
await showProviderMenu({ bot, chatId, link });
|
|
1232
|
+
if (intent.action === 'show_provider_menu') {
|
|
1233
|
+
await showProviderMenu({ bot, chatId, link, editMessageId });
|
|
787
1234
|
return true;
|
|
788
1235
|
}
|
|
789
|
-
if (intent.action === '
|
|
1236
|
+
if (intent.action === 'select_provider' && intent.provider) {
|
|
790
1237
|
updateTelegramControlState(link.user_id, { selectedProvider: intent.provider, selectedModel: null });
|
|
791
|
-
await showModelMenu({ bot, chatId, link });
|
|
1238
|
+
await showModelMenu({ bot, chatId, link, editMessageId });
|
|
792
1239
|
return true;
|
|
793
1240
|
}
|
|
794
|
-
if (intent.action === '
|
|
795
|
-
await showModelMenu({ bot, chatId, link });
|
|
1241
|
+
if (intent.action === 'show_model_menu') {
|
|
1242
|
+
await showModelMenu({ bot, chatId, link, editMessageId });
|
|
796
1243
|
return true;
|
|
797
1244
|
}
|
|
798
|
-
if (intent.action === '
|
|
799
|
-
|
|
1245
|
+
if (intent.action === 'select_model' && intent.model) {
|
|
1246
|
+
updateTelegramControlState(link.user_id, { selectedModel: intent.model });
|
|
1247
|
+
await showMainMenu({
|
|
1248
|
+
bot,
|
|
1249
|
+
chatId,
|
|
1250
|
+
link,
|
|
1251
|
+
editMessageId,
|
|
1252
|
+
notice: t(languageFor(link), 'control.modelSelected', { model: intent.model }),
|
|
1253
|
+
});
|
|
800
1254
|
return true;
|
|
801
1255
|
}
|
|
802
|
-
if (intent.action === '
|
|
803
|
-
await
|
|
1256
|
+
if (intent.action === 'show_runs') {
|
|
1257
|
+
await showRuns({ bot, chatId, link, editMessageId });
|
|
804
1258
|
return true;
|
|
805
1259
|
}
|
|
806
|
-
if (intent.action === '
|
|
807
|
-
await
|
|
1260
|
+
if (intent.action === 'show_approvals') {
|
|
1261
|
+
await showApprovalQueue({ bot, chatId, link, editMessageId });
|
|
1262
|
+
return true;
|
|
1263
|
+
}
|
|
1264
|
+
if (intent.action === 'show_workflows') {
|
|
1265
|
+
await showWorkflowMenu({ bot, chatId, link, editMessageId });
|
|
1266
|
+
return true;
|
|
1267
|
+
}
|
|
1268
|
+
if (intent.action === 'run_workflow') {
|
|
1269
|
+
await runWorkflow({ bot, chatId, link, input: intent.workflowInput || text, activity });
|
|
1270
|
+
return true;
|
|
1271
|
+
}
|
|
1272
|
+
if (intent.action === 'show_sessions') {
|
|
1273
|
+
await showSessions({ bot, chatId, link, editMessageId });
|
|
808
1274
|
return true;
|
|
809
1275
|
}
|
|
810
1276
|
if (intent.action === 'new_chat') {
|
|
811
|
-
await startNewChat({ bot, chatId, link });
|
|
1277
|
+
await startNewChat({ bot, chatId, link, editMessageId });
|
|
812
1278
|
return true;
|
|
813
1279
|
}
|
|
814
|
-
|
|
1280
|
+
await runAgent({ bot, chatId, link, prompt: text, activity });
|
|
1281
|
+
return true;
|
|
815
1282
|
}
|
|
816
1283
|
|
|
817
1284
|
async function fetchRun(userId, runId) {
|
|
@@ -819,10 +1286,17 @@ async function fetchRun(userId, runId) {
|
|
|
819
1286
|
}
|
|
820
1287
|
|
|
821
1288
|
function summarizeRun(run, mode) {
|
|
1289
|
+
const statusIcon = run.status === 'completed'
|
|
1290
|
+
? '✅'
|
|
1291
|
+
: run.status === 'failed'
|
|
1292
|
+
? '❌'
|
|
1293
|
+
: run.status === 'canceled'
|
|
1294
|
+
? '⏹'
|
|
1295
|
+
: '🔧';
|
|
822
1296
|
const lines = [
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1297
|
+
`${statusIcon} Run ${run.id}`,
|
|
1298
|
+
`🧩 Workflow: ${run.workflowId}`,
|
|
1299
|
+
`📌 Status: ${run.status}`,
|
|
826
1300
|
];
|
|
827
1301
|
const nodeRuns = Array.isArray(run.nodeRuns) ? run.nodeRuns : [];
|
|
828
1302
|
if (mode !== 'final') {
|
|
@@ -830,7 +1304,14 @@ function summarizeRun(run, mode) {
|
|
|
830
1304
|
? nodeRuns.filter((node) => node.error || node.status === 'failed')
|
|
831
1305
|
: nodeRuns;
|
|
832
1306
|
for (const node of visibleNodes) {
|
|
833
|
-
|
|
1307
|
+
const nodeIcon = node.status === 'completed'
|
|
1308
|
+
? '✅'
|
|
1309
|
+
: node.status === 'failed'
|
|
1310
|
+
? '❌'
|
|
1311
|
+
: node.status === 'running'
|
|
1312
|
+
? '🔧'
|
|
1313
|
+
: '⏳';
|
|
1314
|
+
lines.push(`${nodeIcon} ${node.status}: ${node.agentLabel || node.nodeId}${node.error ? ` - ${node.error}` : ''}`);
|
|
834
1315
|
}
|
|
835
1316
|
}
|
|
836
1317
|
const outputs = nodeRuns
|
|
@@ -840,7 +1321,7 @@ function summarizeRun(run, mode) {
|
|
|
840
1321
|
return truncate(`${lines.join('\n')}${outputs.join('\n')}`);
|
|
841
1322
|
}
|
|
842
1323
|
|
|
843
|
-
async function monitorWorkflowRun({ bot, chatId, link, runId }) {
|
|
1324
|
+
async function monitorWorkflowRun({ bot, chatId, link, runId, activity = null }) {
|
|
844
1325
|
if (runMonitors.has(runId)) return;
|
|
845
1326
|
runMonitors.set(runId, true);
|
|
846
1327
|
const state = getState(link.user_id);
|
|
@@ -850,12 +1331,23 @@ async function monitorWorkflowRun({ bot, chatId, link, runId }) {
|
|
|
850
1331
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
851
1332
|
const run = await fetchRun(link.user_id, runId);
|
|
852
1333
|
const nodeRuns = Array.isArray(run.nodeRuns) ? run.nodeRuns : [];
|
|
1334
|
+
if (activity && !TERMINAL_RUN_STATES.has(run.status)) {
|
|
1335
|
+
activity.status = 'running';
|
|
1336
|
+
activity.phase = `${t(activity.lang, 'control.activity.workflowRunning')} (${run.status})`;
|
|
1337
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1338
|
+
}
|
|
853
1339
|
if (state.progressMode === 'all') {
|
|
854
1340
|
for (const node of nodeRuns) {
|
|
855
1341
|
const key = `${node.nodeId}:${node.status}`;
|
|
856
1342
|
if (!seenNodeStatus.has(key)) {
|
|
857
1343
|
seenNodeStatus.set(key, true);
|
|
858
|
-
|
|
1344
|
+
if (activity) {
|
|
1345
|
+
pushActivityEvent(activity, `${node.status === 'completed' ? '✅' : node.status === 'failed' ? '❌' : '🔧'} ${node.agentLabel || node.nodeId}: ${node.status}${node.error ? ` - ${node.error}` : ''}`);
|
|
1346
|
+
activity.phase = t(activity.lang, 'control.activity.workflowRunning');
|
|
1347
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1348
|
+
} else {
|
|
1349
|
+
await send(bot, chatId, `${node.agentLabel || node.nodeId}: ${node.status}${node.error ? `\n${node.error}` : ''}`);
|
|
1350
|
+
}
|
|
859
1351
|
}
|
|
860
1352
|
}
|
|
861
1353
|
}
|
|
@@ -864,12 +1356,27 @@ async function monitorWorkflowRun({ bot, chatId, link, runId }) {
|
|
|
864
1356
|
const key = `${node.nodeId}:${node.status}:${node.error || ''}`;
|
|
865
1357
|
if (!seenNodeStatus.has(key)) {
|
|
866
1358
|
seenNodeStatus.set(key, true);
|
|
867
|
-
|
|
1359
|
+
if (activity) {
|
|
1360
|
+
pushActivityEvent(activity, `❌ ${node.agentLabel || node.nodeId}: ${node.status}${node.error ? ` - ${node.error}` : ''}`);
|
|
1361
|
+
activity.phase = t(activity.lang, 'control.activity.workflowRunning');
|
|
1362
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1363
|
+
} else {
|
|
1364
|
+
await send(bot, chatId, `${node.agentLabel || node.nodeId}: ${node.status}\n${node.error || ''}`);
|
|
1365
|
+
}
|
|
868
1366
|
}
|
|
869
1367
|
}
|
|
870
1368
|
}
|
|
871
1369
|
if (TERMINAL_RUN_STATES.has(run.status)) {
|
|
872
|
-
|
|
1370
|
+
if (activity) {
|
|
1371
|
+
activity.status = run.status === 'completed' ? 'done' : 'failed';
|
|
1372
|
+
activity.phase = run.status === 'completed'
|
|
1373
|
+
? t(activity.lang, 'control.activity.done')
|
|
1374
|
+
: t(activity.lang, 'control.activity.failed');
|
|
1375
|
+
activity.output = summarizeRun(run, state.progressMode);
|
|
1376
|
+
await editTelegramActivity({ bot, chatId, activity, force: true });
|
|
1377
|
+
} else {
|
|
1378
|
+
await send(bot, chatId, summarizeRun(run, state.progressMode));
|
|
1379
|
+
}
|
|
873
1380
|
return;
|
|
874
1381
|
}
|
|
875
1382
|
}
|
|
@@ -1124,7 +1631,6 @@ async function handleTelegramControlMessageInternal({ bot, msg, link }) {
|
|
|
1124
1631
|
|
|
1125
1632
|
if (await handleAwaitingInput({ bot, chatId, link, text })) return true;
|
|
1126
1633
|
if (await handleCommand({ bot, chatId, link, text })) return true;
|
|
1127
|
-
if (await handleRoutedIntent({ bot, chatId, link, text })) return true;
|
|
1128
1634
|
|
|
1129
1635
|
const state = getState(link.user_id);
|
|
1130
1636
|
if (state.routerEnabled === false) return false;
|
|
@@ -1133,8 +1639,15 @@ async function handleTelegramControlMessageInternal({ bot, msg, link }) {
|
|
|
1133
1639
|
return true;
|
|
1134
1640
|
}
|
|
1135
1641
|
|
|
1136
|
-
await
|
|
1137
|
-
|
|
1642
|
+
const activity = await createTelegramActivity({
|
|
1643
|
+
bot,
|
|
1644
|
+
chatId,
|
|
1645
|
+
link,
|
|
1646
|
+
type: 'router',
|
|
1647
|
+
prompt: text,
|
|
1648
|
+
phase: t(languageFor(link), 'control.activity.interpreting'),
|
|
1649
|
+
});
|
|
1650
|
+
return handleRoutedIntent({ bot, chatId, link, text, activity });
|
|
1138
1651
|
}
|
|
1139
1652
|
|
|
1140
1653
|
export async function handleTelegramControlMessage(args) {
|