@pixelbyte-software/pixcode 1.53.5 → 1.53.6
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-DP7jnMyc.js} +3 -3
- package/dist/index.html +1 -1
- package/dist-server/server/openai-codex.js +5 -0
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/routes/agent.js +17 -7
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/services/telegram/control-center.js +575 -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 +54 -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/openai-codex.js +5 -0
- package/server/routes/agent.js +17 -7
- package/server/services/telegram/control-center.js +565 -80
- package/server/services/telegram/telegram-gateway.js +115 -51
- package/server/services/telegram/translations.js +54 -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,197 @@ 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 renderActivity(activity, { finalText = null } = {}) {
|
|
382
|
+
const output = finalText || activity.output;
|
|
383
|
+
const lines = [
|
|
384
|
+
`${activity.status === 'failed' ? '❌' : activity.status === 'done' ? '✅' : activity.status === 'running' ? '🔧' : '⏳'} ${activityTitle(activity)}`,
|
|
385
|
+
'',
|
|
386
|
+
`🤖 ${t(activity.lang, 'control.activity.provider')}: ${activity.provider || '-'}`,
|
|
387
|
+
`📁 ${t(activity.lang, 'control.activity.project')}: ${compact(activity.project, 90)}`,
|
|
388
|
+
];
|
|
389
|
+
|
|
390
|
+
if (activity.sessionId) lines.push(`🧵 ${t(activity.lang, 'control.activity.session')}: ${activity.sessionId}`);
|
|
391
|
+
if (activity.runId) lines.push(`🧭 ${t(activity.lang, 'control.activity.run')}: ${activity.runId}`);
|
|
392
|
+
if (activity.workflowId) lines.push(`🧩 ${t(activity.lang, 'control.activity.workflow')}: ${activity.workflowId}`);
|
|
393
|
+
lines.push(`⏱ ${t(activity.lang, 'control.activity.elapsed')}: ${formatElapsed(activity.startedAt)}`);
|
|
394
|
+
lines.push(`📌 ${t(activity.lang, 'control.activity.status')}: ${activity.phase}`);
|
|
395
|
+
|
|
396
|
+
const visibleEvents = activity.mode === 'final'
|
|
397
|
+
? activity.events.slice(-4)
|
|
398
|
+
: activity.events;
|
|
399
|
+
if (visibleEvents.length > 0) {
|
|
400
|
+
lines.push('', `🛠 ${t(activity.lang, 'control.activity.work')}:`);
|
|
401
|
+
for (const event of visibleEvents) lines.push(`• ${event}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (activity.error) {
|
|
405
|
+
lines.push('', `⚠️ ${truncate(activity.error, 700)}`);
|
|
406
|
+
} else if (output) {
|
|
407
|
+
lines.push('', `💬 ${t(activity.lang, 'control.activity.output')}:`);
|
|
408
|
+
lines.push(truncate(output, 1800));
|
|
409
|
+
} else if (activity.prompt) {
|
|
410
|
+
lines.push('', `📝 ${compact(activity.prompt, 220)}`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return truncate(lines.join('\n'), 3400);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async function createTelegramActivity({ bot, chatId, link, type = 'agent', prompt = '', phase = null }) {
|
|
417
|
+
const lang = languageFor(link);
|
|
418
|
+
const state = getState(link.user_id);
|
|
419
|
+
const activity = createActivityState({
|
|
420
|
+
lang,
|
|
421
|
+
type,
|
|
422
|
+
provider: resolveTelegramProvider(state),
|
|
423
|
+
project: projectLabel(state),
|
|
424
|
+
prompt,
|
|
425
|
+
mode: state.progressMode,
|
|
426
|
+
});
|
|
427
|
+
if (phase) activity.phase = phase;
|
|
428
|
+
const sent = await send(bot, chatId, renderActivity(activity), { parse_mode: undefined });
|
|
429
|
+
activity.messageId = sent?.message_id || sent?.message?.message_id || null;
|
|
430
|
+
activity.lastEditAt = Date.now();
|
|
431
|
+
return activity;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async function editTelegramActivity({ bot, chatId, activity, force = false, reply_markup }) {
|
|
435
|
+
if (!activity) return null;
|
|
436
|
+
const now = Date.now();
|
|
437
|
+
if (!force && now - activity.lastEditAt < ACTIVITY_EDIT_THROTTLE_MS) return null;
|
|
438
|
+
const sent = await send(bot, chatId, renderActivity(activity), {
|
|
439
|
+
editMessageId: activity.messageId,
|
|
440
|
+
parse_mode: undefined,
|
|
441
|
+
reply_markup,
|
|
442
|
+
});
|
|
443
|
+
activity.messageId = sent?.message_id || sent?.message?.message_id || activity.messageId;
|
|
444
|
+
activity.lastEditAt = now;
|
|
445
|
+
return sent;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function startActivityHeartbeat({ bot, chatId, activity }) {
|
|
449
|
+
return setInterval(() => {
|
|
450
|
+
if (!activity || activity.status === 'done' || activity.status === 'failed') return;
|
|
451
|
+
if (activity.status === 'starting') {
|
|
452
|
+
activity.status = 'running';
|
|
453
|
+
activity.phase = t(activity.lang, 'control.activity.thinking');
|
|
454
|
+
}
|
|
455
|
+
editTelegramActivity({ bot, chatId, activity }).catch((error) => {
|
|
456
|
+
console.warn('[telegram-control] activity heartbeat failed:', error?.message || error);
|
|
457
|
+
});
|
|
458
|
+
}, ACTIVITY_HEARTBEAT_MS);
|
|
459
|
+
}
|
|
460
|
+
|
|
263
461
|
function stateSummary(lang, state) {
|
|
264
462
|
return [
|
|
265
463
|
`${t(lang, 'control.summary.project')}: ${state.selectedProjectName || t(lang, 'control.notSelected')}`,
|
|
@@ -310,11 +508,12 @@ async function listProjects() {
|
|
|
310
508
|
return projects.slice(0, 20);
|
|
311
509
|
}
|
|
312
510
|
|
|
313
|
-
async function showProjectMenu({ bot, chatId, link, editMessageId }) {
|
|
511
|
+
async function showProjectMenu({ bot, chatId, link, editMessageId, notice }) {
|
|
314
512
|
const lang = languageFor(link);
|
|
315
513
|
const projects = await listProjects();
|
|
316
514
|
if (projects.length === 0) {
|
|
317
|
-
|
|
515
|
+
const prefix = notice ? `${notice}\n\n` : '';
|
|
516
|
+
await send(bot, chatId, `${prefix}${t(lang, 'control.noProjects')}`, { editMessageId });
|
|
318
517
|
return;
|
|
319
518
|
}
|
|
320
519
|
|
|
@@ -327,7 +526,8 @@ async function showProjectMenu({ bot, chatId, link, editMessageId }) {
|
|
|
327
526
|
displayName: project.displayName || project.name,
|
|
328
527
|
},
|
|
329
528
|
));
|
|
330
|
-
|
|
529
|
+
const prefix = notice ? `${notice}\n\n` : '';
|
|
530
|
+
await send(bot, chatId, `${prefix}${t(lang, 'control.pickProject')}`, {
|
|
331
531
|
editMessageId,
|
|
332
532
|
reply_markup: { inline_keyboard: rows(buttons, 1) },
|
|
333
533
|
});
|
|
@@ -539,6 +739,119 @@ function extractAssistantText(response) {
|
|
|
539
739
|
return chunks.join('\n\n').trim();
|
|
540
740
|
}
|
|
541
741
|
|
|
742
|
+
function extractTextFromEvent(event) {
|
|
743
|
+
if (!event || typeof event !== 'object') return '';
|
|
744
|
+
if (typeof event.content === 'string') return event.content;
|
|
745
|
+
if (Array.isArray(event.content)) {
|
|
746
|
+
return event.content
|
|
747
|
+
.map((part) => (typeof part === 'string' ? part : part?.text || ''))
|
|
748
|
+
.join('');
|
|
749
|
+
}
|
|
750
|
+
const legacyContent = event.data?.message?.content || event.message?.content;
|
|
751
|
+
if (Array.isArray(legacyContent)) {
|
|
752
|
+
return legacyContent
|
|
753
|
+
.map((part) => (typeof part === 'string' ? part : part?.text || ''))
|
|
754
|
+
.join('');
|
|
755
|
+
}
|
|
756
|
+
if (typeof legacyContent === 'string') return legacyContent;
|
|
757
|
+
return '';
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function describeToolInput(toolName, input) {
|
|
761
|
+
if (!input || typeof input !== 'object') return '';
|
|
762
|
+
if (toolName === 'Bash' && typeof input.command === 'string') {
|
|
763
|
+
return compact(input.command, 110);
|
|
764
|
+
}
|
|
765
|
+
if (toolName === 'WebSearch' && typeof input.query === 'string') {
|
|
766
|
+
return compact(input.query, 110);
|
|
767
|
+
}
|
|
768
|
+
if (toolName === 'FileChanges') {
|
|
769
|
+
return compact(JSON.stringify(input), 110);
|
|
770
|
+
}
|
|
771
|
+
const keys = Object.keys(input).slice(0, 3);
|
|
772
|
+
if (keys.length === 0) return '';
|
|
773
|
+
return compact(keys.map((key) => `${key}: ${String(input[key])}`).join(', '), 110);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function applyAgentStreamEvent(activity, event) {
|
|
777
|
+
if (!event || typeof event !== 'object') return;
|
|
778
|
+
|
|
779
|
+
const sessionId = event.actualSessionId || event.newSessionId || event.sessionId || event.threadId;
|
|
780
|
+
if (sessionId) activity.sessionId = sessionId;
|
|
781
|
+
|
|
782
|
+
if (event.type === 'status' && event.message) {
|
|
783
|
+
activity.status = 'running';
|
|
784
|
+
activity.phase = compact(event.message, 120);
|
|
785
|
+
pushActivityEvent(activity, `⏳ ${compact(event.message, 120)}`);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (event.type === 'done' || event.kind === 'complete' || event.kind === 'stream_end') {
|
|
789
|
+
activity.status = 'done';
|
|
790
|
+
activity.phase = t(activity.lang, 'control.activity.done');
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if (event.type === 'error' || event.kind === 'error') {
|
|
794
|
+
activity.status = 'failed';
|
|
795
|
+
activity.phase = t(activity.lang, 'control.activity.failed');
|
|
796
|
+
activity.error = event.error || event.message || event.content || t(activity.lang, 'error.generic');
|
|
797
|
+
pushActivityEvent(activity, `❌ ${compact(activity.error, 120)}`);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (event.kind === 'session_created') {
|
|
801
|
+
activity.status = 'running';
|
|
802
|
+
activity.phase = t(activity.lang, 'control.activity.sessionStarted');
|
|
803
|
+
pushActivityEvent(activity, `🧵 ${t(activity.lang, 'control.activity.sessionStarted')}`);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (event.kind === 'thinking') {
|
|
807
|
+
activity.status = 'running';
|
|
808
|
+
activity.phase = t(activity.lang, 'control.activity.thinking');
|
|
809
|
+
const thought = extractTextFromEvent(event);
|
|
810
|
+
if (thought) pushActivityEvent(activity, `🧠 ${compact(thought, 120)}`);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
if (event.kind === 'stream_delta' || event.kind === 'text') {
|
|
814
|
+
activity.status = 'running';
|
|
815
|
+
activity.phase = t(activity.lang, 'control.activity.responding');
|
|
816
|
+
activity.output = `${activity.output}${extractTextFromEvent(event)}`;
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (event.type === 'claude-response' && event.data?.type === 'assistant') {
|
|
820
|
+
activity.status = 'running';
|
|
821
|
+
activity.phase = t(activity.lang, 'control.activity.responding');
|
|
822
|
+
activity.output = `${activity.output}${extractTextFromEvent(event)}`;
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
if (event.kind === 'tool_use') {
|
|
826
|
+
activity.status = 'running';
|
|
827
|
+
activity.phase = t(activity.lang, 'control.activity.working');
|
|
828
|
+
const toolName = event.toolName || 'Tool';
|
|
829
|
+
const input = describeToolInput(toolName, event.toolInput);
|
|
830
|
+
const icon = toolName === 'Bash'
|
|
831
|
+
? '💻'
|
|
832
|
+
: toolName === 'FileChanges'
|
|
833
|
+
? '📝'
|
|
834
|
+
: toolName === 'WebSearch'
|
|
835
|
+
? '🔎'
|
|
836
|
+
: '🔧';
|
|
837
|
+
pushActivityEvent(activity, `${icon} ${toolName}${input ? `: ${input}` : ''}`);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (event.kind === 'tool_result') {
|
|
841
|
+
activity.status = 'running';
|
|
842
|
+
activity.phase = t(activity.lang, 'control.activity.working');
|
|
843
|
+
const label = event.isError
|
|
844
|
+
? t(activity.lang, 'control.activity.toolFailed')
|
|
845
|
+
: t(activity.lang, 'control.activity.toolDone');
|
|
846
|
+
pushActivityEvent(activity, `${event.isError ? '⚠️' : '✅'} ${label}`);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (event.kind === 'status' && event.text === 'token_budget') {
|
|
850
|
+
const used = event.tokenBudget?.used;
|
|
851
|
+
if (used) pushActivityEvent(activity, `📊 ${t(activity.lang, 'control.activity.tokens')}: ${used}`);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
542
855
|
function confirmationLabel(lang, action, payload = {}) {
|
|
543
856
|
if (action === 'install_provider') {
|
|
544
857
|
return t(lang, 'control.confirmInstall', { provider: payload.provider || '' });
|
|
@@ -572,78 +885,105 @@ async function sendToolFailure({ bot, chatId, link, result, editMessageId }) {
|
|
|
572
885
|
await send(bot, chatId, message, { editMessageId });
|
|
573
886
|
}
|
|
574
887
|
|
|
575
|
-
async function runAgent({ bot, chatId, link, prompt }) {
|
|
888
|
+
async function runAgent({ bot, chatId, link, prompt, activity = null }) {
|
|
576
889
|
const lang = languageFor(link);
|
|
577
890
|
const state = getState(link.user_id);
|
|
578
891
|
if (!state.remoteControlEnabled) {
|
|
579
|
-
await send(bot, chatId, t(lang, 'control.disabled'));
|
|
892
|
+
await send(bot, chatId, t(lang, 'control.disabled'), { editMessageId: activity?.messageId });
|
|
580
893
|
return;
|
|
581
894
|
}
|
|
582
895
|
if (!state.selectedProjectPath) {
|
|
583
|
-
await
|
|
584
|
-
await showProjectMenu({ bot, chatId, link });
|
|
896
|
+
await showProjectMenu({ bot, chatId, link, editMessageId: activity?.messageId, notice: t(lang, 'control.selectProjectFirst') });
|
|
585
897
|
return;
|
|
586
898
|
}
|
|
587
899
|
|
|
588
900
|
const provider = resolveTelegramProvider(state);
|
|
589
901
|
const model = resolveTelegramModel(state);
|
|
590
|
-
|
|
591
|
-
|
|
902
|
+
const active = activity || await createTelegramActivity({
|
|
903
|
+
bot,
|
|
904
|
+
chatId,
|
|
905
|
+
link,
|
|
906
|
+
type: 'agent',
|
|
907
|
+
prompt,
|
|
908
|
+
phase: t(lang, 'control.activity.startingProvider', { provider }),
|
|
909
|
+
});
|
|
910
|
+
active.type = 'agent';
|
|
911
|
+
active.provider = provider;
|
|
912
|
+
active.project = projectLabel(state);
|
|
913
|
+
active.status = 'running';
|
|
914
|
+
active.phase = t(lang, 'control.activity.startingProvider', { provider });
|
|
915
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
916
|
+
|
|
917
|
+
const heartbeat = startActivityHeartbeat({ bot, chatId, activity: active });
|
|
918
|
+
let streamFailed = null;
|
|
919
|
+
try {
|
|
920
|
+
await localAgentStream(link.user_id, {
|
|
921
|
+
projectPath: state.selectedProjectPath,
|
|
592
922
|
provider,
|
|
593
|
-
|
|
594
|
-
|
|
923
|
+
model: model || undefined,
|
|
924
|
+
message: buildTelegramAgentPrompt(prompt, state),
|
|
925
|
+
cleanup: false,
|
|
926
|
+
permissionMode: 'default',
|
|
927
|
+
}, async (event) => {
|
|
928
|
+
applyAgentStreamEvent(active, event);
|
|
929
|
+
await editTelegramActivity({ bot, chatId, activity: active });
|
|
930
|
+
});
|
|
931
|
+
} catch (error) {
|
|
932
|
+
streamFailed = error;
|
|
933
|
+
} finally {
|
|
934
|
+
clearInterval(heartbeat);
|
|
595
935
|
}
|
|
596
936
|
|
|
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 });
|
|
937
|
+
if (streamFailed) {
|
|
938
|
+
active.status = 'failed';
|
|
939
|
+
active.phase = t(lang, 'control.activity.failed');
|
|
940
|
+
active.error = streamFailed.message || t(lang, 'error.generic');
|
|
941
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
615
942
|
return;
|
|
616
943
|
}
|
|
617
944
|
|
|
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')}`);
|
|
945
|
+
active.status = active.error ? 'failed' : 'done';
|
|
946
|
+
active.phase = active.error ? t(lang, 'control.activity.failed') : t(lang, 'control.activity.done');
|
|
947
|
+
if (!active.output && !active.error) active.output = t(lang, 'control.noAssistantText');
|
|
948
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
624
949
|
}
|
|
625
950
|
|
|
626
|
-
export async function runWorkflow({ bot, chatId, link, input }) {
|
|
951
|
+
export async function runWorkflow({ bot, chatId, link, input, activity = null }) {
|
|
627
952
|
const lang = languageFor(link);
|
|
628
953
|
const state = getState(link.user_id);
|
|
629
954
|
const workflowId = state.selectedWorkflowId;
|
|
630
955
|
if (!state.remoteControlEnabled) {
|
|
631
|
-
await send(bot, chatId, t(lang, 'control.disabled'));
|
|
956
|
+
await send(bot, chatId, t(lang, 'control.disabled'), { editMessageId: activity?.messageId });
|
|
632
957
|
return;
|
|
633
958
|
}
|
|
634
959
|
if (!workflowId) {
|
|
635
|
-
await send(bot, chatId, t(lang, 'control.selectWorkflowFirst'));
|
|
636
|
-
await showWorkflowMenu({ bot, chatId, link });
|
|
960
|
+
await send(bot, chatId, t(lang, 'control.selectWorkflowFirst'), { editMessageId: activity?.messageId });
|
|
961
|
+
await showWorkflowMenu({ bot, chatId, link, editMessageId: activity?.messageId });
|
|
637
962
|
return;
|
|
638
963
|
}
|
|
639
964
|
if (!state.selectedProjectPath) {
|
|
640
|
-
await
|
|
641
|
-
await showProjectMenu({ bot, chatId, link });
|
|
965
|
+
await showProjectMenu({ bot, chatId, link, editMessageId: activity?.messageId, notice: t(lang, 'control.selectProjectFirst') });
|
|
642
966
|
return;
|
|
643
967
|
}
|
|
644
968
|
|
|
645
969
|
const provider = resolveTelegramProvider(state);
|
|
646
970
|
const model = resolveTelegramModel(state);
|
|
971
|
+
const active = activity || await createTelegramActivity({
|
|
972
|
+
bot,
|
|
973
|
+
chatId,
|
|
974
|
+
link,
|
|
975
|
+
type: 'workflow',
|
|
976
|
+
prompt: input,
|
|
977
|
+
phase: t(lang, 'control.activity.startingWorkflow'),
|
|
978
|
+
});
|
|
979
|
+
active.type = 'workflow';
|
|
980
|
+
active.provider = provider;
|
|
981
|
+
active.project = projectLabel(state);
|
|
982
|
+
active.workflowId = workflowId;
|
|
983
|
+
active.status = 'running';
|
|
984
|
+
active.phase = t(lang, 'control.activity.startingWorkflow');
|
|
985
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
986
|
+
|
|
647
987
|
const result = await runTelegramTool({
|
|
648
988
|
userId: link.user_id,
|
|
649
989
|
action: 'run_workflow',
|
|
@@ -664,12 +1004,15 @@ export async function runWorkflow({ bot, chatId, link, input }) {
|
|
|
664
1004
|
}),
|
|
665
1005
|
});
|
|
666
1006
|
if (!result.ok) {
|
|
667
|
-
await sendToolFailure({ bot, chatId, link, result });
|
|
1007
|
+
await sendToolFailure({ bot, chatId, link, result, editMessageId: active.messageId });
|
|
668
1008
|
return;
|
|
669
1009
|
}
|
|
670
1010
|
const run = result.data;
|
|
671
|
-
|
|
672
|
-
|
|
1011
|
+
active.runId = run.id;
|
|
1012
|
+
active.phase = t(lang, 'control.activity.workflowRunning');
|
|
1013
|
+
pushActivityEvent(active, `🧭 ${t(lang, 'control.workflowStarted', { runId: run.id, workflowId }).replace('\n', ' ')}`);
|
|
1014
|
+
await editTelegramActivity({ bot, chatId, activity: active, force: true });
|
|
1015
|
+
monitorWorkflowRun({ bot, chatId, link, runId: run.id, activity: active }).catch((error) => {
|
|
673
1016
|
console.warn('[telegram-control] workflow monitor failed:', error?.message || error);
|
|
674
1017
|
});
|
|
675
1018
|
}
|
|
@@ -750,21 +1093,96 @@ async function findProjectByQuery(query) {
|
|
|
750
1093
|
}) || null;
|
|
751
1094
|
}
|
|
752
1095
|
|
|
753
|
-
async function
|
|
1096
|
+
async function listWorkflowSummaries(userId) {
|
|
1097
|
+
try {
|
|
1098
|
+
const data = await localApi(userId, '/api/orchestration/workflows', { timeoutMs: 10_000 });
|
|
1099
|
+
if (Array.isArray(data)) return data;
|
|
1100
|
+
if (Array.isArray(data?.workflows)) return data.workflows;
|
|
1101
|
+
} catch {
|
|
1102
|
+
// Workflow context is helpful for routing but should never block chat input.
|
|
1103
|
+
}
|
|
1104
|
+
return [];
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
async function resolveTelegramAiIntent({ bot, chatId, link, text, activity }) {
|
|
754
1108
|
const state = getState(link.user_id);
|
|
755
|
-
|
|
756
|
-
|
|
1109
|
+
if (state.routerEnabled === false || state.routerMode !== 'hybrid') {
|
|
1110
|
+
return { action: 'agent_prompt', prompt: text, confidence: 1 };
|
|
1111
|
+
}
|
|
757
1112
|
|
|
758
|
-
|
|
759
|
-
|
|
1113
|
+
const provider = resolveTelegramProvider(state);
|
|
1114
|
+
const model = resolveTelegramModel(state);
|
|
1115
|
+
if (activity) {
|
|
1116
|
+
activity.type = 'router';
|
|
1117
|
+
activity.provider = provider;
|
|
1118
|
+
activity.project = projectLabel(state);
|
|
1119
|
+
activity.status = 'running';
|
|
1120
|
+
activity.phase = t(activity.lang, 'control.activity.interpreting');
|
|
1121
|
+
pushActivityEvent(activity, `🧠 ${t(activity.lang, 'control.activity.interpreting')}`);
|
|
1122
|
+
await editTelegramActivity({ bot, chatId, activity, force: true });
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
try {
|
|
1126
|
+
const projects = await listProjects().catch(() => []);
|
|
1127
|
+
const workflows = await listWorkflowSummaries(link.user_id);
|
|
1128
|
+
const response = await localApi(link.user_id, '/api/agent', {
|
|
1129
|
+
method: 'POST',
|
|
1130
|
+
timeoutMs: INTENT_ROUTER_TIMEOUT_MS,
|
|
1131
|
+
body: {
|
|
1132
|
+
projectPath: state.selectedProjectPath || process.cwd(),
|
|
1133
|
+
provider,
|
|
1134
|
+
model: model || undefined,
|
|
1135
|
+
message: buildTelegramIntentPrompt(text, state, { projects, workflows }),
|
|
1136
|
+
cleanup: false,
|
|
1137
|
+
stream: false,
|
|
1138
|
+
permissionMode: 'plan',
|
|
1139
|
+
},
|
|
1140
|
+
});
|
|
1141
|
+
const assistantText = extractAssistantText(response);
|
|
1142
|
+
const intent = parseTelegramAiIntentResponse(assistantText, text);
|
|
1143
|
+
if (activity) {
|
|
1144
|
+
pushActivityEvent(activity, `🧭 ${intent.action} (${Math.round(intent.confidence * 100)}%)`);
|
|
1145
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1146
|
+
}
|
|
1147
|
+
return intent;
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
if (activity) {
|
|
1150
|
+
pushActivityEvent(activity, `⚠️ ${t(activity.lang, 'control.activity.routerFallback')}`);
|
|
1151
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1152
|
+
}
|
|
1153
|
+
console.warn('[telegram-control] AI intent router fallback:', error?.message || error);
|
|
1154
|
+
return { action: 'agent_prompt', prompt: text, confidence: 0 };
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
async function handleRoutedIntent({ bot, chatId, link, text, activity }) {
|
|
1159
|
+
const intent = await resolveTelegramAiIntent({ bot, chatId, link, text, activity });
|
|
1160
|
+
const editMessageId = activity?.messageId;
|
|
1161
|
+
|
|
1162
|
+
if (intent.action === 'agent_prompt') {
|
|
1163
|
+
await runAgent({ bot, chatId, link, prompt: intent.prompt || text, activity });
|
|
1164
|
+
return true;
|
|
1165
|
+
}
|
|
1166
|
+
if (intent.action === 'show_menu') {
|
|
1167
|
+
await showMainMenu({ bot, chatId, link, editMessageId });
|
|
1168
|
+
return true;
|
|
1169
|
+
}
|
|
1170
|
+
if (intent.action === 'show_projects') {
|
|
1171
|
+
await showProjectMenu({ bot, chatId, link, editMessageId });
|
|
760
1172
|
return true;
|
|
761
1173
|
}
|
|
762
|
-
if (intent.action === '
|
|
763
|
-
const
|
|
1174
|
+
if (intent.action === 'select_project') {
|
|
1175
|
+
const query = intent.projectQuery || intent.prompt || text;
|
|
1176
|
+
const project = await findProjectByQuery(query);
|
|
764
1177
|
const lang = languageFor(link);
|
|
765
1178
|
if (!project) {
|
|
766
|
-
await
|
|
767
|
-
|
|
1179
|
+
await showProjectMenu({
|
|
1180
|
+
bot,
|
|
1181
|
+
chatId,
|
|
1182
|
+
link,
|
|
1183
|
+
editMessageId,
|
|
1184
|
+
notice: t(lang, 'control.projectNotFound', { query }),
|
|
1185
|
+
});
|
|
768
1186
|
return true;
|
|
769
1187
|
}
|
|
770
1188
|
updateTelegramControlState(link.user_id, {
|
|
@@ -775,6 +1193,7 @@ async function handleRoutedIntent({ bot, chatId, link, text }) {
|
|
|
775
1193
|
bot,
|
|
776
1194
|
chatId,
|
|
777
1195
|
link,
|
|
1196
|
+
editMessageId,
|
|
778
1197
|
notice: t(lang, 'control.projectSelected', {
|
|
779
1198
|
project: project.displayName || project.name,
|
|
780
1199
|
path: project.fullPath || project.path,
|
|
@@ -782,36 +1201,56 @@ async function handleRoutedIntent({ bot, chatId, link, text }) {
|
|
|
782
1201
|
});
|
|
783
1202
|
return true;
|
|
784
1203
|
}
|
|
785
|
-
if (intent.action === '
|
|
786
|
-
await showProviderMenu({ bot, chatId, link });
|
|
1204
|
+
if (intent.action === 'show_provider_menu') {
|
|
1205
|
+
await showProviderMenu({ bot, chatId, link, editMessageId });
|
|
787
1206
|
return true;
|
|
788
1207
|
}
|
|
789
|
-
if (intent.action === '
|
|
1208
|
+
if (intent.action === 'select_provider' && intent.provider) {
|
|
790
1209
|
updateTelegramControlState(link.user_id, { selectedProvider: intent.provider, selectedModel: null });
|
|
791
|
-
await showModelMenu({ bot, chatId, link });
|
|
1210
|
+
await showModelMenu({ bot, chatId, link, editMessageId });
|
|
792
1211
|
return true;
|
|
793
1212
|
}
|
|
794
|
-
if (intent.action === '
|
|
795
|
-
await showModelMenu({ bot, chatId, link });
|
|
1213
|
+
if (intent.action === 'show_model_menu') {
|
|
1214
|
+
await showModelMenu({ bot, chatId, link, editMessageId });
|
|
796
1215
|
return true;
|
|
797
1216
|
}
|
|
798
|
-
if (intent.action === '
|
|
799
|
-
|
|
1217
|
+
if (intent.action === 'select_model' && intent.model) {
|
|
1218
|
+
updateTelegramControlState(link.user_id, { selectedModel: intent.model });
|
|
1219
|
+
await showMainMenu({
|
|
1220
|
+
bot,
|
|
1221
|
+
chatId,
|
|
1222
|
+
link,
|
|
1223
|
+
editMessageId,
|
|
1224
|
+
notice: t(languageFor(link), 'control.modelSelected', { model: intent.model }),
|
|
1225
|
+
});
|
|
800
1226
|
return true;
|
|
801
1227
|
}
|
|
802
|
-
if (intent.action === '
|
|
803
|
-
await
|
|
1228
|
+
if (intent.action === 'show_runs') {
|
|
1229
|
+
await showRuns({ bot, chatId, link, editMessageId });
|
|
804
1230
|
return true;
|
|
805
1231
|
}
|
|
806
|
-
if (intent.action === '
|
|
807
|
-
await
|
|
1232
|
+
if (intent.action === 'show_approvals') {
|
|
1233
|
+
await showApprovalQueue({ bot, chatId, link, editMessageId });
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
1236
|
+
if (intent.action === 'show_workflows') {
|
|
1237
|
+
await showWorkflowMenu({ bot, chatId, link, editMessageId });
|
|
1238
|
+
return true;
|
|
1239
|
+
}
|
|
1240
|
+
if (intent.action === 'run_workflow') {
|
|
1241
|
+
await runWorkflow({ bot, chatId, link, input: intent.workflowInput || text, activity });
|
|
1242
|
+
return true;
|
|
1243
|
+
}
|
|
1244
|
+
if (intent.action === 'show_sessions') {
|
|
1245
|
+
await showSessions({ bot, chatId, link, editMessageId });
|
|
808
1246
|
return true;
|
|
809
1247
|
}
|
|
810
1248
|
if (intent.action === 'new_chat') {
|
|
811
|
-
await startNewChat({ bot, chatId, link });
|
|
1249
|
+
await startNewChat({ bot, chatId, link, editMessageId });
|
|
812
1250
|
return true;
|
|
813
1251
|
}
|
|
814
|
-
|
|
1252
|
+
await runAgent({ bot, chatId, link, prompt: text, activity });
|
|
1253
|
+
return true;
|
|
815
1254
|
}
|
|
816
1255
|
|
|
817
1256
|
async function fetchRun(userId, runId) {
|
|
@@ -819,10 +1258,17 @@ async function fetchRun(userId, runId) {
|
|
|
819
1258
|
}
|
|
820
1259
|
|
|
821
1260
|
function summarizeRun(run, mode) {
|
|
1261
|
+
const statusIcon = run.status === 'completed'
|
|
1262
|
+
? '✅'
|
|
1263
|
+
: run.status === 'failed'
|
|
1264
|
+
? '❌'
|
|
1265
|
+
: run.status === 'canceled'
|
|
1266
|
+
? '⏹'
|
|
1267
|
+
: '🔧';
|
|
822
1268
|
const lines = [
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1269
|
+
`${statusIcon} Run ${run.id}`,
|
|
1270
|
+
`🧩 Workflow: ${run.workflowId}`,
|
|
1271
|
+
`📌 Status: ${run.status}`,
|
|
826
1272
|
];
|
|
827
1273
|
const nodeRuns = Array.isArray(run.nodeRuns) ? run.nodeRuns : [];
|
|
828
1274
|
if (mode !== 'final') {
|
|
@@ -830,7 +1276,14 @@ function summarizeRun(run, mode) {
|
|
|
830
1276
|
? nodeRuns.filter((node) => node.error || node.status === 'failed')
|
|
831
1277
|
: nodeRuns;
|
|
832
1278
|
for (const node of visibleNodes) {
|
|
833
|
-
|
|
1279
|
+
const nodeIcon = node.status === 'completed'
|
|
1280
|
+
? '✅'
|
|
1281
|
+
: node.status === 'failed'
|
|
1282
|
+
? '❌'
|
|
1283
|
+
: node.status === 'running'
|
|
1284
|
+
? '🔧'
|
|
1285
|
+
: '⏳';
|
|
1286
|
+
lines.push(`${nodeIcon} ${node.status}: ${node.agentLabel || node.nodeId}${node.error ? ` - ${node.error}` : ''}`);
|
|
834
1287
|
}
|
|
835
1288
|
}
|
|
836
1289
|
const outputs = nodeRuns
|
|
@@ -840,7 +1293,7 @@ function summarizeRun(run, mode) {
|
|
|
840
1293
|
return truncate(`${lines.join('\n')}${outputs.join('\n')}`);
|
|
841
1294
|
}
|
|
842
1295
|
|
|
843
|
-
async function monitorWorkflowRun({ bot, chatId, link, runId }) {
|
|
1296
|
+
async function monitorWorkflowRun({ bot, chatId, link, runId, activity = null }) {
|
|
844
1297
|
if (runMonitors.has(runId)) return;
|
|
845
1298
|
runMonitors.set(runId, true);
|
|
846
1299
|
const state = getState(link.user_id);
|
|
@@ -850,12 +1303,23 @@ async function monitorWorkflowRun({ bot, chatId, link, runId }) {
|
|
|
850
1303
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
851
1304
|
const run = await fetchRun(link.user_id, runId);
|
|
852
1305
|
const nodeRuns = Array.isArray(run.nodeRuns) ? run.nodeRuns : [];
|
|
1306
|
+
if (activity && !TERMINAL_RUN_STATES.has(run.status)) {
|
|
1307
|
+
activity.status = 'running';
|
|
1308
|
+
activity.phase = `${t(activity.lang, 'control.activity.workflowRunning')} (${run.status})`;
|
|
1309
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1310
|
+
}
|
|
853
1311
|
if (state.progressMode === 'all') {
|
|
854
1312
|
for (const node of nodeRuns) {
|
|
855
1313
|
const key = `${node.nodeId}:${node.status}`;
|
|
856
1314
|
if (!seenNodeStatus.has(key)) {
|
|
857
1315
|
seenNodeStatus.set(key, true);
|
|
858
|
-
|
|
1316
|
+
if (activity) {
|
|
1317
|
+
pushActivityEvent(activity, `${node.status === 'completed' ? '✅' : node.status === 'failed' ? '❌' : '🔧'} ${node.agentLabel || node.nodeId}: ${node.status}${node.error ? ` - ${node.error}` : ''}`);
|
|
1318
|
+
activity.phase = t(activity.lang, 'control.activity.workflowRunning');
|
|
1319
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1320
|
+
} else {
|
|
1321
|
+
await send(bot, chatId, `${node.agentLabel || node.nodeId}: ${node.status}${node.error ? `\n${node.error}` : ''}`);
|
|
1322
|
+
}
|
|
859
1323
|
}
|
|
860
1324
|
}
|
|
861
1325
|
}
|
|
@@ -864,12 +1328,27 @@ async function monitorWorkflowRun({ bot, chatId, link, runId }) {
|
|
|
864
1328
|
const key = `${node.nodeId}:${node.status}:${node.error || ''}`;
|
|
865
1329
|
if (!seenNodeStatus.has(key)) {
|
|
866
1330
|
seenNodeStatus.set(key, true);
|
|
867
|
-
|
|
1331
|
+
if (activity) {
|
|
1332
|
+
pushActivityEvent(activity, `❌ ${node.agentLabel || node.nodeId}: ${node.status}${node.error ? ` - ${node.error}` : ''}`);
|
|
1333
|
+
activity.phase = t(activity.lang, 'control.activity.workflowRunning');
|
|
1334
|
+
await editTelegramActivity({ bot, chatId, activity });
|
|
1335
|
+
} else {
|
|
1336
|
+
await send(bot, chatId, `${node.agentLabel || node.nodeId}: ${node.status}\n${node.error || ''}`);
|
|
1337
|
+
}
|
|
868
1338
|
}
|
|
869
1339
|
}
|
|
870
1340
|
}
|
|
871
1341
|
if (TERMINAL_RUN_STATES.has(run.status)) {
|
|
872
|
-
|
|
1342
|
+
if (activity) {
|
|
1343
|
+
activity.status = run.status === 'completed' ? 'done' : 'failed';
|
|
1344
|
+
activity.phase = run.status === 'completed'
|
|
1345
|
+
? t(activity.lang, 'control.activity.done')
|
|
1346
|
+
: t(activity.lang, 'control.activity.failed');
|
|
1347
|
+
activity.output = summarizeRun(run, state.progressMode);
|
|
1348
|
+
await editTelegramActivity({ bot, chatId, activity, force: true });
|
|
1349
|
+
} else {
|
|
1350
|
+
await send(bot, chatId, summarizeRun(run, state.progressMode));
|
|
1351
|
+
}
|
|
873
1352
|
return;
|
|
874
1353
|
}
|
|
875
1354
|
}
|
|
@@ -1124,7 +1603,6 @@ async function handleTelegramControlMessageInternal({ bot, msg, link }) {
|
|
|
1124
1603
|
|
|
1125
1604
|
if (await handleAwaitingInput({ bot, chatId, link, text })) return true;
|
|
1126
1605
|
if (await handleCommand({ bot, chatId, link, text })) return true;
|
|
1127
|
-
if (await handleRoutedIntent({ bot, chatId, link, text })) return true;
|
|
1128
1606
|
|
|
1129
1607
|
const state = getState(link.user_id);
|
|
1130
1608
|
if (state.routerEnabled === false) return false;
|
|
@@ -1133,8 +1611,15 @@ async function handleTelegramControlMessageInternal({ bot, msg, link }) {
|
|
|
1133
1611
|
return true;
|
|
1134
1612
|
}
|
|
1135
1613
|
|
|
1136
|
-
await
|
|
1137
|
-
|
|
1614
|
+
const activity = await createTelegramActivity({
|
|
1615
|
+
bot,
|
|
1616
|
+
chatId,
|
|
1617
|
+
link,
|
|
1618
|
+
type: 'router',
|
|
1619
|
+
prompt: text,
|
|
1620
|
+
phase: t(languageFor(link), 'control.activity.interpreting'),
|
|
1621
|
+
});
|
|
1622
|
+
return handleRoutedIntent({ bot, chatId, link, text, activity });
|
|
1138
1623
|
}
|
|
1139
1624
|
|
|
1140
1625
|
export async function handleTelegramControlMessage(args) {
|