@llblab/pi-telegram 0.52.2 → 0.53.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.
Files changed (204) hide show
  1. package/AGENTS.md +9 -7
  2. package/BACKLOG.md +1 -18
  3. package/CHANGELOG.md +11 -0
  4. package/README.md +7 -11
  5. package/api/commands.ts +3 -0
  6. package/dist/api/commands.d.ts +1 -1
  7. package/dist/lib/activity-verbosity.d.ts +1 -5
  8. package/dist/lib/activity-verbosity.js +15 -42
  9. package/dist/lib/activity.d.ts +9 -0
  10. package/dist/lib/activity.js +39 -10
  11. package/dist/lib/agent-messages.js +7 -3
  12. package/dist/lib/bindings.d.ts +43 -5
  13. package/dist/lib/bindings.js +163 -20
  14. package/dist/lib/bus-api.d.ts +12 -0
  15. package/dist/lib/bus-api.js +182 -79
  16. package/dist/lib/bus-follower.d.ts +111 -31
  17. package/dist/lib/bus-follower.js +1324 -359
  18. package/dist/lib/bus-leader.d.ts +41 -91
  19. package/dist/lib/bus-leader.js +969 -637
  20. package/dist/lib/bus-transport.js +16 -9
  21. package/dist/lib/bus.d.ts +215 -18
  22. package/dist/lib/bus.js +1048 -327
  23. package/dist/lib/channel-posts.d.ts +5 -0
  24. package/dist/lib/channel-posts.js +434 -261
  25. package/dist/lib/command-templates.d.ts +26 -5
  26. package/dist/lib/command-templates.js +92 -176
  27. package/dist/lib/commands.d.ts +168 -82
  28. package/dist/lib/commands.js +1409 -291
  29. package/dist/lib/config.d.ts +1 -8
  30. package/dist/lib/config.js +34 -76
  31. package/dist/lib/delivery.d.ts +2 -2
  32. package/dist/lib/delivery.js +16 -16
  33. package/dist/lib/extension.js +639 -171
  34. package/dist/lib/generative-app-worker.mjs +19 -7
  35. package/dist/lib/generative-apps.d.ts +0 -5
  36. package/dist/lib/generative-apps.js +61 -49
  37. package/dist/lib/inbound.d.ts +5 -0
  38. package/dist/lib/inbound.js +35 -59
  39. package/dist/lib/journal.d.ts +33 -3
  40. package/dist/lib/journal.js +2063 -782
  41. package/dist/lib/lifecycle.d.ts +2 -6
  42. package/dist/lib/lifecycle.js +75 -58
  43. package/dist/lib/locks.d.ts +29 -3
  44. package/dist/lib/locks.js +289 -108
  45. package/dist/lib/logging.js +50 -14
  46. package/dist/lib/media.d.ts +22 -1
  47. package/dist/lib/media.js +46 -28
  48. package/dist/lib/menu-model.d.ts +19 -5
  49. package/dist/lib/menu-model.js +53 -11
  50. package/dist/lib/menu-queue.d.ts +5 -4
  51. package/dist/lib/menu-queue.js +45 -63
  52. package/dist/lib/menu-settings.d.ts +5 -4
  53. package/dist/lib/menu-settings.js +114 -78
  54. package/dist/lib/menu-status.d.ts +1 -1
  55. package/dist/lib/menu-status.js +8 -25
  56. package/dist/lib/menu-thinking.d.ts +6 -1
  57. package/dist/lib/menu-thinking.js +14 -21
  58. package/dist/lib/menu.d.ts +8 -61
  59. package/dist/lib/menu.js +92 -164
  60. package/dist/lib/model.d.ts +1 -1
  61. package/dist/lib/model.js +2 -16
  62. package/dist/lib/outbound-attachments.d.ts +6 -27
  63. package/dist/lib/outbound-attachments.js +45 -38
  64. package/dist/lib/outbound-buttons.js +22 -16
  65. package/dist/lib/outbound-markup.d.ts +0 -2
  66. package/dist/lib/outbound-markup.js +51 -64
  67. package/dist/lib/outbound-voice.js +6 -3
  68. package/dist/lib/outbound.d.ts +6 -6
  69. package/dist/lib/outbound.js +61 -51
  70. package/dist/lib/ownership.js +6 -2
  71. package/dist/lib/paths.d.ts +5 -0
  72. package/dist/lib/paths.js +37 -21
  73. package/dist/lib/pi.d.ts +2 -0
  74. package/dist/lib/pi.js +8 -1
  75. package/dist/lib/polling.d.ts +21 -37
  76. package/dist/lib/polling.js +59 -50
  77. package/dist/lib/preview.js +39 -19
  78. package/dist/lib/process-identity.d.ts +24 -0
  79. package/dist/lib/process-identity.js +133 -0
  80. package/dist/lib/prompts.js +2 -7
  81. package/dist/lib/queue.d.ts +14 -16
  82. package/dist/lib/queue.js +249 -126
  83. package/dist/lib/recovery.d.ts +2 -83
  84. package/dist/lib/recovery.js +20 -274
  85. package/dist/lib/replies.d.ts +16 -24
  86. package/dist/lib/replies.js +99 -116
  87. package/dist/lib/routing.d.ts +120 -7
  88. package/dist/lib/routing.js +4667 -1152
  89. package/dist/lib/runtime.d.ts +1 -11
  90. package/dist/lib/sections.d.ts +4 -29
  91. package/dist/lib/sections.js +7 -20
  92. package/dist/lib/skills.js +4 -3
  93. package/dist/lib/status.js +86 -57
  94. package/dist/lib/sync.d.ts +1 -3
  95. package/dist/lib/sync.js +67 -43
  96. package/dist/lib/target.d.ts +4 -0
  97. package/dist/lib/target.js +18 -0
  98. package/dist/lib/telegram-api.d.ts +18 -13
  99. package/dist/lib/telegram-api.js +148 -53
  100. package/dist/lib/text-groups.js +4 -39
  101. package/dist/lib/thread-cleanup-manager.d.ts +6 -27
  102. package/dist/lib/thread-cleanup-manager.js +369 -186
  103. package/dist/lib/thread-display.d.ts +1 -2
  104. package/dist/lib/thread-display.js +58 -38
  105. package/dist/lib/thread-naming.d.ts +28 -8
  106. package/dist/lib/thread-naming.js +175 -66
  107. package/dist/lib/thread-reconciler.d.ts +42 -16
  108. package/dist/lib/thread-reconciler.js +90 -50
  109. package/dist/lib/threads.d.ts +82 -20
  110. package/dist/lib/threads.js +2247 -855
  111. package/dist/lib/time-injection.js +1 -2
  112. package/dist/lib/turns.js +1 -19
  113. package/dist/lib/updates.d.ts +169 -80
  114. package/dist/lib/updates.js +2418 -641
  115. package/dist/lib/voice.d.ts +7 -22
  116. package/dist/lib/voice.js +7 -22
  117. package/dist/lib/wire.js +1 -1
  118. package/dist/lib/workspace-admission.d.ts +3 -1
  119. package/dist/lib/workspace-admission.js +425 -211
  120. package/dist/lib/workspace-identity.js +8 -13
  121. package/dist/lib/workspace-retirement.d.ts +1 -1
  122. package/dist/lib/workspace-retirement.js +328 -140
  123. package/dist/lib/workspace-slots.js +8 -4
  124. package/dist/package.json +1 -1
  125. package/docs/architecture.md +31 -29
  126. package/docs/inbound.md +1 -1
  127. package/docs/multi-instance-bus.md +15 -3
  128. package/docs/outbound.md +1 -1
  129. package/docs/public-api.md +29 -1
  130. package/docs/ui-style.md +25 -8
  131. package/lib/activity-verbosity.ts +59 -82
  132. package/lib/activity.ts +146 -102
  133. package/lib/agent-messages.ts +12 -5
  134. package/lib/bindings.ts +389 -82
  135. package/lib/bus-api.ts +263 -102
  136. package/lib/bus-follower.ts +2440 -601
  137. package/lib/bus-leader.ts +2209 -1290
  138. package/lib/bus-transport.ts +46 -12
  139. package/lib/bus.ts +1673 -444
  140. package/lib/channel-posts.ts +848 -327
  141. package/lib/command-templates.ts +152 -265
  142. package/lib/commands.ts +2398 -555
  143. package/lib/config.ts +177 -170
  144. package/lib/delivery.ts +41 -29
  145. package/lib/extension.ts +1206 -433
  146. package/lib/generative-app-worker.mjs +19 -7
  147. package/lib/generative-apps.ts +324 -211
  148. package/lib/inbound.ts +95 -97
  149. package/lib/journal.ts +5311 -1763
  150. package/lib/keyboard.ts +1 -3
  151. package/lib/lifecycle.ts +128 -78
  152. package/lib/locks.ts +677 -203
  153. package/lib/logging.ts +147 -58
  154. package/lib/media.ts +82 -43
  155. package/lib/menu-model.ts +85 -18
  156. package/lib/menu-queue.ts +88 -129
  157. package/lib/menu-settings.ts +260 -155
  158. package/lib/menu-status.ts +20 -60
  159. package/lib/menu-thinking.ts +41 -56
  160. package/lib/menu.ts +112 -285
  161. package/lib/model.ts +8 -22
  162. package/lib/outbound-attachments.ts +156 -125
  163. package/lib/outbound-buttons.ts +76 -49
  164. package/lib/outbound-markup.ts +96 -94
  165. package/lib/outbound-voice.ts +33 -27
  166. package/lib/outbound.ts +125 -125
  167. package/lib/ownership.ts +19 -10
  168. package/lib/paths.ts +148 -49
  169. package/lib/pi.ts +35 -15
  170. package/lib/polling.ts +144 -147
  171. package/lib/preview.ts +93 -40
  172. package/lib/process-identity.ts +164 -0
  173. package/lib/prompts.ts +4 -11
  174. package/lib/queue.ts +511 -300
  175. package/lib/recovery.ts +50 -454
  176. package/lib/rendering.ts +1 -3
  177. package/lib/replies.ts +248 -209
  178. package/lib/routing.ts +8509 -2186
  179. package/lib/runtime.ts +1 -12
  180. package/lib/sections.ts +24 -89
  181. package/lib/setup.ts +1 -3
  182. package/lib/skills.ts +9 -4
  183. package/lib/status.ts +215 -110
  184. package/lib/sync.ts +448 -361
  185. package/lib/target.ts +22 -3
  186. package/lib/telegram-api.ts +302 -127
  187. package/lib/text-groups.ts +5 -39
  188. package/lib/thread-cleanup-manager.ts +833 -331
  189. package/lib/thread-display.ts +213 -97
  190. package/lib/thread-naming.ts +232 -87
  191. package/lib/thread-reconciler.ts +155 -72
  192. package/lib/threads.ts +5318 -1845
  193. package/lib/time-injection.ts +1 -4
  194. package/lib/turns.ts +15 -28
  195. package/lib/updates.ts +5184 -1612
  196. package/lib/voice.ts +7 -35
  197. package/lib/wire.ts +1 -1
  198. package/lib/workspace-admission.ts +996 -416
  199. package/lib/workspace-identity.ts +14 -17
  200. package/lib/workspace-retirement.ts +878 -376
  201. package/lib/workspace-slots.ts +22 -13
  202. package/package.json +1 -1
  203. package/scripts/check-downgrade.mjs +0 -472
  204. package/scripts/measure-workspace.mjs +0 -108
