@llblab/pi-telegram 0.23.3 → 0.24.1

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.
@@ -36,9 +36,7 @@ export interface CommandTemplateObjectConfig {
36
36
  }
37
37
 
38
38
  export type CommandTemplateValue =
39
- | string
40
- | CommandTemplateConfig[]
41
- | CommandTemplateObjectConfig;
39
+ string | CommandTemplateConfig[] | CommandTemplateObjectConfig;
42
40
 
43
41
  export type CommandTemplateConfig = string | CommandTemplateObjectConfig;
44
42
 
@@ -90,12 +88,6 @@ const COMMAND_TEMPLATE_RISK_LABEL_ORDER: CommandTemplateRiskLabel[] = [
90
88
  "risk.platform_specific",
91
89
  ];
92
90
 
93
- export type CommandTemplateExecCommand = (
94
- command: string,
95
- args: string[],
96
- options?: CommandTemplateExecOptions,
97
- ) => Promise<CommandTemplateExecResult>;
98
-
99
91
  function normalizeCommandTemplateArgs(value: string[] | undefined): string[] {
100
92
  if (!Array.isArray(value)) return [];
101
93
  return value.map(String).map((item) => item.trim());
@@ -257,9 +249,18 @@ function getLeafCommandTemplateRiskLabels(
257
249
  labels.add("risk.broad_fs_write");
258
250
  }
259
251
  if (
260
- ["curl", "wget", "ssh", "scp", "sftp", "rsync", "nc", "ncat", "telnet", "ftp"].includes(
261
- command,
262
- ) ||
252
+ [
253
+ "curl",
254
+ "wget",
255
+ "ssh",
256
+ "scp",
257
+ "sftp",
258
+ "rsync",
259
+ "nc",
260
+ "ncat",
261
+ "telnet",
262
+ "ftp",
263
+ ].includes(command) ||
263
264
  (command === "git" &&
264
265
  hasAnyArg(args, ["clone", "fetch", "pull", "push", "ls-remote"])) ||
265
266
  ["npm", "pnpm", "yarn", "pip", "cargo"].includes(command)
@@ -283,13 +284,32 @@ function getLeafCommandTemplateRiskLabels(
283
284
  labels.add("risk.long_running");
284
285
  }
285
286
  if (
286
- ["systemctl", "launchctl", "osascript", "open", "xdg-open", "powershell", "pwsh", "cmd.exe", "apt", "apt-get", "dnf", "yum", "brew", "pacman", "apk", "xclip", "wl-copy"].includes(
287
- command,
288
- )
287
+ [
288
+ "systemctl",
289
+ "launchctl",
290
+ "osascript",
291
+ "open",
292
+ "xdg-open",
293
+ "powershell",
294
+ "pwsh",
295
+ "cmd.exe",
296
+ "apt",
297
+ "apt-get",
298
+ "dnf",
299
+ "yum",
300
+ "brew",
301
+ "pacman",
302
+ "apk",
303
+ "xclip",
304
+ "wl-copy",
305
+ ].includes(command)
289
306
  ) {
290
307
  labels.add("risk.platform_specific");
291
308
  }
292
- if (["pass", "gpg", "ssh-add"].includes(command) || hasSecretTouchingText(parts)) {
309
+ if (
310
+ ["pass", "gpg", "ssh-add"].includes(command) ||
311
+ hasSecretTouchingText(parts)
312
+ ) {
293
313
  labels.add("risk.secret_touching");
294
314
  }
295
315
  return sortRiskLabels(labels);
@@ -347,10 +367,6 @@ function pad(value: number, width: number): string {
347
367
  return String(value).padStart(width, "0");
348
368
  }
349
369
 
350
- export function isCommandTemplateRepeatPlaceholder(name: string): boolean {
351
- return /^_{0,6}(?:index|prev|next|repeat)$/.test(name);
352
- }
353
-
354
370
  export function getCommandTemplateRepeatDefaults(
355
371
  index: number,
356
372
  repeat: number,
package/lib/commands.ts CHANGED
@@ -4,7 +4,10 @@
4
4
  * Owns Telegram slash-command normalization, bot command metadata, and pi-side command registration behind runtime ports
5
5
  */
6
6
 
7
- import { pairTelegramUserIfNeeded } from "./config.ts";
7
+ import {
8
+ pairTelegramUserIfNeeded,
9
+ TELEGRAM_DEFAULT_PROFILE_NAME,
10
+ } from "./config.ts";
8
11
  import type { ExtensionAPI, ExtensionCommandContext } from "./pi.ts";
9
12
  import type { TelegramBridgeStatusLineOptions } from "./status.ts";
10
13
  import {
@@ -321,7 +324,7 @@ function parseTelegramProfileArg(args: string): string | undefined {
321
324
  const word = args.trim().split(/\s+/)[0];
322
325
  if (!word || word.length === 0) return undefined;
323
326
  if (word.startsWith("-")) return undefined;
324
- return word;
327
+ return word === TELEGRAM_DEFAULT_PROFILE_NAME ? undefined : word;
325
328
  }
326
329
 
327
330
  function formatTelegramTakeoverTitle(ctx: ExtensionCommandContext): string {
package/lib/config.ts CHANGED
@@ -15,7 +15,12 @@ import {
15
15
  writeFileSync,
16
16
  } from "node:fs";
17
17
  import { chmod, mkdir, rename, writeFile } from "node:fs/promises";
18
- import { resolveAgentDir, resolveTelegramConfigPath } from "./paths.ts";
18
+ import {
19
+ resolveAgentDir,
20
+ resolveTelegramConfigPath,
21
+ TELEGRAM_DEFAULT_PROFILE_NAME,
22
+ } from "./paths.ts";
23
+ export { TELEGRAM_DEFAULT_PROFILE_NAME } from "./paths.ts";
19
24
 
20
25
  import type { CommandTemplateObjectConfig } from "./command-templates.ts";
21
26
  import type { TelegramInboundHandlerConfig } from "./inbound.ts";
@@ -52,10 +57,15 @@ export interface ResolvedTelegramTimeConfig {
52
57
  export type TelegramAssistantRenderingMode = "rich" | "html";
53
58
 
54
59
  export interface TelegramConfig {
60
+ /** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
55
61
  botToken?: string;
62
+ /** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
56
63
  botUsername?: string;
64
+ /** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
57
65
  botId?: number;
66
+ /** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
58
67
  allowedUserId?: number;
68
+ /** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
59
69
  lastUpdateId?: number;
60
70
  inboundHandlers?: TelegramInboundHandlerConfig[];
61
71
  attachmentHandlers?: TelegramInboundHandlerConfig[];
@@ -77,7 +87,7 @@ export interface TelegramConfig {
77
87
  sendTranscript?: boolean;
78
88
  };
79
89
  time?: TelegramTimeConfig;
80
- /** Named bot/session profiles (e.g. "work", "omp"). */
90
+ /** Canonical bot/session profiles, including profiles.default. */
81
91
  profiles?: Record<string, TelegramBotProfile>;
82
92
  }
83
93
 
@@ -98,7 +108,6 @@ export interface TelegramBotProfile {
98
108
  /** Profile names must contain only lowercase ASCII letters and digits; max 32 chars. */
99
109
  const TELEGRAM_PROFILE_NAME_PATTERN = /^[a-z0-9]{1,32}$/;
100
110
  const TELEGRAM_RESERVED_PROFILE_NAMES: ReadonlySet<string> = new Set([
101
- "default",
102
111
  "main",
103
112
  "active",
104
113
  ]);
@@ -110,41 +119,6 @@ export function isValidTelegramProfileName(name: string): boolean {
110
119
  );
111
120
  }
112
121
 
113
- /**
114
- * Resolve the effective config for a named (or default) profile.
115
- * Returns bot/session fields from the named profile, falling back to
116
- * top-level fields for the default profile. Shared bridge settings
117
- * always come from the top level.
118
- */
119
- export function resolveTelegramActiveProfile(
120
- config: TelegramConfig,
121
- profileName?: string,
122
- ): {
123
- botToken?: string;
124
- botUsername?: string;
125
- botId?: number;
126
- allowedUserId?: number;
127
- lastUpdateId?: number;
128
- } {
129
- if (!profileName || !config.profiles?.[profileName]) {
130
- return {
131
- botToken: config.botToken,
132
- botUsername: config.botUsername,
133
- botId: config.botId,
134
- allowedUserId: config.allowedUserId,
135
- lastUpdateId: config.lastUpdateId,
136
- };
137
- }
138
- const profile = config.profiles[profileName];
139
- return {
140
- botToken: profile.botToken,
141
- botUsername: profile.botUsername,
142
- botId: profile.botId,
143
- allowedUserId: profile.allowedUserId,
144
- lastUpdateId: profile.lastUpdateId,
145
- };
146
- }
147
-
148
122
  /** List defined profile names. */
149
123
  export function getTelegramProfileNames(config: TelegramConfig): string[] {
150
124
  return Object.keys(config.profiles ?? {}).sort();
@@ -154,6 +128,7 @@ export interface TelegramConfigStore {
154
128
  get: () => TelegramConfig;
155
129
  getStoredConfig: () => TelegramConfig;
156
130
  set: (config: TelegramConfig) => void;
131
+ setProfile: (profileName: string, profile: TelegramBotProfile) => void;
157
132
  update: (mutate: (config: TelegramConfig) => void) => void;
158
133
  activateProfile: (profileName: string | undefined) => boolean;
159
134
  getActiveProfileName: () => string | undefined;
@@ -396,10 +371,91 @@ export function getTelegramProfileFields(
396
371
  if (!token) return undefined;
397
372
  return {
398
373
  botToken: token,
399
- botUsername: config.botUsername,
400
- botId: config.botId,
401
- allowedUserId: config.allowedUserId,
402
- lastUpdateId: config.lastUpdateId,
374
+ ...(config.botUsername !== undefined
375
+ ? { botUsername: config.botUsername }
376
+ : {}),
377
+ ...(config.botId !== undefined ? { botId: config.botId } : {}),
378
+ ...(config.allowedUserId !== undefined
379
+ ? { allowedUserId: config.allowedUserId }
380
+ : {}),
381
+ ...(config.lastUpdateId !== undefined
382
+ ? { lastUpdateId: config.lastUpdateId }
383
+ : {}),
384
+ };
385
+ }
386
+
387
+ function omitTelegramRootProfileFields(config: TelegramConfig): TelegramConfig {
388
+ const {
389
+ botToken: _botToken,
390
+ botUsername: _botUsername,
391
+ botId: _botId,
392
+ allowedUserId: _allowedUserId,
393
+ lastUpdateId: _lastUpdateId,
394
+ ...sharedConfig
395
+ } = config;
396
+ return sharedConfig;
397
+ }
398
+
399
+ export function normalizeTelegramDefaultProfileConfig(config: TelegramConfig): {
400
+ config: TelegramConfig;
401
+ changed: boolean;
402
+ } {
403
+ const hasLegacyRootProfile = [
404
+ "botToken",
405
+ "botUsername",
406
+ "botId",
407
+ "allowedUserId",
408
+ "lastUpdateId",
409
+ ].some((field) => Object.hasOwn(config, field));
410
+ if (!hasLegacyRootProfile) return { config, changed: false };
411
+ const canonicalProfile = config.profiles?.[TELEGRAM_DEFAULT_PROFILE_NAME];
412
+ const legacyToken = config.botToken?.trim();
413
+ if (Object.hasOwn(config, "botToken") && !legacyToken) {
414
+ throw new Error("Legacy Telegram default profile has no bot token");
415
+ }
416
+ const legacyProfile: Partial<TelegramBotProfile> = {
417
+ ...(legacyToken ? { botToken: legacyToken } : {}),
418
+ ...(config.botUsername !== undefined
419
+ ? { botUsername: config.botUsername }
420
+ : {}),
421
+ ...(config.botId !== undefined ? { botId: config.botId } : {}),
422
+ ...(config.allowedUserId !== undefined
423
+ ? { allowedUserId: config.allowedUserId }
424
+ : {}),
425
+ ...(config.lastUpdateId !== undefined
426
+ ? { lastUpdateId: config.lastUpdateId }
427
+ : {}),
428
+ };
429
+ if (!canonicalProfile && !legacyToken) {
430
+ throw new Error("Legacy Telegram default profile has no bot token");
431
+ }
432
+ const hasConflict = canonicalProfile
433
+ ? Object.entries(legacyProfile).some(
434
+ ([field, value]) =>
435
+ Object.hasOwn(canonicalProfile, field) &&
436
+ !configValuesEqual(
437
+ canonicalProfile[field as keyof TelegramBotProfile],
438
+ value,
439
+ ),
440
+ )
441
+ : false;
442
+ if (hasConflict) {
443
+ throw new Error(
444
+ "Conflicting Telegram default profile identity at root and profiles.default",
445
+ );
446
+ }
447
+ const normalizedProfile: TelegramBotProfile = canonicalProfile
448
+ ? { ...legacyProfile, ...canonicalProfile }
449
+ : (legacyProfile as TelegramBotProfile);
450
+ return {
451
+ config: {
452
+ ...omitTelegramRootProfileFields(config),
453
+ profiles: {
454
+ ...(config.profiles ?? {}),
455
+ [TELEGRAM_DEFAULT_PROFILE_NAME]: normalizedProfile,
456
+ },
457
+ },
458
+ changed: true,
403
459
  };
404
460
  }
405
461
 
@@ -407,16 +463,12 @@ function applyTelegramProfile(
407
463
  config: TelegramConfig,
408
464
  profileName: string | undefined,
409
465
  ): TelegramConfig {
410
- if (!profileName) return config;
411
- const profile = config.profiles?.[profileName];
412
- if (!profile) return config;
466
+ const effectiveProfileName = profileName ?? TELEGRAM_DEFAULT_PROFILE_NAME;
467
+ const profile = config.profiles?.[effectiveProfileName];
468
+ if (!profile) return omitTelegramRootProfileFields(config);
413
469
  return {
414
- ...config,
415
- botToken: profile.botToken,
416
- botUsername: profile.botUsername,
417
- botId: profile.botId,
418
- allowedUserId: profile.allowedUserId,
419
- lastUpdateId: profile.lastUpdateId,
470
+ ...omitTelegramRootProfileFields(config),
471
+ ...profile,
420
472
  };
421
473
  }
422
474
 
@@ -425,18 +477,13 @@ function storeTelegramEffectiveConfig(
425
477
  nextConfig: TelegramConfig,
426
478
  profileName: string | undefined,
427
479
  ): TelegramConfig {
428
- if (!profileName) return nextConfig;
480
+ const effectiveProfileName = profileName ?? TELEGRAM_DEFAULT_PROFILE_NAME;
429
481
  const profile = getTelegramProfileFields(nextConfig);
430
482
  const profiles = { ...(baseConfig.profiles ?? {}) };
431
- if (profile) profiles[profileName] = profile;
432
- else delete profiles[profileName];
483
+ if (profile) profiles[effectiveProfileName] = profile;
484
+ else delete profiles[effectiveProfileName];
433
485
  return {
434
- ...nextConfig,
435
- botToken: baseConfig.botToken,
436
- botUsername: baseConfig.botUsername,
437
- botId: baseConfig.botId,
438
- allowedUserId: baseConfig.allowedUserId,
439
- lastUpdateId: baseConfig.lastUpdateId,
486
+ ...omitTelegramRootProfileFields(nextConfig),
440
487
  profiles: Object.keys(profiles).length > 0 ? profiles : undefined,
441
488
  };
442
489
  }
@@ -444,7 +491,9 @@ function storeTelegramEffectiveConfig(
444
491
  export function createTelegramConfigStore(
445
492
  options: TelegramConfigStoreOptions = {},
446
493
  ): TelegramConfigStore {
447
- let config: TelegramConfig = cloneTelegramConfig(options.initialConfig ?? {});
494
+ let config: TelegramConfig = normalizeTelegramDefaultProfileConfig(
495
+ cloneTelegramConfig(options.initialConfig ?? {}),
496
+ ).config;
448
497
  let persistedConfig: TelegramConfig = {};
449
498
  let mutationVersion = 0;
450
499
  let persistQueue: Promise<void> = Promise.resolve();
@@ -465,14 +514,30 @@ export function createTelegramConfigStore(
465
514
  get: getEffectiveConfig,
466
515
  getStoredConfig: () => config,
467
516
  set: setEffectiveConfig,
517
+ setProfile: (profileName, profile) => {
518
+ config = {
519
+ ...omitTelegramRootProfileFields(config),
520
+ profiles: {
521
+ ...(config.profiles ?? {}),
522
+ [profileName]: cloneTelegramConfig(profile),
523
+ },
524
+ };
525
+ mutationVersion += 1;
526
+ },
468
527
  update: (mutate) => {
469
528
  const nextConfig = getEffectiveConfig();
470
529
  mutate(nextConfig);
471
530
  setEffectiveConfig(nextConfig);
472
531
  },
473
532
  activateProfile: (profileName) => {
474
- if (profileName && !config.profiles?.[profileName]) return false;
475
- activeProfileName = profileName;
533
+ const normalizedProfileName =
534
+ !profileName || profileName === TELEGRAM_DEFAULT_PROFILE_NAME
535
+ ? undefined
536
+ : profileName;
537
+ if (normalizedProfileName && !config.profiles?.[normalizedProfileName]) {
538
+ return false;
539
+ }
540
+ activeProfileName = normalizedProfileName;
476
541
  return true;
477
542
  },
478
543
  getActiveProfileName: () => activeProfileName,
@@ -491,7 +556,7 @@ export function createTelegramConfigStore(
491
556
  setEffectiveConfig(nextConfig);
492
557
  },
493
558
  load: async () => {
494
- config = await readTelegramConfig(configPath, {
559
+ const loadedConfig = await readTelegramConfig(configPath, {
495
560
  onInvalidConfig: (recovery) => {
496
561
  options.recordRuntimeEvent?.("config", recovery.error, {
497
562
  phase: "load",
@@ -500,6 +565,31 @@ export function createTelegramConfigStore(
500
565
  });
501
566
  },
502
567
  });
568
+ let normalized: ReturnType<typeof normalizeTelegramDefaultProfileConfig>;
569
+ try {
570
+ normalized = normalizeTelegramDefaultProfileConfig(loadedConfig);
571
+ } catch (error) {
572
+ options.recordRuntimeEvent?.("config", error, {
573
+ phase: "default-profile-normalize",
574
+ configPath,
575
+ });
576
+ throw error;
577
+ }
578
+ config = normalized.changed
579
+ ? withTelegramFileTransaction(`${configPath}.transaction`, () => {
580
+ const latestConfig = readTelegramConfigForTransaction(configPath);
581
+ const latestNormalized =
582
+ normalizeTelegramDefaultProfileConfig(latestConfig);
583
+ if (latestNormalized.changed) {
584
+ writeTelegramConfigInTransaction(
585
+ agentDir,
586
+ configPath,
587
+ latestNormalized.config,
588
+ );
589
+ }
590
+ return latestNormalized.config;
591
+ })
592
+ : normalized.config;
503
593
  persistedConfig = cloneTelegramConfig(config);
504
594
  mutationVersion += 1;
505
595
  },
@@ -522,7 +612,9 @@ export function createTelegramConfigStore(
522
612
  desiredConfig as Record<string, unknown>,
523
613
  latestConfig as Record<string, unknown>,
524
614
  ) as TelegramConfig;
525
- writeTelegramConfigInTransaction(agentDir, configPath, merged);
615
+ if (!configValuesEqual(latestConfig, merged)) {
616
+ writeTelegramConfigInTransaction(agentDir, configPath, merged);
617
+ }
526
618
  return merged;
527
619
  },
528
620
  );
package/lib/inbound.ts CHANGED
@@ -23,8 +23,7 @@ const MAX_INBOUND_HANDLER_OUTPUT_LENGTH = 12_000;
23
23
  const MAX_INBOUND_HANDLER_FAILURE_STREAM_LENGTH = 4_000;
24
24
 
25
25
  type TelegramInboundCommandTemplateConfig =
26
- | string
27
- | CommandTemplateObjectConfig;
26
+ string | CommandTemplateObjectConfig;
28
27
 
29
28
  export interface TelegramInboundHandlerConfig {
30
29
  match?: string | string[];
@@ -74,10 +73,6 @@ export interface TelegramInboundHandlerExecResult {
74
73
  killed: boolean;
75
74
  }
76
75
 
77
- export interface TelegramInboundHandlerRuntimeContext {
78
- cwd: string;
79
- }
80
-
81
76
  export interface TelegramInboundHandlerRuntimeDeps<TContext> {
82
77
  getHandlers: () => TelegramInboundHandlerConfig[] | undefined;
83
78
  execCommand: (
@@ -102,9 +97,7 @@ export interface TelegramInboundHandlerRuntime<TContext> {
102
97
  }
103
98
 
104
99
  export type TelegramInboundProgrammaticHandlerResult =
105
- | string
106
- | { text: string }
107
- | undefined;
100
+ string | { text: string } | undefined;
108
101
 
109
102
  export interface TelegramInboundProgrammaticHandlerInput {
110
103
  kind: string;
@@ -179,12 +172,6 @@ export function getTelegramInboundProgrammaticHandlers(
179
172
  ];
180
173
  }
181
174
 
182
- export function hasTelegramInboundHandler(kind?: string): boolean {
183
- const registry = getOrCreateInboundHandlerRegistry();
184
- if (kind !== undefined) return (registry.handlers.get(kind)?.length ?? 0) > 0;
185
- return Array.from(registry.handlers.values()).some((list) => list.length > 0);
186
- }
187
-
188
175
  export function clearTelegramInboundHandlers(): void {
189
176
  getOrCreateInboundHandlerRegistry().handlers.clear();
190
177
  }
@@ -281,13 +268,6 @@ export function findTelegramInboundHandlers(
281
268
  );
282
269
  }
283
270
 
284
- export function findTelegramInboundHandler(
285
- handlers: TelegramInboundHandlerConfig[] | undefined,
286
- file: TelegramInboundHandlerFile,
287
- ): TelegramInboundHandlerConfig | undefined {
288
- return findTelegramInboundHandlers(handlers, file)[0];
289
- }
290
-
291
271
  function hasInboundFilePlaceholder(value: string): boolean {
292
272
  return /\{file\}/.test(value);
293
273
  }
@@ -420,9 +400,13 @@ function formatTelegramInboundHandlerFailure(
420
400
  `Inbound handler exited with code ${result.code}${result.killed ? " (killed)" : ""}`,
421
401
  ];
422
402
  if (result.stderr.trim())
423
- parts.push(`stderr:\n${truncateTelegramInboundFailureStream(result.stderr)}`);
403
+ parts.push(
404
+ `stderr:\n${truncateTelegramInboundFailureStream(result.stderr)}`,
405
+ );
424
406
  if (result.stdout.trim())
425
- parts.push(`stdout:\n${truncateTelegramInboundFailureStream(result.stdout)}`);
407
+ parts.push(
408
+ `stdout:\n${truncateTelegramInboundFailureStream(result.stdout)}`,
409
+ );
426
410
  return parts.join("\n\n");
427
411
  }
428
412
 
@@ -445,7 +429,13 @@ async function executeTelegramInboundHandlerInvocation(
445
429
  cwd,
446
430
  timeout,
447
431
  ...(typeof handler === "object" && handler.retry !== undefined
448
- ? { retry: resolveTelegramInboundNumericControlField(handler.retry, {}, "retry") }
432
+ ? {
433
+ retry: resolveTelegramInboundNumericControlField(
434
+ handler.retry,
435
+ {},
436
+ "retry",
437
+ ),
438
+ }
449
439
  : {}),
450
440
  ...(stdin !== undefined ? { stdin } : {}),
451
441
  });
@@ -519,7 +509,13 @@ async function executeTelegramTextHandlerInvocation(
519
509
  timeout,
520
510
  stdin: text,
521
511
  ...(typeof handler === "object" && handler.retry !== undefined
522
- ? { retry: resolveTelegramInboundNumericControlField(handler.retry, {}, "retry") }
512
+ ? {
513
+ retry: resolveTelegramInboundNumericControlField(
514
+ handler.retry,
515
+ {},
516
+ "retry",
517
+ ),
518
+ }
523
519
  : {}),
524
520
  });
525
521
  if (result.code !== 0)
package/lib/lifecycle.ts CHANGED
@@ -196,19 +196,6 @@ export function createTelegramSessionContextStore<TContext>(
196
196
  };
197
197
  }
198
198
 
199
- export function createTelegramSessionContextTracker(
200
- store: Pick<TelegramSessionContextStore<ExtensionContext>, "set" | "clear">,
201
- ): TelegramSessionLifecycleHooks {
202
- return {
203
- onSessionStart: async (_event, ctx) => {
204
- store.set(ctx);
205
- },
206
- onSessionShutdown: async (_event, ctx) => {
207
- store.clear(ctx);
208
- },
209
- };
210
- }
211
-
212
199
  export function createTelegramSessionGenerationFence(
213
200
  store: TelegramSessionContextStore<ExtensionContext>,
214
201
  hooks: TelegramSessionLifecycleHooks,