@llblab/pi-telegram 0.24.11 → 0.25.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.
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Bridge-owned Telegram activity verbosity projection
3
+ * Zones: telegram activity, rich rendering, operational delivery
4
+ * Owns ephemeral reasoning drafts and bounded durable tool disclosures; excludes activity normalization, assistant answer rendering, and transport authority policy
5
+ */
6
+
7
+ import type { TelegramActivityEvent } from "./activity.ts";
8
+ import { escapeHtml } from "./rendering.ts";
9
+ import type {
10
+ TelegramEditMessageTextBody,
11
+ TelegramRichText,
12
+ TelegramSendMessageBody,
13
+ TelegramSendRichMessageDraftBody,
14
+ TelegramSentMessage,
15
+ } from "./telegram-api.ts";
16
+ import type { TelegramTarget } from "./target.ts";
17
+
18
+ export const TELEGRAM_TOOL_ACTIVITY_ICON = "🛠";
19
+ export const TELEGRAM_ACTIVITY_DETAIL_MAX_CHARS = 1_200;
20
+ export const TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS = 3_900;
21
+ export const TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS = 6;
22
+ export const TELEGRAM_REASONING_DRAFT_MAX_FRAMES = 24;
23
+ export const TELEGRAM_REASONING_BUFFER_MAX_CHARS = 1_200;
24
+ export const TELEGRAM_TOOL_UPDATE_MAX_ENTRIES = 4;
25
+
26
+ interface ToolActivity {
27
+ id: string;
28
+ name: string;
29
+ args: string;
30
+ updates: string[];
31
+ droppedUpdates: number;
32
+ result?: string;
33
+ isError?: boolean;
34
+ complete: boolean;
35
+ }
36
+
37
+ interface ToolMessage {
38
+ messageId: number;
39
+ tools: ToolActivity[];
40
+ target: TelegramTarget;
41
+ }
42
+
43
+ function targetEquals(left: TelegramTarget, right: TelegramTarget): boolean {
44
+ return left.chatId === right.chatId && left.threadId === right.threadId;
45
+ }
46
+
47
+ function redactActivityText(text: string): string {
48
+ return text
49
+ .replace(/\b\d{8,12}:[A-Za-z0-9_-]{30,}\b/g, "[REDACTED_BOT_TOKEN]")
50
+ .replace(
51
+ /\b(Bearer\s+)[A-Za-z0-9._~+/=-]{16,}\b/gi,
52
+ "$1[REDACTED]",
53
+ )
54
+ .replace(
55
+ /(["']?(?:api[_-]?key|token|password|secret)["']?\s*[:=]\s*["']?)[^"',\s}]+/gi,
56
+ "$1[REDACTED]",
57
+ );
58
+ }
59
+
60
+ function renderReasoningRichText(text: string): TelegramRichText {
61
+ const parts: TelegramRichText[] = [];
62
+ let cursor = 0;
63
+ while (cursor < text.length) {
64
+ const codeStart = text.indexOf("`", cursor);
65
+ const boldStart = text.indexOf("**", cursor);
66
+ const starts = [codeStart, boldStart].filter((index) => index >= 0);
67
+ if (starts.length === 0) {
68
+ parts.push(text.slice(cursor));
69
+ break;
70
+ }
71
+ const start = Math.min(...starts);
72
+ if (start > cursor) parts.push(text.slice(cursor, start));
73
+ const marker = start === codeStart ? "`" : "**";
74
+ const end = text.indexOf(marker, start + marker.length);
75
+ if (end < 0) {
76
+ parts.push(text.slice(start));
77
+ break;
78
+ }
79
+ parts.push({
80
+ type: marker === "`" ? "code" : "bold",
81
+ text: text.slice(start + marker.length, end),
82
+ });
83
+ cursor = end + marker.length;
84
+ }
85
+ if (parts.length === 0) return "";
86
+ return parts.length === 1 ? parts[0]! : parts;
87
+ }
88
+
89
+ function serializeActivityValue(value: unknown): string {
90
+ const seen = new WeakSet<object>();
91
+ let text: string;
92
+ try {
93
+ text =
94
+ JSON.stringify(
95
+ value,
96
+ (_key, nested) => {
97
+ if (typeof nested === "bigint") return nested.toString();
98
+ if (nested && typeof nested === "object") {
99
+ if (seen.has(nested)) return "[Circular]";
100
+ seen.add(nested);
101
+ }
102
+ return nested;
103
+ },
104
+ 2,
105
+ ) ?? JSON.stringify(String(value));
106
+ } catch {
107
+ text = JSON.stringify(String(value));
108
+ }
109
+ const redacted = redactActivityText(text);
110
+ if (redacted.length <= TELEGRAM_ACTIVITY_DETAIL_MAX_CHARS) return redacted;
111
+ const omitted = redacted.length - TELEGRAM_ACTIVITY_DETAIL_MAX_CHARS;
112
+ return `${redacted.slice(0, TELEGRAM_ACTIVITY_DETAIL_MAX_CHARS)}\n… [${omitted} chars truncated]`;
113
+ }
114
+
115
+ function renderToolActivityHtml(tool: ToolActivity): string {
116
+ const evidence = [`arguments: ${tool.args}`];
117
+ if (tool.droppedUpdates > 0) {
118
+ evidence.push(`… [${tool.droppedUpdates} earlier updates omitted]`);
119
+ }
120
+ tool.updates.forEach((update, index) => {
121
+ evidence.push(
122
+ `update ${tool.droppedUpdates + index + 1}: ${update}`,
123
+ );
124
+ });
125
+ if (tool.complete && tool.result !== undefined) {
126
+ evidence.push(`${tool.isError ? "error" : "result"}: ${tool.result}`);
127
+ }
128
+ const status = tool.complete
129
+ ? tool.isError
130
+ ? "failed"
131
+ : "done"
132
+ : "running";
133
+ return [
134
+ `<b>${TELEGRAM_TOOL_ACTIVITY_ICON}&#160; ${escapeHtml(tool.name)}:</b> <code>${status}</code>`,
135
+ `<blockquote expandable>${escapeHtml(evidence.join("\n\n"))}</blockquote>`,
136
+ ].join("\n");
137
+ }
138
+
139
+ export function renderTelegramToolActivityHtml(
140
+ tools: readonly ToolActivity[],
141
+ ): string {
142
+ return tools.map(renderToolActivityHtml).join("\n\n");
143
+ }
144
+
145
+ function toolMessageSize(tools: readonly ToolActivity[]): number {
146
+ return renderTelegramToolActivityHtml(tools).length;
147
+ }
148
+
149
+ function draftIdForActivity(activityId: string): number {
150
+ let hash = 2166136261;
151
+ for (const character of activityId) {
152
+ hash ^= character.charCodeAt(0);
153
+ hash = Math.imul(hash, 16777619);
154
+ }
155
+ return (hash >>> 0) || 1;
156
+ }
157
+
158
+ export interface TelegramActivityVerbosityRuntime {
159
+ accept: (event: TelegramActivityEvent) => void;
160
+ reset: () => void;
161
+ stop: () => void;
162
+ waitForIdle: () => Promise<void>;
163
+ }
164
+
165
+ export function createTelegramActivityVerbosityRuntime<TAuthority>(deps: {
166
+ isVerbose: () => boolean;
167
+ resolveTarget: (event: TelegramActivityEvent) => TelegramTarget | undefined;
168
+ captureAuthority: () => TAuthority;
169
+ isAuthorityActive: (authority: TAuthority) => boolean;
170
+ sendMessage: (body: TelegramSendMessageBody) => Promise<TelegramSentMessage>;
171
+ sendRichMessageDraft: (
172
+ body: TelegramSendRichMessageDraftBody,
173
+ ) => Promise<boolean>;
174
+ editMessageText: (
175
+ body: TelegramEditMessageTextBody,
176
+ ) => Promise<"edited" | "unchanged">;
177
+ recordFailure?: (
178
+ operation: "reasoning-draft" | "tool-send" | "tool-edit",
179
+ event: TelegramActivityEvent,
180
+ error: unknown,
181
+ ) => void;
182
+ }): TelegramActivityVerbosityRuntime {
183
+ let active = true;
184
+ let generation = 0;
185
+ let tail = Promise.resolve();
186
+ let activityId: string | undefined;
187
+ let authority: TAuthority | undefined;
188
+ let target: TelegramTarget | undefined;
189
+ let reasoningBuffer = "";
190
+ let reasoningChars = 0;
191
+ let reasoningDraftFrames = 0;
192
+ let lastReasoningDraftChars = 0;
193
+ let toolMessage: ToolMessage | undefined;
194
+ const tools = new Map<string, ToolActivity>();
195
+ const toolOrder: string[] = [];
196
+
197
+ const clearActivity = () => {
198
+ activityId = undefined;
199
+ authority = undefined;
200
+ target = undefined;
201
+ reasoningBuffer = "";
202
+ reasoningChars = 0;
203
+ reasoningDraftFrames = 0;
204
+ lastReasoningDraftChars = 0;
205
+ toolMessage = undefined;
206
+ tools.clear();
207
+ toolOrder.length = 0;
208
+ };
209
+ const hasAuthority = (): boolean =>
210
+ authority !== undefined && deps.isAuthorityActive(authority);
211
+ const ensureActivity = (event: TelegramActivityEvent): boolean => {
212
+ if (!deps.isVerbose()) return false;
213
+ if (activityId === event.activityId) return hasAuthority();
214
+ clearActivity();
215
+ const resolvedTarget = deps.resolveTarget(event);
216
+ if (!resolvedTarget) return false;
217
+ activityId = event.activityId;
218
+ target = { ...resolvedTarget };
219
+ authority = deps.captureAuthority();
220
+ return hasAuthority();
221
+ };
222
+ const closeToolBatch = () => {
223
+ toolMessage = undefined;
224
+ };
225
+ const sendReasoningDraft = async (
226
+ event: TelegramActivityEvent,
227
+ acceptedGeneration: number,
228
+ ) => {
229
+ if (
230
+ generation !== acceptedGeneration ||
231
+ !target ||
232
+ !hasAuthority()
233
+ ) {
234
+ return;
235
+ }
236
+ const omitted = reasoningChars - reasoningBuffer.length;
237
+ const text =
238
+ omitted > 0
239
+ ? `… [${omitted} earlier chars omitted]\n${reasoningBuffer}`
240
+ : reasoningBuffer;
241
+ try {
242
+ await deps.sendRichMessageDraft({
243
+ chat_id: target.chatId,
244
+ ...(target.threadId === undefined
245
+ ? {}
246
+ : { message_thread_id: target.threadId }),
247
+ draft_id: draftIdForActivity(event.activityId),
248
+ rich_message: {
249
+ blocks: [
250
+ {
251
+ type: "thinking",
252
+ text: renderReasoningRichText(redactActivityText(text)),
253
+ },
254
+ ],
255
+ skip_entity_detection: true,
256
+ },
257
+ });
258
+ if (generation !== acceptedGeneration) return;
259
+ reasoningDraftFrames += 1;
260
+ lastReasoningDraftChars = reasoningChars;
261
+ } catch (error) {
262
+ deps.recordFailure?.("reasoning-draft", event, error);
263
+ }
264
+ };
265
+ const publishTool = async (
266
+ event: TelegramActivityEvent,
267
+ tool: ToolActivity,
268
+ acceptedGeneration: number,
269
+ ) => {
270
+ if (
271
+ generation !== acceptedGeneration ||
272
+ !target ||
273
+ !hasAuthority()
274
+ ) {
275
+ return;
276
+ }
277
+ const canAppend =
278
+ toolMessage &&
279
+ targetEquals(toolMessage.target, target) &&
280
+ toolMessage.tools.length < TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS &&
281
+ toolMessageSize([...toolMessage.tools, tool]) <=
282
+ TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS;
283
+ try {
284
+ if (canAppend && toolMessage) {
285
+ const nextTools = [...toolMessage.tools, tool];
286
+ await deps.editMessageText({
287
+ chat_id: target.chatId,
288
+ message_id: toolMessage.messageId,
289
+ text: renderTelegramToolActivityHtml(nextTools),
290
+ parse_mode: "HTML",
291
+ });
292
+ if (generation !== acceptedGeneration) return;
293
+ toolMessage.tools = nextTools;
294
+ return;
295
+ }
296
+ const sent = await deps.sendMessage({
297
+ chat_id: target.chatId,
298
+ ...(target.threadId === undefined
299
+ ? {}
300
+ : { message_thread_id: target.threadId }),
301
+ text: renderTelegramToolActivityHtml([tool]),
302
+ parse_mode: "HTML",
303
+ });
304
+ if (generation !== acceptedGeneration) return;
305
+ toolMessage = {
306
+ messageId: sent.message_id,
307
+ tools: [tool],
308
+ target: { ...target },
309
+ };
310
+ } catch (error) {
311
+ deps.recordFailure?.(canAppend ? "tool-edit" : "tool-send", event, error);
312
+ closeToolBatch();
313
+ }
314
+ };
315
+ const process = async (
316
+ event: TelegramActivityEvent,
317
+ acceptedGeneration: number,
318
+ ) => {
319
+ if (!ensureActivity(event)) {
320
+ if (activityId === event.activityId && !deps.isVerbose()) clearActivity();
321
+ return;
322
+ }
323
+ if (
324
+ event.type === "assistant-text-delta" ||
325
+ event.type === "assistant-segment" ||
326
+ event.type === "reasoning-delta" ||
327
+ event.type === "reasoning-end"
328
+ ) {
329
+ closeToolBatch();
330
+ }
331
+ if (event.type === "reasoning-delta") {
332
+ reasoningChars += event.delta.length;
333
+ reasoningBuffer = `${reasoningBuffer}${event.delta}`.slice(
334
+ -TELEGRAM_REASONING_BUFFER_MAX_CHARS,
335
+ );
336
+ if (
337
+ reasoningDraftFrames < TELEGRAM_REASONING_DRAFT_MAX_FRAMES &&
338
+ (reasoningDraftFrames === 0 ||
339
+ reasoningChars - lastReasoningDraftChars >= 160)
340
+ ) {
341
+ await sendReasoningDraft(event, acceptedGeneration);
342
+ }
343
+ return;
344
+ }
345
+ if (event.type === "reasoning-end") {
346
+ if (
347
+ reasoningChars > lastReasoningDraftChars &&
348
+ reasoningDraftFrames < TELEGRAM_REASONING_DRAFT_MAX_FRAMES
349
+ ) {
350
+ await sendReasoningDraft(event, acceptedGeneration);
351
+ }
352
+ reasoningBuffer = "";
353
+ reasoningChars = 0;
354
+ return;
355
+ }
356
+ if (event.type === "tool-start") {
357
+ tools.set(event.toolCallId, {
358
+ id: event.toolCallId,
359
+ name: event.toolName,
360
+ args: serializeActivityValue(event.args),
361
+ updates: [],
362
+ droppedUpdates: 0,
363
+ complete: false,
364
+ });
365
+ toolOrder.push(event.toolCallId);
366
+ return;
367
+ }
368
+ if (event.type === "tool-update") {
369
+ const tool = tools.get(event.toolCallId);
370
+ if (!tool) return;
371
+ tool.updates.push(serializeActivityValue(event.update));
372
+ if (tool.updates.length > TELEGRAM_TOOL_UPDATE_MAX_ENTRIES) {
373
+ tool.updates.shift();
374
+ tool.droppedUpdates += 1;
375
+ }
376
+ return;
377
+ }
378
+ if (event.type === "tool-end") {
379
+ const tool = tools.get(event.toolCallId) ?? {
380
+ id: event.toolCallId,
381
+ name: event.toolName,
382
+ args: serializeActivityValue(undefined),
383
+ updates: [],
384
+ droppedUpdates: 0,
385
+ complete: false,
386
+ };
387
+ if (!tools.has(event.toolCallId)) toolOrder.push(event.toolCallId);
388
+ tool.result = serializeActivityValue(event.result);
389
+ tool.isError = event.isError;
390
+ tool.complete = true;
391
+ tools.set(event.toolCallId, tool);
392
+ while (toolOrder.length > 0) {
393
+ const next = tools.get(toolOrder[0]!);
394
+ if (!next?.complete) break;
395
+ toolOrder.shift();
396
+ tools.delete(next.id);
397
+ await publishTool(event, next, acceptedGeneration);
398
+ if (generation !== acceptedGeneration) return;
399
+ }
400
+ return;
401
+ }
402
+ if (event.type === "agent-end" || event.type === "agent-settled") {
403
+ clearActivity();
404
+ }
405
+ };
406
+ return {
407
+ accept(event) {
408
+ if (!active) return;
409
+ const acceptedGeneration = generation;
410
+ tail = tail
411
+ .then(() => {
412
+ if (!active || generation !== acceptedGeneration) return;
413
+ return process(event, acceptedGeneration);
414
+ })
415
+ .catch((error) => {
416
+ deps.recordFailure?.("tool-send", event, error);
417
+ });
418
+ },
419
+ reset() {
420
+ generation += 1;
421
+ clearActivity();
422
+ tail = Promise.resolve();
423
+ },
424
+ stop() {
425
+ active = false;
426
+ generation += 1;
427
+ clearActivity();
428
+ tail = Promise.resolve();
429
+ },
430
+ waitForIdle() {
431
+ return tail;
432
+ },
433
+ };
434
+ }
package/lib/bindings.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import * as Activity from "./activity.ts";
8
+ import type { TelegramActivityVerbosityRuntime } from "./activity-verbosity.ts";
8
9
  import * as CommandTemplates from "./command-templates.ts";
9
10
  import * as Commands from "./commands.ts";
10
11
  import * as Config from "./config.ts";
@@ -61,19 +62,24 @@ export function createTelegramAssistantOutputBindingRuntime<
61
62
  sender: Parameters<
62
63
  typeof OutboundHandlers.createTelegramAssistantOutputSender<TTransportStamp>
63
64
  >[0];
65
+ waitForActivityIdle?: () => Promise<void>;
64
66
  recordRuntimeEvent: TelegramRuntimeEventRecorder;
65
67
  }): TelegramAssistantOutputBindingRuntime<TTransportStamp> {
66
68
  const authority = Routing.createTelegramAssistantOutputAuthorityRuntime(
67
69
  deps.authority,
68
70
  );
69
- const send =
71
+ const sendOutput =
70
72
  OutboundHandlers.createTelegramAssistantOutputSender<TTransportStamp>(
71
73
  deps.sender,
72
74
  );
73
75
  const runtime = Activity.createTelegramAssistantOutputRuntime({
74
76
  isEnabled: deps.isEnabled,
75
77
  ...authority,
76
- send,
78
+ async send(event, authority, isAuthorityActive) {
79
+ await deps.waitForActivityIdle?.();
80
+ if (!isAuthorityActive()) return;
81
+ await sendOutput(event, authority, isAuthorityActive);
82
+ },
77
83
  recordFailure(event, error) {
78
84
  deps.recordRuntimeEvent("proactive-push", error, {
79
85
  activityId: event.activityId,
@@ -278,6 +284,7 @@ export function registerTelegramCommandsAndTools({
278
284
  interface TelegramLifecycleBindingDeps {
279
285
  pi: Pi.ExtensionAPI;
280
286
  activityRuntime: Activity.TelegramActivityRuntime;
287
+ activityVerbosityRuntime?: TelegramActivityVerbosityRuntime;
281
288
  assistantOutputRuntime: Pick<
282
289
  Activity.TelegramAssistantOutputRuntime,
283
290
  "start" | "stop"
@@ -363,6 +370,7 @@ interface TelegramLifecycleBindingDeps {
363
370
  export function registerTelegramLifecycleRuntimeHooks({
364
371
  pi,
365
372
  activityRuntime,
373
+ activityVerbosityRuntime,
366
374
  assistantOutputRuntime,
367
375
  sessionLifecycleRuntime,
368
376
  configStore,
@@ -556,6 +564,7 @@ export function registerTelegramLifecycleRuntimeHooks({
556
564
  isSessionActive: isSessionContextActive,
557
565
  isTurnTransportActive,
558
566
  waitForTypingIdle: typing.waitForIdle,
567
+ waitForActivityIdle: activityVerbosityRuntime?.waitForIdle,
559
568
  dispatchNextQueuedTelegramTurn,
560
569
  requestDeferredDispatchNextQueuedTelegramTurn:
561
570
  deferredQueueDispatchRuntime.request,
@@ -638,6 +647,7 @@ export function registerTelegramLifecycleRuntimeHooks({
638
647
  previewRuntime.invalidate();
639
648
  assistantOutputRuntime.start();
640
649
  activityRuntime.onSessionStart?.();
650
+ activityVerbosityRuntime?.reset();
641
651
  modelContextAvailabilityRuntime.reconcile();
642
652
  await sessionLifecycleRuntime.onSessionStart(event, ctx);
643
653
  },
@@ -645,6 +655,7 @@ export function registerTelegramLifecycleRuntimeHooks({
645
655
  if (!isSessionContextActive(ctx)) return;
646
656
  agentLifecycleHooks.clearRetainedAgentEnd();
647
657
  activityRuntime.onSessionShutdown();
658
+ activityVerbosityRuntime?.reset();
648
659
  assistantOutputRuntime.stop();
649
660
  compactionObserver.onSessionShutdown();
650
661
  if (event.reason === "quit" && disconnectOnQuit) {
package/lib/config.ts CHANGED
@@ -55,6 +55,7 @@ export interface ResolvedTelegramTimeConfig {
55
55
  }
56
56
 
57
57
  export type TelegramAssistantRenderingMode = "rich" | "html";
58
+ export type TelegramActivityVerbosity = "quiet" | "verbose";
58
59
 
59
60
  export interface TelegramConfig {
60
61
  /** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
@@ -74,6 +75,9 @@ export interface TelegramConfig {
74
75
  draftPreviews?: boolean;
75
76
  rendering?: TelegramAssistantRenderingMode;
76
77
  proactivePush?: boolean;
78
+ activity?: TelegramActivityVerbosity;
79
+ /** @deprecated use activity */
80
+ activityVerbosity?: TelegramActivityVerbosity;
77
81
  };
78
82
  /** @deprecated use assistant.draftPreviews */
79
83
  draftPreviews?: boolean;
@@ -678,7 +682,7 @@ export function createTelegramProactivePushSetter(
678
682
  return async (enabled) => {
679
683
  await loadLatestTelegramConfig(configStore);
680
684
  const current = configStore.get();
681
- const config = {
685
+ const config: TelegramConfig = {
682
686
  ...current,
683
687
  assistant: { ...current.assistant, proactivePush: enabled },
684
688
  };
@@ -746,6 +750,40 @@ export function createTelegramAssistantRenderingModeSetter(
746
750
  };
747
751
  }
748
752
 
753
+ export function createTelegramActivityVerbosityGetter(
754
+ configStore: Pick<TelegramConfigStore, "get">,
755
+ ): () => TelegramActivityVerbosity {
756
+ return () => {
757
+ const assistant = configStore.get().assistant;
758
+ if (assistant?.activity !== undefined) {
759
+ return assistant.activity === "verbose" ? "verbose" : "quiet";
760
+ }
761
+ return assistant?.activityVerbosity === "verbose" ? "verbose" : "quiet";
762
+ };
763
+ }
764
+
765
+ export function createTelegramActivityVerbositySetter(
766
+ configStore: TelegramMutableConfigStore,
767
+ ): (verbosity: TelegramActivityVerbosity) => Promise<void> {
768
+ return async (verbosity) => {
769
+ await loadLatestTelegramConfig(configStore);
770
+ const current = configStore.get();
771
+ const {
772
+ activityVerbosity: _legacyActivityVerbosity,
773
+ ...assistant
774
+ } = current.assistant ?? {};
775
+ const config = {
776
+ ...current,
777
+ assistant: {
778
+ ...assistant,
779
+ activity: verbosity,
780
+ },
781
+ };
782
+ configStore.set(config);
783
+ await configStore.persist(config);
784
+ };
785
+ }
786
+
749
787
  export function createTelegramVoiceReplyModeGetter(
750
788
  configStore: Pick<TelegramConfigStore, "get">,
751
789
  ): () => "hidden" | "mirror" | "always" {
@@ -919,6 +957,10 @@ export function createTelegramConfigControls(
919
957
  createTelegramAssistantRenderingModeGetter(configStore),
920
958
  setAssistantRenderingMode:
921
959
  createTelegramAssistantRenderingModeSetter(configStore),
960
+ getActivityVerbosity:
961
+ createTelegramActivityVerbosityGetter(configStore),
962
+ setActivityVerbosity:
963
+ createTelegramActivityVerbositySetter(configStore),
922
964
  getVoiceReplyMode: createTelegramVoiceReplyModeGetter(configStore),
923
965
  isVoiceReplyModeConfigured:
924
966
  createTelegramVoiceReplyModeConfiguredChecker(configStore),