@nordbyte/nordrelay 0.5.2 → 0.6.0
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/.env.example +63 -11
- package/README.md +90 -19
- package/dist/access-control.js +1 -0
- package/dist/activity-events.js +44 -0
- package/dist/audit-log.js +40 -2
- package/dist/bot-rendering.js +10 -7
- package/dist/bot.js +458 -5
- package/dist/channel-actions.js +7 -2
- package/dist/channel-adapter.js +34 -7
- package/dist/channel-command-service.js +156 -0
- package/dist/channel-turn-service.js +237 -0
- package/dist/config-metadata.js +78 -13
- package/dist/config.js +77 -7
- package/dist/context-key.js +77 -5
- package/dist/discord-artifacts.js +165 -0
- package/dist/discord-bot.js +2014 -0
- package/dist/discord-channel-runtime.js +133 -0
- package/dist/discord-command-surface.js +119 -0
- package/dist/discord-rate-limit.js +141 -0
- package/dist/index.js +16 -5
- package/dist/job-store.js +127 -0
- package/dist/metrics.js +41 -0
- package/dist/relay-external-activity-monitor.js +47 -6
- package/dist/relay-runtime.js +986 -281
- package/dist/runtime-cache.js +57 -0
- package/dist/session-locks.js +10 -7
- package/dist/support-bundle.js +1 -0
- package/dist/telegram-access-commands.js +15 -2
- package/dist/telegram-access-middleware.js +16 -3
- package/dist/telegram-agent-commands.js +25 -0
- package/dist/telegram-artifact-commands.js +46 -0
- package/dist/telegram-diagnostics-command.js +5 -50
- package/dist/telegram-general-commands.js +2 -6
- package/dist/telegram-operational-commands.js +14 -6
- package/dist/telegram-queue-commands.js +74 -4
- package/dist/telegram-support-command.js +7 -0
- package/dist/telegram-update-commands.js +27 -0
- package/dist/user-management.js +208 -0
- package/dist/web-api-contract.js +9 -0
- package/dist/web-dashboard-access-routes.js +74 -1
- package/dist/web-dashboard-artifact-routes.js +3 -3
- package/dist/web-dashboard-assets.js +2 -0
- package/dist/web-dashboard-pages.js +97 -13
- package/dist/web-dashboard-runtime-routes.js +53 -8
- package/dist/web-dashboard-session-routes.js +27 -20
- package/dist/web-dashboard-ui.js +1 -0
- package/dist/web-dashboard.js +148 -6
- package/dist/web-state.js +33 -2
- package/dist/webui-assets/dashboard.css +75 -1
- package/dist/webui-assets/dashboard.js +358 -47
- package/package.json +3 -1
- package/plugins/nordrelay/scripts/nordrelay.mjs +210 -17
package/dist/bot.js
CHANGED
|
@@ -11,6 +11,7 @@ import { AuditLogStore } from "./audit-log.js";
|
|
|
11
11
|
import { formatSessionLabel } from "./bot-ui.js";
|
|
12
12
|
import { BotPreferencesStore, isQuietNow, } from "./bot-preferences.js";
|
|
13
13
|
import { renderAgentUpdateJobAction } from "./channel-actions.js";
|
|
14
|
+
import { ChannelCommandService } from "./channel-command-service.js";
|
|
14
15
|
import { deliverChannelAction } from "./channel-runtime.js";
|
|
15
16
|
import { agentLabel, agentReasoningLabel, agentReasoningOptions, } from "./agent.js";
|
|
16
17
|
import { getExternalActivityForSession, getExternalSnapshotForSession, } from "./agent-activity.js";
|
|
@@ -45,6 +46,7 @@ import { registerTelegramSupportCommands } from "./telegram-support-command.js";
|
|
|
45
46
|
import { registerTelegramUpdateCommands } from "./telegram-update-commands.js";
|
|
46
47
|
import { appendWithCap, authHelpText, buildStreamingPreview, capabilitiesOf, filterSessions, formatAgentLaunchProfileLabel, formatAgentSettingScope, formatDurationSeconds, formatError, formatLocalDateTime, formatLockOwner, formatModelButtonLabel, formatRelativeTime, formatTelegramName, formatToolSummaryLine, formatTurnUsageLine, getWorkspaceShortName, idOf, isEmptyArtifactReport, isPromptEnvelopeLike, isQueuedPromptLike, labelOf, orderPinnedSessions, parseFastModeArgument, renderExternalMirrorEvent, renderExternalMirrorStatus, renderPromptFailure, renderTodoList, renderToolEndMessage, renderToolStartMessage, requiresTurnApproval, trimLine, } from "./bot-rendering.js";
|
|
47
48
|
import { UserStore } from "./user-management.js";
|
|
49
|
+
import { WebActivityStore } from "./web-state.js";
|
|
48
50
|
import { evaluateWorkspacePolicy, filterAllowedWorkspaces, renderWorkspacePolicyLine, } from "./workspace-policy.js";
|
|
49
51
|
export { formatToolSummaryLine, formatTurnUsageLine, summarizeToolName } from "./bot-rendering.js";
|
|
50
52
|
export { registerCommands } from "./telegram-command-menu.js";
|
|
@@ -54,6 +56,10 @@ const TOOL_OUTPUT_PREVIEW_LIMIT = 500;
|
|
|
54
56
|
const MAX_AUDIO_FILE_SIZE = 25 * 1024 * 1024;
|
|
55
57
|
const MEDIA_GROUP_FLUSH_MS = 1200;
|
|
56
58
|
const LAUNCH_PROFILES_COMMAND = "/launch_profiles";
|
|
59
|
+
const CLI_ACTIVITY_ACTOR = {
|
|
60
|
+
channel: "cli",
|
|
61
|
+
label: "CLI",
|
|
62
|
+
};
|
|
57
63
|
export function createBot(config, registry) {
|
|
58
64
|
configureRedaction(config.telegramRedactPatterns);
|
|
59
65
|
telegramRateLimiter.configure({
|
|
@@ -80,11 +86,17 @@ export function createBot(config, registry) {
|
|
|
80
86
|
const turnProgress = new Map();
|
|
81
87
|
const promptStore = new PromptStore(config.workspace, config.stateBackend);
|
|
82
88
|
const preferencesStore = new BotPreferencesStore(config.workspace, config.stateBackend);
|
|
89
|
+
const activityStore = new WebActivityStore(config.workspace, config.stateBackend, config.auditMaxEvents);
|
|
83
90
|
const auditLog = new AuditLogStore(config.workspace, config.stateBackend, config.auditMaxEvents);
|
|
84
91
|
const lockStore = new SessionLockStore(config.workspace, config.stateBackend);
|
|
85
92
|
const userStore = new UserStore();
|
|
86
93
|
const contextUsers = new WeakMap();
|
|
87
|
-
const
|
|
94
|
+
const agentUpdateActors = new Map();
|
|
95
|
+
const agentUpdateStates = new Map();
|
|
96
|
+
const commandService = new ChannelCommandService(config);
|
|
97
|
+
const agentUpdates = new AgentUpdateManager({
|
|
98
|
+
onUpdate: (job) => recordTelegramAgentUpdateLifecycle(job),
|
|
99
|
+
});
|
|
88
100
|
const linkAttempts = new Map();
|
|
89
101
|
const drainingQueues = new Set();
|
|
90
102
|
const externalQueueTimers = new Map();
|
|
@@ -228,6 +240,19 @@ export function createBot(config, registry) {
|
|
|
228
240
|
const startTelegramAgentUpdate = async (ctx, agentId, operation = "update") => {
|
|
229
241
|
try {
|
|
230
242
|
const job = agentUpdates.start(agentId, agentUpdateContext(), operation);
|
|
243
|
+
const actor = telegramActivityActor(ctx);
|
|
244
|
+
agentUpdateActors.set(job.id, actor);
|
|
245
|
+
agentUpdateStates.set(job.id, { status: job.status, needsInput: job.needsInput });
|
|
246
|
+
appendActivity({
|
|
247
|
+
source: "telegram",
|
|
248
|
+
status: "info",
|
|
249
|
+
type: operation === "install" ? "agent_install_started" : "agent_update_started",
|
|
250
|
+
threadId: null,
|
|
251
|
+
workspace: config.workspace,
|
|
252
|
+
agentId,
|
|
253
|
+
actor,
|
|
254
|
+
detail: `${job.method}: ${job.summary}`,
|
|
255
|
+
});
|
|
231
256
|
const contextKey = contextKeyFromCtx(ctx);
|
|
232
257
|
if (contextKey) {
|
|
233
258
|
audit({
|
|
@@ -235,6 +260,9 @@ export function createBot(config, registry) {
|
|
|
235
260
|
status: "ok",
|
|
236
261
|
contextKey,
|
|
237
262
|
agentId,
|
|
263
|
+
actor,
|
|
264
|
+
actorId: getAuthenticatedUser(ctx)?.user.id ?? ctx.from?.id,
|
|
265
|
+
actorRole: getUserRole(ctx),
|
|
238
266
|
description: `${operation} ${agentId}`,
|
|
239
267
|
detail: job.summary,
|
|
240
268
|
});
|
|
@@ -495,6 +523,26 @@ export function createBot(config, registry) {
|
|
|
495
523
|
if (snapshot.activity.active) {
|
|
496
524
|
state.turnId = snapshot.activity.turnId;
|
|
497
525
|
state.startedAt = snapshot.activity.startedAt;
|
|
526
|
+
const turnKey = snapshot.activity.turnId ?? snapshot.activity.startedAt?.toISOString() ?? "unknown";
|
|
527
|
+
if (state.activityStartedTurnKey !== turnKey) {
|
|
528
|
+
const info = session.getInfo();
|
|
529
|
+
appendActivity({
|
|
530
|
+
source: "cli",
|
|
531
|
+
status: "running",
|
|
532
|
+
type: "cli_turn_started",
|
|
533
|
+
contextKey,
|
|
534
|
+
threadId: snapshot.threadId,
|
|
535
|
+
workspace: info.workspace,
|
|
536
|
+
agentId: info.agentId,
|
|
537
|
+
actor: CLI_ACTIVITY_ACTOR,
|
|
538
|
+
prompt: snapshot.latestUserMessage ?? `${snapshot.agentLabel} CLI task`,
|
|
539
|
+
detail: `${snapshot.sourceLabel}: ${snapshot.sourcePath}`,
|
|
540
|
+
});
|
|
541
|
+
state.activityStartedTurnKey = turnKey;
|
|
542
|
+
state.activityFinishedTurnKey = undefined;
|
|
543
|
+
state.activityToolStartLines = [];
|
|
544
|
+
state.activityToolEndLines = [];
|
|
545
|
+
}
|
|
498
546
|
if (mirrorMode !== "off") {
|
|
499
547
|
await sendExternalMirrorTyping(chatId, parsed.messageThreadId, state);
|
|
500
548
|
}
|
|
@@ -542,6 +590,43 @@ export function createBot(config, registry) {
|
|
|
542
590
|
state.latestMirroredEventLine = event.lineNumber;
|
|
543
591
|
}
|
|
544
592
|
}
|
|
593
|
+
const info = session.getInfo();
|
|
594
|
+
const loggedStartLines = new Set(state.activityToolStartLines ?? []);
|
|
595
|
+
const loggedEndLines = new Set(state.activityToolEndLines ?? []);
|
|
596
|
+
for (const event of snapshot.events.filter((event) => event.lineNumber > state.lastLine && event.kind === "tool")) {
|
|
597
|
+
if (event.status === "started" && !loggedStartLines.has(event.lineNumber)) {
|
|
598
|
+
appendActivity({
|
|
599
|
+
source: "cli",
|
|
600
|
+
status: "running",
|
|
601
|
+
type: "cli_tool_started",
|
|
602
|
+
contextKey,
|
|
603
|
+
threadId: snapshot.threadId,
|
|
604
|
+
workspace: info.workspace,
|
|
605
|
+
agentId: info.agentId,
|
|
606
|
+
actor: CLI_ACTIVITY_ACTOR,
|
|
607
|
+
prompt: snapshot.latestUserMessage ?? undefined,
|
|
608
|
+
detail: event.toolName ?? "tool",
|
|
609
|
+
});
|
|
610
|
+
loggedStartLines.add(event.lineNumber);
|
|
611
|
+
}
|
|
612
|
+
if ((event.status === "finished" || event.status === "failed") && !loggedEndLines.has(event.lineNumber)) {
|
|
613
|
+
appendActivity({
|
|
614
|
+
source: "cli",
|
|
615
|
+
status: event.status === "failed" ? "failed" : "completed",
|
|
616
|
+
type: event.status === "failed" ? "cli_tool_failed" : "cli_tool_completed",
|
|
617
|
+
contextKey,
|
|
618
|
+
threadId: snapshot.threadId,
|
|
619
|
+
workspace: info.workspace,
|
|
620
|
+
agentId: info.agentId,
|
|
621
|
+
actor: CLI_ACTIVITY_ACTOR,
|
|
622
|
+
prompt: snapshot.latestUserMessage ?? undefined,
|
|
623
|
+
detail: event.toolName ?? "tool",
|
|
624
|
+
});
|
|
625
|
+
loggedEndLines.add(event.lineNumber);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
state.activityToolStartLines = [...loggedStartLines].slice(-200);
|
|
629
|
+
state.activityToolEndLines = [...loggedEndLines].slice(-200);
|
|
545
630
|
state.lastLine = Math.max(state.lastLine, snapshot.lineCount);
|
|
546
631
|
return;
|
|
547
632
|
}
|
|
@@ -551,6 +636,25 @@ export function createBot(config, registry) {
|
|
|
551
636
|
}
|
|
552
637
|
const terminalEvent = [...snapshot.events].reverse().find((event) => event.kind === "task" && event.status && event.status !== "started");
|
|
553
638
|
if (terminalEvent) {
|
|
639
|
+
const turnKey = terminalEvent.turnId ?? snapshot.activity.turnId ?? state.startedAt?.toString() ?? "unknown";
|
|
640
|
+
if (state.activityFinishedTurnKey !== turnKey) {
|
|
641
|
+
const info = session.getInfo();
|
|
642
|
+
const startedAt = state.startedAt instanceof Date ? state.startedAt : state.startedAt ? new Date(state.startedAt) : snapshot.activity.startedAt;
|
|
643
|
+
appendActivity({
|
|
644
|
+
source: "cli",
|
|
645
|
+
status: terminalEvent.status === "aborted" ? "aborted" : terminalEvent.status === "failed" ? "failed" : "completed",
|
|
646
|
+
type: "cli_turn_finished",
|
|
647
|
+
contextKey,
|
|
648
|
+
threadId: snapshot.threadId,
|
|
649
|
+
workspace: info.workspace,
|
|
650
|
+
agentId: info.agentId,
|
|
651
|
+
actor: CLI_ACTIVITY_ACTOR,
|
|
652
|
+
prompt: snapshot.latestUserMessage ?? undefined,
|
|
653
|
+
detail: `${snapshot.agentLabel} CLI task ${terminalEvent.status ?? "finished"}.`,
|
|
654
|
+
durationMs: startedAt && terminalEvent.timestamp ? Math.max(0, terminalEvent.timestamp.getTime() - startedAt.getTime()) : undefined,
|
|
655
|
+
});
|
|
656
|
+
state.activityFinishedTurnKey = turnKey;
|
|
657
|
+
}
|
|
554
658
|
if (mirrorMode !== "off") {
|
|
555
659
|
const doneText = `${snapshot.agentLabel} CLI task ${terminalEvent.status}.`;
|
|
556
660
|
if (state.statusMessageId) {
|
|
@@ -637,6 +741,18 @@ export function createBot(config, registry) {
|
|
|
637
741
|
for (const artifact of (persistedReport?.artifacts ?? report.artifacts)) {
|
|
638
742
|
await sendArtifactFileByApi(bot.api, chatId, artifact, messageThreadId);
|
|
639
743
|
}
|
|
744
|
+
const info = session.getInfo();
|
|
745
|
+
appendActivity({
|
|
746
|
+
source: "cli",
|
|
747
|
+
status: "info",
|
|
748
|
+
type: "artifacts_sent",
|
|
749
|
+
contextKey,
|
|
750
|
+
threadId: info.threadId,
|
|
751
|
+
workspace: info.workspace,
|
|
752
|
+
agentId: info.agentId,
|
|
753
|
+
actor: CLI_ACTIVITY_ACTOR,
|
|
754
|
+
detail: summary,
|
|
755
|
+
});
|
|
640
756
|
if (state)
|
|
641
757
|
state.artifactsDeliveredForTurnId = turnId;
|
|
642
758
|
};
|
|
@@ -688,9 +804,11 @@ export function createBot(config, registry) {
|
|
|
688
804
|
};
|
|
689
805
|
const auditContext = (ctx, contextKey, session, patch) => {
|
|
690
806
|
const info = session.getInfo();
|
|
807
|
+
const authUser = getAuthenticatedUser(ctx);
|
|
691
808
|
audit({
|
|
692
809
|
contextKey,
|
|
693
|
-
|
|
810
|
+
actor: telegramActivityActor(ctx),
|
|
811
|
+
actorId: authUser?.user.id ?? ctx.from?.id,
|
|
694
812
|
actorRole: getUserRole(ctx),
|
|
695
813
|
agentId: idOf(info),
|
|
696
814
|
threadId: info.threadId,
|
|
@@ -698,10 +816,68 @@ export function createBot(config, registry) {
|
|
|
698
816
|
...patch,
|
|
699
817
|
});
|
|
700
818
|
};
|
|
819
|
+
function telegramActivityActor(ctx) {
|
|
820
|
+
const user = ctx.from;
|
|
821
|
+
const authUser = getAuthenticatedUser(ctx);
|
|
822
|
+
const label = authUser?.user.displayName || formatTelegramName(ctx) || user?.username || (user?.id ? String(user.id) : "Telegram user");
|
|
823
|
+
return {
|
|
824
|
+
channel: "telegram",
|
|
825
|
+
id: authUser?.user.id ?? (user?.id !== undefined ? `telegram:${user.id}` : undefined),
|
|
826
|
+
label,
|
|
827
|
+
username: authUser?.user.email ?? user?.username,
|
|
828
|
+
channelUserId: user?.id !== undefined ? String(user.id) : undefined,
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function appendActivity(input) {
|
|
832
|
+
return activityStore.append(input);
|
|
833
|
+
}
|
|
834
|
+
function appendTelegramActivity(ctx, contextKey, session, input) {
|
|
835
|
+
const info = session.getInfo();
|
|
836
|
+
return appendActivity({
|
|
837
|
+
source: "telegram",
|
|
838
|
+
contextKey,
|
|
839
|
+
...input,
|
|
840
|
+
threadId: input.threadId ?? info.threadId,
|
|
841
|
+
workspace: input.workspace ?? info.workspace,
|
|
842
|
+
agentId: input.agentId ?? idOf(info),
|
|
843
|
+
actor: input.actor ?? telegramActivityActor(ctx),
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
function recordTelegramAgentUpdateLifecycle(job) {
|
|
847
|
+
const previous = agentUpdateStates.get(job.id);
|
|
848
|
+
const actor = agentUpdateActors.get(job.id);
|
|
849
|
+
if (job.needsInput && !previous?.needsInput) {
|
|
850
|
+
appendActivity({
|
|
851
|
+
source: "telegram",
|
|
852
|
+
status: "info",
|
|
853
|
+
type: "agent_update_input_required",
|
|
854
|
+
threadId: null,
|
|
855
|
+
workspace: config.workspace,
|
|
856
|
+
agentId: job.agentId,
|
|
857
|
+
actor,
|
|
858
|
+
detail: `${job.agentLabel} ${job.operation} may require input.`,
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
if (job.status !== "running" && previous?.status === "running") {
|
|
862
|
+
appendActivity({
|
|
863
|
+
source: "telegram",
|
|
864
|
+
status: job.status === "completed" ? "completed" : job.status === "cancelled" ? "aborted" : "failed",
|
|
865
|
+
type: job.operation === "install" ? `agent_install_${job.status}` : `agent_update_${job.status}`,
|
|
866
|
+
threadId: null,
|
|
867
|
+
workspace: config.workspace,
|
|
868
|
+
agentId: job.agentId,
|
|
869
|
+
actor,
|
|
870
|
+
detail: job.error ?? `${job.agentLabel} ${job.operation} ${job.status}.`,
|
|
871
|
+
durationMs: Math.max(0, Date.parse(job.finishedAt ?? job.updatedAt) - Date.parse(job.startedAt)),
|
|
872
|
+
});
|
|
873
|
+
agentUpdateActors.delete(job.id);
|
|
874
|
+
}
|
|
875
|
+
agentUpdateStates.set(job.id, { status: job.status, needsInput: job.needsInput });
|
|
876
|
+
}
|
|
701
877
|
const denyIfLocked = async (ctx, contextKey, session) => {
|
|
702
878
|
const lock = lockStore.get(contextKey);
|
|
703
879
|
const isAdmin = isAdminUser(ctx);
|
|
704
|
-
if (canWriteWithLock(lock, ctx.
|
|
880
|
+
if (canWriteWithLock(lock, getAuthenticatedUser(ctx)?.user.id, isAdmin)) {
|
|
705
881
|
return false;
|
|
706
882
|
}
|
|
707
883
|
const owner = formatLockOwner(lock);
|
|
@@ -711,6 +887,11 @@ export function createBot(config, registry) {
|
|
|
711
887
|
status: "denied",
|
|
712
888
|
detail: text,
|
|
713
889
|
});
|
|
890
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
891
|
+
status: "failed",
|
|
892
|
+
type: "lock_denied",
|
|
893
|
+
detail: text,
|
|
894
|
+
});
|
|
714
895
|
await safeReply(ctx, escapeHTML(text), { fallbackText: text });
|
|
715
896
|
return true;
|
|
716
897
|
};
|
|
@@ -809,7 +990,11 @@ export function createBot(config, registry) {
|
|
|
809
990
|
}
|
|
810
991
|
const parsed = parseContextKey(contextKey);
|
|
811
992
|
const messageThreadId = parsed.messageThreadId;
|
|
812
|
-
const
|
|
993
|
+
const rawEnvelope = isPromptEnvelopeLike(prompt) ? prompt : toPromptEnvelope(prompt);
|
|
994
|
+
const envelope = {
|
|
995
|
+
...rawEnvelope,
|
|
996
|
+
activityActor: rawEnvelope.activityActor ?? telegramActivityActor(ctx),
|
|
997
|
+
};
|
|
813
998
|
if (!options.fromQueue && await denyIfLocked(ctx, contextKey, session)) {
|
|
814
999
|
return;
|
|
815
1000
|
}
|
|
@@ -840,6 +1025,13 @@ export function createBot(config, registry) {
|
|
|
840
1025
|
description: item.description,
|
|
841
1026
|
detail: busy.kind,
|
|
842
1027
|
});
|
|
1028
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1029
|
+
status: "queued",
|
|
1030
|
+
type: "prompt_queued",
|
|
1031
|
+
prompt: item.description,
|
|
1032
|
+
detail: `Queued prompt ${item.id} at position ${position}; busy=${busy.kind}`,
|
|
1033
|
+
actor: envelope.activityActor,
|
|
1034
|
+
});
|
|
843
1035
|
if (busy.kind === "external") {
|
|
844
1036
|
scheduleExternalQueueDrain(ctx, contextKey, chatId, session);
|
|
845
1037
|
}
|
|
@@ -877,6 +1069,9 @@ export function createBot(config, registry) {
|
|
|
877
1069
|
let lastRenderedPlan = "";
|
|
878
1070
|
let planMessageSending = false;
|
|
879
1071
|
let lastTurnUsage;
|
|
1072
|
+
let promptStartedAt;
|
|
1073
|
+
const toolActivityNames = new Map();
|
|
1074
|
+
const toolActivityStartedAt = new Map();
|
|
880
1075
|
const typingInterval = setInterval(() => {
|
|
881
1076
|
void sendChatActionSafe(bot.api, chatId, "typing", messageThreadId).catch(() => { });
|
|
882
1077
|
}, TYPING_INTERVAL_MS);
|
|
@@ -1080,6 +1275,15 @@ export function createBot(config, registry) {
|
|
|
1080
1275
|
progress.lastTool = toolName;
|
|
1081
1276
|
progress.updatedAt = Date.now();
|
|
1082
1277
|
progress.toolCounts.set(toolName, (progress.toolCounts.get(toolName) ?? 0) + 1);
|
|
1278
|
+
toolActivityNames.set(toolCallId, toolName);
|
|
1279
|
+
toolActivityStartedAt.set(toolCallId, Date.now());
|
|
1280
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1281
|
+
status: "running",
|
|
1282
|
+
type: "tool_started",
|
|
1283
|
+
prompt: envelope.description,
|
|
1284
|
+
detail: toolName,
|
|
1285
|
+
actor: envelope.activityActor,
|
|
1286
|
+
});
|
|
1083
1287
|
if (toolVerbosity === "summary") {
|
|
1084
1288
|
toolCounts.set(toolName, (toolCounts.get(toolName) ?? 0) + 1);
|
|
1085
1289
|
return;
|
|
@@ -1127,6 +1331,18 @@ export function createBot(config, registry) {
|
|
|
1127
1331
|
onToolEnd: (toolCallId, isError) => {
|
|
1128
1332
|
progress.currentTool = undefined;
|
|
1129
1333
|
progress.updatedAt = Date.now();
|
|
1334
|
+
const activityToolName = toolActivityNames.get(toolCallId) ?? "tool";
|
|
1335
|
+
const activityStartedAt = toolActivityStartedAt.get(toolCallId);
|
|
1336
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1337
|
+
status: isError ? "failed" : "completed",
|
|
1338
|
+
type: isError ? "tool_failed" : "tool_completed",
|
|
1339
|
+
prompt: envelope.description,
|
|
1340
|
+
detail: activityToolName,
|
|
1341
|
+
actor: envelope.activityActor,
|
|
1342
|
+
durationMs: activityStartedAt ? Date.now() - activityStartedAt : undefined,
|
|
1343
|
+
});
|
|
1344
|
+
toolActivityNames.delete(toolCallId);
|
|
1345
|
+
toolActivityStartedAt.delete(toolCallId);
|
|
1130
1346
|
if (toolVerbosity === "none" || toolVerbosity === "summary") {
|
|
1131
1347
|
return;
|
|
1132
1348
|
}
|
|
@@ -1266,11 +1482,25 @@ export function createBot(config, registry) {
|
|
|
1266
1482
|
replyMarkup: createQueuedPromptCancelKeyboard(contextKey, item.id),
|
|
1267
1483
|
});
|
|
1268
1484
|
await updateQueueStatusMessage(contextKey, `Waiting for ${label} CLI task... ${promptStore.list(contextKey).length} queued.`);
|
|
1485
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1486
|
+
status: "queued",
|
|
1487
|
+
type: "prompt_queued",
|
|
1488
|
+
prompt: item.description,
|
|
1489
|
+
detail: `Queued prompt ${item.id} at position 1; external ${label} CLI task active`,
|
|
1490
|
+
actor: envelope.activityActor,
|
|
1491
|
+
});
|
|
1269
1492
|
scheduleExternalQueueDrain(ctx, contextKey, chatId, session);
|
|
1270
1493
|
turnProgress.delete(contextKey);
|
|
1271
1494
|
return;
|
|
1272
1495
|
}
|
|
1273
1496
|
promptStore.setLastPrompt(contextKey, envelope);
|
|
1497
|
+
promptStartedAt = Date.now();
|
|
1498
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1499
|
+
status: "running",
|
|
1500
|
+
type: "prompt_started",
|
|
1501
|
+
prompt: envelope.description,
|
|
1502
|
+
actor: envelope.activityActor,
|
|
1503
|
+
});
|
|
1274
1504
|
auditContext(ctx, contextKey, session, {
|
|
1275
1505
|
action: "prompt_started",
|
|
1276
1506
|
status: "ok",
|
|
@@ -1300,6 +1530,13 @@ export function createBot(config, registry) {
|
|
|
1300
1530
|
status: "ok",
|
|
1301
1531
|
description: envelope.description,
|
|
1302
1532
|
});
|
|
1533
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1534
|
+
status: "completed",
|
|
1535
|
+
type: "prompt_completed",
|
|
1536
|
+
prompt: envelope.description,
|
|
1537
|
+
actor: envelope.activityActor,
|
|
1538
|
+
durationMs: promptStartedAt ? Date.now() - promptStartedAt : undefined,
|
|
1539
|
+
});
|
|
1303
1540
|
}
|
|
1304
1541
|
catch (error) {
|
|
1305
1542
|
progress.status = "failed";
|
|
@@ -1310,6 +1547,16 @@ export function createBot(config, registry) {
|
|
|
1310
1547
|
description: envelope.description,
|
|
1311
1548
|
detail: progress.error,
|
|
1312
1549
|
});
|
|
1550
|
+
if (promptStartedAt) {
|
|
1551
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1552
|
+
status: "failed",
|
|
1553
|
+
type: "prompt_failed",
|
|
1554
|
+
prompt: envelope.description,
|
|
1555
|
+
detail: progress.error,
|
|
1556
|
+
actor: envelope.activityActor,
|
|
1557
|
+
durationMs: Date.now() - promptStartedAt,
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1313
1560
|
progress.completedAt = Date.now();
|
|
1314
1561
|
progress.updatedAt = progress.completedAt;
|
|
1315
1562
|
stopTyping();
|
|
@@ -1400,6 +1647,15 @@ export function createBot(config, registry) {
|
|
|
1400
1647
|
source: "turn",
|
|
1401
1648
|
};
|
|
1402
1649
|
await deliverArtifactReport(ctx, chatId, report, messageThreadId);
|
|
1650
|
+
const contextKey = contextKeyFromCtx(ctx);
|
|
1651
|
+
const session = contextKey ? registry.get(contextKey) : undefined;
|
|
1652
|
+
if (contextKey && session) {
|
|
1653
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
1654
|
+
status: "info",
|
|
1655
|
+
type: "artifacts_sent",
|
|
1656
|
+
detail: formatArtifactSummary(report.artifacts, report.skippedCount, report.omittedCount),
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1403
1659
|
await pruneArtifacts(workspace);
|
|
1404
1660
|
};
|
|
1405
1661
|
const deliverArtifactReport = async (ctx, chatId, report, messageThreadId) => {
|
|
@@ -1596,6 +1852,11 @@ export function createBot(config, registry) {
|
|
|
1596
1852
|
}
|
|
1597
1853
|
const receivedText = `Received ${stagedFiles.length} media group file${stagedFiles.length === 1 ? "" : "s"}${skippedCount > 0 ? ` (${skippedCount} skipped)` : ""}.`;
|
|
1598
1854
|
await safeReply(pending.ctx, escapeHTML(receivedText), { fallbackText: receivedText });
|
|
1855
|
+
appendTelegramActivity(pending.ctx, pending.contextKey, pending.session, {
|
|
1856
|
+
status: "info",
|
|
1857
|
+
type: "attachment_staged",
|
|
1858
|
+
detail: receivedText,
|
|
1859
|
+
});
|
|
1599
1860
|
await sendChatActionSafe(pending.ctx.api, pending.chatId, "typing", pending.messageThreadId).catch(() => { });
|
|
1600
1861
|
const promptInput = {
|
|
1601
1862
|
stagedFileInstructions: buildFileInstructions(stagedFiles, outDir),
|
|
@@ -1625,6 +1886,7 @@ export function createBot(config, registry) {
|
|
|
1625
1886
|
checkAgentAuthStatus,
|
|
1626
1887
|
isTopicContext,
|
|
1627
1888
|
replyChannelAction,
|
|
1889
|
+
commandService,
|
|
1628
1890
|
});
|
|
1629
1891
|
registerTelegramAgentCommands({
|
|
1630
1892
|
bot,
|
|
@@ -1641,6 +1903,13 @@ export function createBot(config, registry) {
|
|
|
1641
1903
|
startAgentLogout,
|
|
1642
1904
|
hostLoginCommand,
|
|
1643
1905
|
hostLogoutCommand,
|
|
1906
|
+
appendActivity: (ctx, input) => appendActivity({
|
|
1907
|
+
source: "telegram",
|
|
1908
|
+
...input,
|
|
1909
|
+
threadId: input.threadId ?? null,
|
|
1910
|
+
workspace: input.workspace ?? config.workspace,
|
|
1911
|
+
actor: input.actor ?? telegramActivityActor(ctx),
|
|
1912
|
+
}),
|
|
1644
1913
|
});
|
|
1645
1914
|
registerTelegramPreferenceCommands({
|
|
1646
1915
|
bot,
|
|
@@ -1673,6 +1942,7 @@ export function createBot(config, registry) {
|
|
|
1673
1942
|
getEffectiveVoiceLanguage,
|
|
1674
1943
|
isVoiceTranscribeOnly,
|
|
1675
1944
|
replyChannelAction,
|
|
1945
|
+
commandService,
|
|
1676
1946
|
});
|
|
1677
1947
|
registerTelegramOperationalCommands({
|
|
1678
1948
|
bot,
|
|
@@ -1686,10 +1956,34 @@ export function createBot(config, registry) {
|
|
|
1686
1956
|
getExternalActivity,
|
|
1687
1957
|
isAdminUser,
|
|
1688
1958
|
auditContext,
|
|
1959
|
+
getLockOwner: (ctx) => {
|
|
1960
|
+
const authUser = getAuthenticatedUser(ctx);
|
|
1961
|
+
if (!authUser) {
|
|
1962
|
+
return null;
|
|
1963
|
+
}
|
|
1964
|
+
return {
|
|
1965
|
+
userId: authUser.user.id,
|
|
1966
|
+
label: authUser.user.displayName || authUser.user.email,
|
|
1967
|
+
channel: "telegram",
|
|
1968
|
+
channelUserId: ctx.from?.id !== undefined ? String(ctx.from.id) : undefined,
|
|
1969
|
+
};
|
|
1970
|
+
},
|
|
1689
1971
|
updateSessionMetadata,
|
|
1690
1972
|
});
|
|
1691
1973
|
registerTelegramSupportCommands({ bot, config, auditLog, agentUpdates, getUserRole, audit });
|
|
1692
|
-
registerTelegramUpdateCommands({
|
|
1974
|
+
registerTelegramUpdateCommands({
|
|
1975
|
+
bot,
|
|
1976
|
+
agentUpdates,
|
|
1977
|
+
replyChannelAction,
|
|
1978
|
+
startTelegramAgentUpdate,
|
|
1979
|
+
appendActivity: (ctx, input) => appendActivity({
|
|
1980
|
+
source: "telegram",
|
|
1981
|
+
...input,
|
|
1982
|
+
threadId: input.threadId ?? null,
|
|
1983
|
+
workspace: input.workspace ?? config.workspace,
|
|
1984
|
+
actor: input.actor ?? telegramActivityActor(ctx),
|
|
1985
|
+
}),
|
|
1986
|
+
});
|
|
1693
1987
|
bot.command("new", async (ctx) => {
|
|
1694
1988
|
const chatId = ctx.chat?.id;
|
|
1695
1989
|
if (!chatId) {
|
|
@@ -1718,6 +2012,14 @@ export function createBot(config, registry) {
|
|
|
1718
2012
|
try {
|
|
1719
2013
|
const info = await session.newThread();
|
|
1720
2014
|
updateSessionMetadata(contextKey, session);
|
|
2015
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2016
|
+
status: "info",
|
|
2017
|
+
type: "session_new",
|
|
2018
|
+
threadId: info.threadId,
|
|
2019
|
+
workspace: info.workspace,
|
|
2020
|
+
agentId: info.agentId,
|
|
2021
|
+
detail: info.workspace,
|
|
2022
|
+
});
|
|
1721
2023
|
const label = isTopicContext(contextKey) ? "New thread created for this topic." : "New thread created.";
|
|
1722
2024
|
const policyLine = renderWorkspacePolicyLine(info.workspace, config);
|
|
1723
2025
|
const plainText = [label, policyLine, "", renderSessionInfoPlain(info)].filter((line) => line !== undefined).join("\n");
|
|
@@ -1755,12 +2057,22 @@ export function createBot(config, registry) {
|
|
|
1755
2057
|
if (busy.kind === "external") {
|
|
1756
2058
|
const text = `Cannot abort the external ${busy.activity.agentLabel} CLI task from NordRelay. Stop it in the terminal where it is running; queued Telegram messages will wait.`;
|
|
1757
2059
|
await safeReply(ctx, escapeHTML(text), { fallbackText: text });
|
|
2060
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2061
|
+
status: "failed",
|
|
2062
|
+
type: "prompt_abort_rejected",
|
|
2063
|
+
detail: text,
|
|
2064
|
+
});
|
|
1758
2065
|
return;
|
|
1759
2066
|
}
|
|
1760
2067
|
await session.abort();
|
|
1761
2068
|
await safeReply(ctx, escapeHTML("Aborted current operation"), {
|
|
1762
2069
|
fallbackText: "Aborted current operation",
|
|
1763
2070
|
});
|
|
2071
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2072
|
+
status: "aborted",
|
|
2073
|
+
type: "prompt_aborted",
|
|
2074
|
+
detail: "Abort requested from Telegram.",
|
|
2075
|
+
});
|
|
1764
2076
|
}
|
|
1765
2077
|
catch (error) {
|
|
1766
2078
|
await safeReply(ctx, `<b>Failed:</b> ${escapeHTML(friendlyErrorText(error))}`, {
|
|
@@ -1809,6 +2121,8 @@ export function createBot(config, registry) {
|
|
|
1809
2121
|
drainQueuedPrompts,
|
|
1810
2122
|
handleUserPrompt,
|
|
1811
2123
|
auditContext,
|
|
2124
|
+
activityActor: telegramActivityActor,
|
|
2125
|
+
appendActivity: appendTelegramActivity,
|
|
1812
2126
|
});
|
|
1813
2127
|
registerTelegramArtifactCommands({
|
|
1814
2128
|
bot,
|
|
@@ -1816,6 +2130,13 @@ export function createBot(config, registry) {
|
|
|
1816
2130
|
getContextSession,
|
|
1817
2131
|
deliverArtifactReport,
|
|
1818
2132
|
deliverArtifactReportZip,
|
|
2133
|
+
appendActivity: (ctx, input) => appendActivity({
|
|
2134
|
+
source: "telegram",
|
|
2135
|
+
...input,
|
|
2136
|
+
threadId: input.threadId ?? null,
|
|
2137
|
+
workspace: input.workspace ?? config.workspace,
|
|
2138
|
+
actor: input.actor ?? telegramActivityActor(ctx),
|
|
2139
|
+
}),
|
|
1819
2140
|
});
|
|
1820
2141
|
bot.command("session", async (ctx) => {
|
|
1821
2142
|
const contextSession = await getContextSession(ctx, { deferThreadStart: true });
|
|
@@ -1910,6 +2231,14 @@ export function createBot(config, registry) {
|
|
|
1910
2231
|
try {
|
|
1911
2232
|
const info = session.handback();
|
|
1912
2233
|
updateSessionMetadata(contextKey, session);
|
|
2234
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2235
|
+
status: "info",
|
|
2236
|
+
type: "handback",
|
|
2237
|
+
threadId: info.threadId,
|
|
2238
|
+
workspace: info.workspace,
|
|
2239
|
+
agentId: idOf(session.getInfo()),
|
|
2240
|
+
detail: info.command ?? info.threadId ?? "handback",
|
|
2241
|
+
});
|
|
1913
2242
|
if (!info.threadId) {
|
|
1914
2243
|
await safeReply(ctx, escapeHTML("This thread has not started yet, so there is no resumable thread ID. Send a message to create one, or use /new to start fresh."), {
|
|
1915
2244
|
fallbackText: "This thread has not started yet, so there is no resumable thread ID. Send a message to create one, or use /new to start fresh.",
|
|
@@ -2005,6 +2334,14 @@ export function createBot(config, registry) {
|
|
|
2005
2334
|
try {
|
|
2006
2335
|
const info = await session.switchSession(threadId);
|
|
2007
2336
|
updateSessionMetadata(contextKey, session);
|
|
2337
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2338
|
+
status: "info",
|
|
2339
|
+
type: "session_attach",
|
|
2340
|
+
threadId: info.threadId,
|
|
2341
|
+
workspace: info.workspace,
|
|
2342
|
+
agentId: info.agentId,
|
|
2343
|
+
detail: threadId,
|
|
2344
|
+
});
|
|
2008
2345
|
const policyLine = renderWorkspacePolicyLine(info.workspace, config);
|
|
2009
2346
|
const html = ["<b>Attached to thread.</b>", policyLine ? `<i>${escapeHTML(policyLine)}</i>` : undefined, "", renderSessionInfoHTML(info)].filter((line) => line !== undefined).join("\n");
|
|
2010
2347
|
const plain = ["Attached to thread.", policyLine, "", renderSessionInfoPlain(info)].filter((line) => line !== undefined).join("\n");
|
|
@@ -2051,6 +2388,14 @@ export function createBot(config, registry) {
|
|
|
2051
2388
|
try {
|
|
2052
2389
|
const info = await session.switchSession(threadId);
|
|
2053
2390
|
updateSessionMetadata(contextKey, session);
|
|
2391
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2392
|
+
status: "info",
|
|
2393
|
+
type: "session_switch",
|
|
2394
|
+
threadId: info.threadId,
|
|
2395
|
+
workspace: info.workspace,
|
|
2396
|
+
agentId: info.agentId,
|
|
2397
|
+
detail: threadId,
|
|
2398
|
+
});
|
|
2054
2399
|
const policyLine = renderWorkspacePolicyLine(info.workspace, config);
|
|
2055
2400
|
const html = ["<b>Switched thread.</b>", policyLine ? `<i>${escapeHTML(policyLine)}</i>` : undefined, "", renderSessionInfoHTML(info)].filter((line) => line !== undefined).join("\n");
|
|
2056
2401
|
const plain = ["Switched thread.", policyLine, "", renderSessionInfoPlain(info)].filter((line) => line !== undefined).join("\n");
|
|
@@ -2137,6 +2482,12 @@ export function createBot(config, registry) {
|
|
|
2137
2482
|
return;
|
|
2138
2483
|
}
|
|
2139
2484
|
const pinned = registry.pinThread(contextKey, threadId);
|
|
2485
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2486
|
+
status: "info",
|
|
2487
|
+
type: "session_pinned",
|
|
2488
|
+
threadId,
|
|
2489
|
+
detail: threadId,
|
|
2490
|
+
});
|
|
2140
2491
|
await safeReply(ctx, `<b>Pinned thread:</b> <code>${escapeHTML(threadId)}</code>\n<b>Total pinned:</b> <code>${pinned.length}</code>`, {
|
|
2141
2492
|
fallbackText: `Pinned thread: ${threadId}\nTotal pinned: ${pinned.length}`,
|
|
2142
2493
|
});
|
|
@@ -2157,6 +2508,12 @@ export function createBot(config, registry) {
|
|
|
2157
2508
|
return;
|
|
2158
2509
|
}
|
|
2159
2510
|
const pinned = registry.unpinThread(contextKey, threadId);
|
|
2511
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2512
|
+
status: "info",
|
|
2513
|
+
type: "session_unpinned",
|
|
2514
|
+
threadId,
|
|
2515
|
+
detail: threadId,
|
|
2516
|
+
});
|
|
2160
2517
|
await safeReply(ctx, `<b>Unpinned thread:</b> <code>${escapeHTML(threadId)}</code>\n<b>Total pinned:</b> <code>${pinned.length}</code>`, {
|
|
2161
2518
|
fallbackText: `Unpinned thread: ${threadId}\nTotal pinned: ${pinned.length}`,
|
|
2162
2519
|
});
|
|
@@ -2272,6 +2629,14 @@ export function createBot(config, registry) {
|
|
|
2272
2629
|
const result = session.setFastMode(nextFastMode);
|
|
2273
2630
|
updateSessionMetadata(contextKey, session);
|
|
2274
2631
|
const info = session.getInfo();
|
|
2632
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2633
|
+
status: "info",
|
|
2634
|
+
type: "fast_mode_changed",
|
|
2635
|
+
threadId: info.threadId,
|
|
2636
|
+
workspace: info.workspace,
|
|
2637
|
+
agentId: info.agentId,
|
|
2638
|
+
detail: result.enabled ? "on" : "off",
|
|
2639
|
+
});
|
|
2275
2640
|
const plain = [
|
|
2276
2641
|
`Fast mode: ${result.enabled ? "on" : "off"}`,
|
|
2277
2642
|
`Launch profile: ${result.profile.label} (${formatLaunchProfileBehavior(result.profile)})`,
|
|
@@ -2384,6 +2749,15 @@ export function createBot(config, registry) {
|
|
|
2384
2749
|
});
|
|
2385
2750
|
}
|
|
2386
2751
|
const session = registry.get(pending.contextKey);
|
|
2752
|
+
if (session) {
|
|
2753
|
+
appendTelegramActivity(ctx, pending.contextKey, session, {
|
|
2754
|
+
status: "aborted",
|
|
2755
|
+
type: "prompt_approval_denied",
|
|
2756
|
+
prompt: pending.prompt.description,
|
|
2757
|
+
detail: approvalId,
|
|
2758
|
+
actor: pending.prompt.activityActor,
|
|
2759
|
+
});
|
|
2760
|
+
}
|
|
2387
2761
|
if (chatId && session) {
|
|
2388
2762
|
void drainQueuedPrompts(ctx, pending.contextKey, chatId, session).catch((error) => {
|
|
2389
2763
|
console.error("Failed to drain queue after approval denial:", error);
|
|
@@ -2402,6 +2776,13 @@ export function createBot(config, registry) {
|
|
|
2402
2776
|
fallbackText: `Approved prompt ${approvalId}.`,
|
|
2403
2777
|
});
|
|
2404
2778
|
}
|
|
2779
|
+
appendTelegramActivity(ctx, pending.contextKey, contextSession.session, {
|
|
2780
|
+
status: "info",
|
|
2781
|
+
type: "prompt_approval_approved",
|
|
2782
|
+
prompt: pending.prompt.description,
|
|
2783
|
+
detail: approvalId,
|
|
2784
|
+
actor: pending.prompt.activityActor,
|
|
2785
|
+
});
|
|
2405
2786
|
await handleUserPrompt(ctx, pending.contextKey, chatId ?? parseContextKey(pending.contextKey).chatId, contextSession.session, pending.prompt, {
|
|
2406
2787
|
approved: true,
|
|
2407
2788
|
});
|
|
@@ -2445,6 +2826,14 @@ export function createBot(config, registry) {
|
|
|
2445
2826
|
try {
|
|
2446
2827
|
const info = await session.switchSession(threadId);
|
|
2447
2828
|
updateSessionMetadata(contextKey, session);
|
|
2829
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2830
|
+
status: "info",
|
|
2831
|
+
type: "session_switch",
|
|
2832
|
+
threadId: info.threadId,
|
|
2833
|
+
workspace: info.workspace,
|
|
2834
|
+
agentId: info.agentId,
|
|
2835
|
+
detail: threadId,
|
|
2836
|
+
});
|
|
2448
2837
|
const policyLine = renderWorkspacePolicyLine(info.workspace, config);
|
|
2449
2838
|
const plainText = ["Switched session.", policyLine, "", renderSessionInfoPlain(info)].filter((line) => line !== undefined).join("\n");
|
|
2450
2839
|
const html = ["<b>Switched session.</b>", policyLine ? `<i>${escapeHTML(policyLine)}</i>` : undefined, "", renderSessionInfoHTML(info)].filter((line) => line !== undefined).join("\n");
|
|
@@ -2507,6 +2896,14 @@ export function createBot(config, registry) {
|
|
|
2507
2896
|
try {
|
|
2508
2897
|
const info = await session.newThread(workspace);
|
|
2509
2898
|
updateSessionMetadata(contextKey, session);
|
|
2899
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
2900
|
+
status: "info",
|
|
2901
|
+
type: "session_new",
|
|
2902
|
+
threadId: info.threadId,
|
|
2903
|
+
workspace: info.workspace,
|
|
2904
|
+
agentId: info.agentId,
|
|
2905
|
+
detail: workspace,
|
|
2906
|
+
});
|
|
2510
2907
|
const label = isTopicContext(contextKey) ? "New thread created for this topic." : "New thread created.";
|
|
2511
2908
|
const policyLine = renderWorkspacePolicyLine(info.workspace, config);
|
|
2512
2909
|
const plainText = [label, policyLine, "", renderSessionInfoPlain(info)].filter((line) => line !== undefined).join("\n");
|
|
@@ -2602,6 +2999,14 @@ export function createBot(config, registry) {
|
|
|
2602
2999
|
session.setLaunchProfile(profile.id);
|
|
2603
3000
|
updateSessionMetadata(contextKey, session);
|
|
2604
3001
|
const info = session.getInfo();
|
|
3002
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3003
|
+
status: "info",
|
|
3004
|
+
type: "launch_profile_changed",
|
|
3005
|
+
threadId: info.threadId,
|
|
3006
|
+
workspace: info.workspace,
|
|
3007
|
+
agentId: info.agentId,
|
|
3008
|
+
detail: info.launchProfileLabel,
|
|
3009
|
+
});
|
|
2605
3010
|
const html = [
|
|
2606
3011
|
`<b>Launch profile set to</b> <code>${escapeHTML(info.launchProfileLabel)}</code>`,
|
|
2607
3012
|
`<b>Behavior:</b> <code>${escapeHTML(info.launchProfileBehavior)}</code>`,
|
|
@@ -2664,6 +3069,14 @@ export function createBot(config, registry) {
|
|
|
2664
3069
|
session.setLaunchProfile(profile.id);
|
|
2665
3070
|
updateSessionMetadata(contextKey, session);
|
|
2666
3071
|
const info = session.getInfo();
|
|
3072
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3073
|
+
status: "info",
|
|
3074
|
+
type: "launch_profile_changed",
|
|
3075
|
+
threadId: info.threadId,
|
|
3076
|
+
workspace: info.workspace,
|
|
3077
|
+
agentId: info.agentId,
|
|
3078
|
+
detail: info.launchProfileLabel,
|
|
3079
|
+
});
|
|
2667
3080
|
await ctx.answerCallbackQuery({ text: `Launch set to ${info.launchProfileLabel}` });
|
|
2668
3081
|
const html = [
|
|
2669
3082
|
`<b>Launch profile set to</b> <code>${escapeHTML(info.launchProfileLabel)}</code>`,
|
|
@@ -2710,6 +3123,15 @@ export function createBot(config, registry) {
|
|
|
2710
3123
|
try {
|
|
2711
3124
|
const result = await session.setModelForCurrentSession(slug);
|
|
2712
3125
|
updateSessionMetadata(contextKey, session);
|
|
3126
|
+
const info = session.getInfo();
|
|
3127
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3128
|
+
status: "info",
|
|
3129
|
+
type: "model_changed",
|
|
3130
|
+
threadId: info.threadId,
|
|
3131
|
+
workspace: info.workspace,
|
|
3132
|
+
agentId: info.agentId,
|
|
3133
|
+
detail: result.value,
|
|
3134
|
+
});
|
|
2713
3135
|
const scope = formatAgentSettingScope(session.getInfo(), result.appliedToActiveThread);
|
|
2714
3136
|
const html = `<b>Model set to</b> <code>${escapeHTML(result.value)}</code> — ${escapeHTML(scope)}.`;
|
|
2715
3137
|
const plainText = `Model set to ${result.value} — ${scope}.`;
|
|
@@ -2756,6 +3178,15 @@ export function createBot(config, registry) {
|
|
|
2756
3178
|
pendingEffortButtons.delete(contextKey);
|
|
2757
3179
|
const result = await session.setReasoningEffortForCurrentSession(effort);
|
|
2758
3180
|
updateSessionMetadata(contextKey, session);
|
|
3181
|
+
const info = session.getInfo();
|
|
3182
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3183
|
+
status: "info",
|
|
3184
|
+
type: "reasoning_changed",
|
|
3185
|
+
threadId: info.threadId,
|
|
3186
|
+
workspace: info.workspace,
|
|
3187
|
+
agentId: info.agentId,
|
|
3188
|
+
detail: result.value,
|
|
3189
|
+
});
|
|
2759
3190
|
const label = agentReasoningLabel(idOf(session.getInfo()));
|
|
2760
3191
|
const scope = formatAgentSettingScope(session.getInfo(), result.appliedToActiveThread);
|
|
2761
3192
|
const html = `⚡ ${escapeHTML(label)} set to <code>${escapeHTML(effort)}</code> — ${escapeHTML(scope)}.`;
|
|
@@ -2815,12 +3246,24 @@ export function createBot(config, registry) {
|
|
|
2815
3246
|
}
|
|
2816
3247
|
const preview = trimLine(transcript.replace(/\s+/g, " "), 100);
|
|
2817
3248
|
await safeReply(ctx, `🎙️ <b>Transcribed:</b> ${escapeHTML(preview)} <i>(via ${escapeHTML(result.backend)}, ${formatDurationSeconds(result.durationMs / 1000)})</i>`, { fallbackText: `🎙️ Transcribed: ${preview} (via ${result.backend}, ${formatDurationSeconds(result.durationMs / 1000)})` });
|
|
3249
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3250
|
+
status: "info",
|
|
3251
|
+
type: "voice_transcribed",
|
|
3252
|
+
prompt: preview,
|
|
3253
|
+
detail: result.backend,
|
|
3254
|
+
durationMs: result.durationMs,
|
|
3255
|
+
});
|
|
2818
3256
|
}
|
|
2819
3257
|
catch (error) {
|
|
2820
3258
|
const note = "Voice uses faster-whisper/parakeet locally or OPENAI_API_KEY for cloud transcription, not CODEX_API_KEY.";
|
|
2821
3259
|
await safeReply(ctx, `<b>Transcription failed:</b>\n${escapeHTML(friendlyErrorText(error))}\n\n<i>${escapeHTML(note)}</i>`, {
|
|
2822
3260
|
fallbackText: `Transcription failed:\n${friendlyErrorText(error)}\n\n${note}`,
|
|
2823
3261
|
});
|
|
3262
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3263
|
+
status: "failed",
|
|
3264
|
+
type: "voice_transcription_failed",
|
|
3265
|
+
detail: friendlyErrorText(error),
|
|
3266
|
+
});
|
|
2824
3267
|
return;
|
|
2825
3268
|
}
|
|
2826
3269
|
finally {
|
|
@@ -2915,6 +3358,11 @@ export function createBot(config, registry) {
|
|
|
2915
3358
|
if (caption) {
|
|
2916
3359
|
promptInput.text = caption;
|
|
2917
3360
|
}
|
|
3361
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3362
|
+
status: "info",
|
|
3363
|
+
type: "attachment_staged",
|
|
3364
|
+
detail: stagedPhoto.safeName,
|
|
3365
|
+
});
|
|
2918
3366
|
await setReaction(ctx, "👀");
|
|
2919
3367
|
try {
|
|
2920
3368
|
await handleUserPrompt(ctx, contextKey, chatId, session, toPromptEnvelope(promptInput, outDir));
|
|
@@ -2997,6 +3445,11 @@ export function createBot(config, registry) {
|
|
|
2997
3445
|
await safeReply(ctx, `📎 <b>Received:</b> <code>${escapeHTML(stagedFile.safeName)}</code>`, {
|
|
2998
3446
|
fallbackText: `📎 Received: ${stagedFile.safeName}`,
|
|
2999
3447
|
});
|
|
3448
|
+
appendTelegramActivity(ctx, contextKey, session, {
|
|
3449
|
+
status: "info",
|
|
3450
|
+
type: "attachment_staged",
|
|
3451
|
+
detail: stagedFile.safeName,
|
|
3452
|
+
});
|
|
3000
3453
|
// Keep typing visible during the gap between staging and prompt execution
|
|
3001
3454
|
await sendChatActionSafe(ctx.api, chatId, "typing", ctx.message?.message_thread_id).catch(() => { });
|
|
3002
3455
|
const outDir = outboxPath(workspace, turnId);
|