@@ -3,9 +3,9 @@
3
3
  * Zones: telegram config, pairing, filesystem
4
4
  * Owns persisted bot/session pairing state, local config storage, live config controls, authorization policy, and first-user pairing side effects
5
5
  */
6
- export { TELEGRAM_DEFAULT_PROFILE_NAME } from "./paths.ts";
7
6
  import type { CommandTemplateObjectConfig } from "./command-templates.ts";
8
7
  import type { TelegramInboundHandlerConfig } from "./inbound.ts";
8
+ export { TELEGRAM_DEFAULT_PROFILE_NAME } from "./paths.ts";
9
9
  /** Parsed stored bot-token form: a literal secret or one environment-variable reference. */
10
10
  export type TelegramBotTokenReference = {
11
11
  kind: "literal";
@@ -152,20 +152,13 @@ export interface TelegramInvalidConfigRecovery {
152
152
  recoveryPath: string;
153
153
  error: unknown;
154
154
  }
155
- export interface TelegramConfigRuntime {
156
- updateVoiceConfig: (voice: NonNullable<TelegramConfig["voice"]>) => void;
157
- }
158
- export declare function setGlobalTelegramConfigRuntime(runtime: TelegramConfigRuntime | undefined): void;
159
- export declare function updateTelegramVoiceConfig(voice: NonNullable<TelegramConfig["voice"]>): boolean;
160
155
  type TelegramMutableConfigStore = Pick<TelegramConfigStore, "get" | "set" | "persist"> & {
161
156
  load?: () => Promise<void>;
162
157
  didLastLoadRecoverInvalidConfig?: () => boolean;
163
158
  };
164
- export declare function bindGlobalTelegramConfigRuntime(configStore: TelegramMutableConfigStore): void;
165
159
  export declare function readTelegramConfig(configPath: string, options?: {
166
160
  onInvalidConfig?: (recovery: TelegramInvalidConfigRecovery) => void;
167
161
  }): Promise<TelegramConfig>;
168
- export declare function writeTelegramConfig(agentDir: string, configPath: string, config: TelegramConfig): Promise<void>;
169
162
  export declare function getTelegramProfileFields(config: TelegramConfig): TelegramBotProfile | undefined;
170
163
  export declare function normalizeTelegramDefaultProfileConfig(config: TelegramConfig): {
171
164
  config: TelegramConfig;
@@ -5,12 +5,11 @@
5
5
  */
6
6
  import { createHash, randomUUID } from "node:crypto";
7
7
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
8
- import { chmod, mkdir, rename, writeFile } from "node:fs/promises";
8
+ import { withTelegramFileTransaction } from "./locks.js";
9
9
  import { resolveAgentDir, resolveTelegramConfigPath, TELEGRAM_DEFAULT_PROFILE_NAME, } from "./paths.js";
10
- export { TELEGRAM_DEFAULT_PROFILE_NAME } from "./paths.js";
10
+ import { isPiStaleContextError } from "./pi.js";
11
11
  import { isWireRecord as isPlainConfigRecord } from "./wire.js";
12
- import { withTelegramFileTransaction } from "./locks.js";
13
- const CONFIG_RUNTIME_KEY = "__piTelegramConfigRuntime__";
12
+ export { TELEGRAM_DEFAULT_PROFILE_NAME } from "./paths.js";
14
13
  const CONFIG_REPLACE_RETRY_ATTEMPTS = 5;
15
14
  const CONFIG_REPLACE_RETRY_DELAY_MS = 25;
16
15
  function isRetryableConfigReplaceError(error) {
@@ -81,7 +80,10 @@ export function getTelegramBotTokenDiagnostic(value, env = process.env) {
81
80
  return `Telegram bot token environment variable ${reference.variable} is not set.`;
82
81
  }
83
82
  const TELEGRAM_THREAD_DISPLAY_MODES = [
84
- "letters", "names", "directory-snake", "directory-title",
83
+ "letters",
84
+ "names",
85
+ "directory-snake",
86
+ "directory-title",
85
87
  ];
86
88
  export function resolveTelegramThreadDisplayMode(config) {
87
89
  return TELEGRAM_THREAD_DISPLAY_MODES.includes(config.threadDisplayMode)
@@ -124,20 +126,6 @@ export function createTelegramConfigBotIdGetter(store) {
124
126
  export function createTelegramActiveProfileKeyGetter(store) {
125
127
  return () => store.getActiveProfileName() ?? TELEGRAM_DEFAULT_PROFILE_NAME;
126
128
  }
127
- export function setGlobalTelegramConfigRuntime(runtime) {
128
- const globals = globalThis;
129
- if (runtime)
130
- globals[CONFIG_RUNTIME_KEY] = runtime;
131
- else
132
- delete globals[CONFIG_RUNTIME_KEY];
133
- }
134
- export function updateTelegramVoiceConfig(voice) {
135
- const runtime = globalThis[CONFIG_RUNTIME_KEY];
136
- if (!runtime || typeof runtime.updateVoiceConfig !== "function")
137
- return false;
138
- runtime.updateVoiceConfig(voice);
139
- return true;
140
- }
141
129
  function isEmptyTelegramConfig(config) {
142
130
  return Object.keys(config).length === 0;
143
131
  }
@@ -151,19 +139,6 @@ async function loadLatestTelegramConfig(configStore) {
151
139
  configStore.set(before);
152
140
  }
153
141
  }
154
- export function bindGlobalTelegramConfigRuntime(configStore) {
155
- setGlobalTelegramConfigRuntime({
156
- updateVoiceConfig(voice) {
157
- const current = configStore.get();
158
- const next = {
159
- ...current,
160
- voice: { ...(current.voice ?? {}), ...voice },
161
- };
162
- configStore.set(next);
163
- void configStore.persist(next);
164
- },
165
- });
166
- }
167
142
  function getInvalidTelegramConfigRecoveryPath(configPath) {
168
143
  return `${configPath}.invalid-${process.pid}-${Date.now()}`;
169
144
  }
@@ -201,17 +176,6 @@ export async function readTelegramConfig(configPath, options = {}) {
201
176
  });
202
177
  }
203
178
  }
204
- export async function writeTelegramConfig(agentDir, configPath, config) {
205
- await mkdir(agentDir, { recursive: true });
206
- const tempConfigPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
207
- await writeFile(tempConfigPath, JSON.stringify(config, null, "\t") + "\n", {
208
- encoding: "utf8",
209
- mode: 0o600,
210
- });
211
- await chmod(tempConfigPath, 0o600);
212
- await rename(tempConfigPath, configPath);
213
- await chmod(configPath, 0o600);
214
- }
215
179
  function cloneTelegramConfig(value) {
216
180
  return structuredClone(value);
217
181
  }
@@ -275,10 +239,13 @@ export function getTelegramProfileFields(config) {
275
239
  const token = config.botToken?.trim();
276
240
  if (!token)
277
241
  return undefined;
242
+ return { botToken: token, ...pickTelegramRootProfileFields(config) };
243
+ }
244
+ /** Root-level profile fields other than the token, including the retired legacy cursor. */
245
+ function pickTelegramRootProfileFields(config) {
278
246
  const legacyCursor = config
279
247
  .lastUpdateId;
280
248
  return {
281
- botToken: token,
282
249
  ...(config.botUsername !== undefined
283
250
  ? { botUsername: config.botUsername }
284
251
  : {}),
@@ -332,23 +299,7 @@ export function normalizeTelegramDefaultProfileConfig(config) {
332
299
  }
333
300
  const legacyProfile = {
334
301
  ...(legacyToken ? { botToken: legacyToken } : {}),
335
- ...(config.botUsername !== undefined
336
- ? { botUsername: config.botUsername }
337
- : {}),
338
- ...(config.botId !== undefined ? { botId: config.botId } : {}),
339
- ...(config.allowedUserId !== undefined
340
- ? { allowedUserId: config.allowedUserId }
341
- : {}),
342
- ...(config.threadDisplayMode !== undefined
343
- ? { threadDisplayMode: config.threadDisplayMode }
344
- : {}),
345
- ...(config
346
- .lastUpdateId !== undefined
347
- ? {
348
- lastUpdateId: config
349
- .lastUpdateId,
350
- }
351
- : {}),
302
+ ...pickTelegramRootProfileFields(config),
352
303
  };
353
304
  if (!canonicalProfile && !legacyToken) {
354
305
  throw new Error("Legacy Telegram default profile has no bot token");
@@ -421,7 +372,8 @@ export function createTelegramConfigStore(options = {}) {
421
372
  config = nextConfig;
422
373
  };
423
374
  const withPersistedPairingProfile = (profileName, tokenSha256, observe) => {
424
- if ((profileName !== TELEGRAM_DEFAULT_PROFILE_NAME && !isValidTelegramProfileName(profileName)) ||
375
+ if ((profileName !== TELEGRAM_DEFAULT_PROFILE_NAME &&
376
+ !isValidTelegramProfileName(profileName)) ||
425
377
  !/^[a-f0-9]{64}$/u.test(tokenSha256)) {
426
378
  throw new Error("Invalid Telegram pairing admission identity.");
427
379
  }
@@ -431,10 +383,13 @@ export function createTelegramConfigStore(options = {}) {
431
383
  const resolvedToken = typeof profile?.botToken === "string"
432
384
  ? resolveTelegramBotToken(profile.botToken, env)
433
385
  : undefined;
434
- if (!profile || !resolvedToken ||
435
- createHash("sha256").update(resolvedToken).digest("hex") !== tokenSha256 ||
386
+ if (!profile ||
387
+ !resolvedToken ||
388
+ createHash("sha256").update(resolvedToken).digest("hex") !==
389
+ tokenSha256 ||
436
390
  (profile.allowedUserId !== undefined &&
437
- (!Number.isSafeInteger(profile.allowedUserId) || profile.allowedUserId <= 0))) {
391
+ (!Number.isSafeInteger(profile.allowedUserId) ||
392
+ profile.allowedUserId <= 0))) {
438
393
  throw new Error("Telegram pairing admission authority is unavailable or changed.");
439
394
  }
440
395
  return observe(latest, profile);
@@ -506,9 +461,11 @@ export function createTelegramConfigStore(options = {}) {
506
461
  assertExecutionCurrent?.();
507
462
  const current = getEffectiveConfig();
508
463
  const previousOwner = persistedConfig.profiles?.[profileName]?.allowedUserId;
509
- if ((activeProfileName ?? TELEGRAM_DEFAULT_PROFILE_NAME) !== profileName ||
464
+ if ((activeProfileName ?? TELEGRAM_DEFAULT_PROFILE_NAME) !==
465
+ profileName ||
510
466
  current.botToken !== profile.botToken ||
511
- (current.allowedUserId !== undefined && current.allowedUserId !== userId) ||
467
+ (current.allowedUserId !== undefined &&
468
+ current.allowedUserId !== userId) ||
512
469
  (current.allowedUserId === undefined && previousOwner !== undefined)) {
513
470
  throw new Error("Telegram paired admission lost local profile authority.");
514
471
  }
@@ -524,7 +481,8 @@ export function createTelegramConfigStore(options = {}) {
524
481
  const previousOwner = getEffectiveConfig().allowedUserId;
525
482
  const assertCurrent = () => {
526
483
  assertExecutionCurrent?.();
527
- if (activeProfileName !== profileName || getEffectiveConfig().botToken !== botToken ||
484
+ if (activeProfileName !== profileName ||
485
+ getEffectiveConfig().botToken !== botToken ||
528
486
  getEffectiveConfig().allowedUserId !== previousOwner) {
529
487
  throw new Error("Telegram pairing lost its originating profile authority.");
530
488
  }
@@ -544,8 +502,13 @@ export function createTelegramConfigStore(options = {}) {
544
502
  assertCurrent();
545
503
  if (profile.allowedUserId !== undefined)
546
504
  return latest;
547
- const next = { ...latest, profiles: { ...latest.profiles,
548
- [profileKey]: { ...profile, allowedUserId: userId } } };
505
+ const next = {
506
+ ...latest,
507
+ profiles: {
508
+ ...latest.profiles,
509
+ [profileKey]: { ...profile, allowedUserId: userId },
510
+ },
511
+ };
549
512
  writeTelegramConfigInTransaction(agentDir, configPath, next);
550
513
  return next;
551
514
  });
@@ -851,11 +814,6 @@ export function getTelegramAuthorizationState(userId, allowedUserId) {
851
814
  }
852
815
  return { kind: "deny" };
853
816
  }
854
- function isTelegramStaleContextError(error) {
855
- return (error instanceof Error &&
856
- (error.message.includes("stale after session") ||
857
- error.message.includes("stale ctx")));
858
- }
859
817
  export async function pairTelegramUserIfNeeded(userId, deps) {
860
818
  const authorization = getTelegramAuthorizationState(userId, deps.allowedUserId);
861
819
  if (authorization.kind !== "pair")
@@ -869,7 +827,7 @@ export async function pairTelegramUserIfNeeded(userId, deps) {
869
827
  deps.updateStatus(deps.ctx);
870
828
  }
871
829
  catch (error) {
872
- if (!isTelegramStaleContextError(error))
830
+ if (!isPiStaleContextError(error))
873
831
  throw error;
874
832
  }
875
833
  return true;
@@ -49,6 +49,7 @@ export type TelegramDeliveryChatAction = "typing" | "upload_document" | "upload_
49
49
  /** @internal */
50
50
  export interface TelegramDeliveryRuntime {
51
51
  readonly generation: string;
52
+ hasPendingTarget?: (target: TelegramDeliveryTarget) => boolean;
52
53
  shutdown: () => void;
53
54
  sendView: (view: TelegramDeliveryView, options: SendTelegramViewOptions) => Promise<TelegramDeliveryResult<TelegramDeliveryHandle>>;
54
55
  editView: (handle: TelegramDeliveryHandle, view: TelegramDeliveryView) => Promise<TelegramDeliveryResult<TelegramDeliveryHandle>>;
@@ -100,6 +101,7 @@ export interface TelegramBridgeDeliveryRuntimeDeps {
100
101
  export declare function createTelegramDeliveryLifecycleHooks(createRuntime: () => TelegramDeliveryRuntime): {
101
102
  onSessionStart: () => Promise<void>;
102
103
  onSessionShutdown: () => Promise<void>;
104
+ hasPendingTarget: (target: TelegramDeliveryTarget) => boolean | undefined;
103
105
  };
104
106
  export declare function createTelegramDeliveryGenerationSeed(instanceId: string): string;
105
107
  /** @internal */
@@ -152,8 +154,6 @@ export declare function clearTelegramDeliveryRuntime(): void;
152
154
  /** @internal */
153
155
  export declare function isTelegramDeliveryHandleCurrent(handle: TelegramDeliveryHandle): boolean;
154
156
  export declare function sendTelegramView(view: TelegramDeliveryView, options: SendTelegramViewOptions): Promise<TelegramDeliveryResult<TelegramDeliveryHandle>>;
155
- /** @internal Edit an exact Telegram message through the currently bound runtime generation. */
156
- export declare function editTelegramTargetView(target: TelegramDeliveryTarget, messageId: number, view: TelegramDeliveryView): Promise<TelegramDeliveryResult<TelegramDeliveryHandle>>;
157
157
  export declare function editTelegramView(handle: TelegramDeliveryHandle, view: TelegramDeliveryView): Promise<TelegramDeliveryResult<TelegramDeliveryHandle>>;
158
158
  export declare function deleteTelegramView(handle: TelegramDeliveryHandle): Promise<TelegramDeliveryResult<void>>;
159
159
  export declare function sendTelegramChatAction(action: TelegramDeliveryChatAction, options: {
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { markTelegramBusAggregateDelivery } from "./bus.js";
7
7
  import { assertTelegramInlineKeyboardCallbackData, } from "./keyboard.js";
8
- import { withTelegramReplyParameters, renderTelegramMessage, } from "./replies.js";
8
+ import { renderTelegramMessage, withTelegramReplyParameters, } from "./replies.js";
9
9
  import { areTelegramTargetsEqual as areDeliveryTargetsEqual, getTelegramTargetThreadParams, } from "./target.js";
10
10
  import { getTelegramApiRetryAfterMs, isRetryableTelegramApiError, isTelegramApiCommitUnknownError, isTelegramMessageUnavailableError, } from "./telegram-api.js";
11
11
  const TELEGRAM_DELIVERY_RUNTIME_KEY = "__piTelegramDeliveryRuntime__";
@@ -26,6 +26,7 @@ export function createTelegramDeliveryLifecycleHooks(createRuntime) {
26
26
  runtime = undefined;
27
27
  };
28
28
  return {
29
+ hasPendingTarget: (target) => runtime?.hasPendingTarget?.(target),
29
30
  onSessionStart: async () => {
30
31
  stopCurrentRuntime();
31
32
  runtime = createRuntime();
@@ -167,7 +168,7 @@ function resolveTelegramDeliveryTarget(scope, deps) {
167
168
  }
168
169
  function createTelegramDeliveryTargetQueue() {
169
170
  const queues = new Map();
170
- return async function run(target, operation) {
171
+ async function run(target, operation) {
171
172
  const key = targetKey(target);
172
173
  const previous = queues.get(key) ?? Promise.resolve();
173
174
  const current = previous.then(operation, operation);
@@ -180,6 +181,10 @@ function createTelegramDeliveryTargetQueue() {
180
181
  if (queues.get(key) === settled)
181
182
  queues.delete(key);
182
183
  }
184
+ }
185
+ return {
186
+ run,
187
+ hasPendingTarget: (target) => queues.has(targetKey(target)),
183
188
  };
184
189
  }
185
190
  function getChunkTransportOptions(view, index, chunkCount, replyToMessageId, editing = false) {
@@ -198,7 +203,8 @@ function getChunkTransportOptions(view, index, chunkCount, replyToMessageId, edi
198
203
  export function createTelegramDeliveryRuntime(deps) {
199
204
  let active = true;
200
205
  const handleBindings = new WeakMap();
201
- const runForTarget = createTelegramDeliveryTargetQueue();
206
+ const targetQueue = createTelegramDeliveryTargetQueue();
207
+ const runForTarget = targetQueue.run;
202
208
  const render = (view) => {
203
209
  const chunks = deps.renderView(view);
204
210
  if (chunks.length === 0 ||
@@ -247,6 +253,7 @@ export function createTelegramDeliveryRuntime(deps) {
247
253
  };
248
254
  return {
249
255
  generation: deps.generation,
256
+ hasPendingTarget: targetQueue.hasPendingTarget,
250
257
  shutdown() {
251
258
  active = false;
252
259
  },
@@ -420,10 +427,14 @@ export function createTelegramBridgeDeliveryRuntime(deps) {
420
427
  const body = {
421
428
  chat_id: target.chatId,
422
429
  text: chunk.text,
423
- ...(chunk.parseMode === "html" ? { parse_mode: "HTML" } : {}),
430
+ ...(chunk.parseMode === "html"
431
+ ? { parse_mode: "HTML" }
432
+ : {}),
424
433
  ...getTelegramTargetThreadParams(target),
425
434
  ...(replyParameters ? { reply_parameters: replyParameters } : {}),
426
- ...(options.replyMarkup ? { reply_markup: options.replyMarkup } : {}),
435
+ ...(options.replyMarkup
436
+ ? { reply_markup: options.replyMarkup }
437
+ : {}),
427
438
  };
428
439
  return deps.api.sendMessage(target.threadId === undefined
429
440
  ? markTelegramBusAggregateDelivery(body)
@@ -515,17 +526,6 @@ export async function sendTelegramView(view, options) {
515
526
  return invalid;
516
527
  return runDeliveryOperation((runtime) => runtime.sendView(view, options));
517
528
  }
518
- /** @internal Edit an exact Telegram message through the currently bound runtime generation. */
519
- export async function editTelegramTargetView(target, messageId, view) {
520
- const invalid = validateView(view);
521
- if (invalid)
522
- return invalid;
523
- return runDeliveryOperation((runtime) => runtime.editView({
524
- target: { ...target },
525
- messageIds: [messageId],
526
- generation: runtime.generation,
527
- }, view));
528
- }
529
529
  export async function editTelegramView(handle, view) {
530
530
  const invalid = validateView(view);
531
531
  if (invalid)