@llblab/pi-kit 0.5.1 → 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.
Files changed (41) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +3 -3
  3. package/node_modules/@llblab/pi-grow-loop/AGENTS.md +2 -2
  4. package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +4 -0
  5. package/node_modules/@llblab/pi-grow-loop/README.md +6 -6
  6. package/node_modules/@llblab/pi-grow-loop/index.ts +6 -3
  7. package/node_modules/@llblab/pi-grow-loop/package.json +1 -1
  8. package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
  9. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +20 -0
  10. package/node_modules/@llblab/pi-telegram/README.md +1 -1
  11. package/node_modules/@llblab/pi-telegram/docs/architecture.md +2 -2
  12. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +4 -0
  13. package/node_modules/@llblab/pi-telegram/docs/outbound.md +27 -5
  14. package/node_modules/@llblab/pi-telegram/docs/public-api.md +1 -0
  15. package/node_modules/@llblab/pi-telegram/index.ts +24 -19
  16. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +43 -25
  17. package/node_modules/@llblab/pi-telegram/lib/activity.ts +79 -6
  18. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +110 -91
  19. package/node_modules/@llblab/pi-telegram/lib/config.ts +1 -1
  20. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +18 -18
  21. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +14 -3
  22. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +2 -2
  23. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +41 -37
  24. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +39 -42
  25. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +28 -17
  26. package/node_modules/@llblab/pi-telegram/lib/preview.ts +134 -73
  27. package/node_modules/@llblab/pi-telegram/lib/queue.ts +113 -72
  28. package/node_modules/@llblab/pi-telegram/lib/replies.ts +46 -38
  29. package/node_modules/@llblab/pi-telegram/lib/routing.ts +192 -58
  30. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +36 -3
  31. package/node_modules/@llblab/pi-telegram/lib/updates.ts +27 -35
  32. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  33. package/node_modules/@llblab/skills/abcd-context/AGENTS.md +1 -0
  34. package/node_modules/@llblab/skills/abcd-context/CHANGELOG.md +6 -2
  35. package/node_modules/@llblab/skills/abcd-context/SKILL.md +1 -1
  36. package/node_modules/@llblab/skills/abcd-context/docs/validation-design.md +11 -5
  37. package/node_modules/@llblab/skills/abcd-context/scripts/_self-test.mjs +61 -0
  38. package/node_modules/@llblab/skills/abcd-context/scripts/validate-context.mjs +67 -0
  39. package/node_modules/@llblab/skills/package.json +1 -1
  40. package/node_modules/@llblab/skills/release-flow/SKILL.md +2 -4
  41. package/package.json +4 -4
@@ -7,6 +7,7 @@
7
7
  import { createHash } from "node:crypto";
8
8
 
9
9
  import { isVoiceTurn } from "./voice.ts";
10
+ import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
10
11
 
11
12
  // --- Queue Items ---
12
13
 
@@ -1497,6 +1498,14 @@ export interface TelegramAgentEndRuntimeDeps<
1497
1498
  updateStatus: () => void;
1498
1499
  dispatchNextQueuedTelegramTurn: () => void;
1499
1500
  scheduleActiveTurnDelivery?: (task: () => Promise<void>) => void;
1501
+ preparePreviewDelivery?: (isDeliveryActive: () => boolean) => Pick<
1502
+ TelegramAgentEndRuntimeDeps<TTurn, TReplyMarkup>,
1503
+ "clearPreview" | "setPreviewPendingText" | "finalizeMarkdownPreview"
1504
+ > | undefined;
1505
+ preparePreviewClear?: (
1506
+ chatId: number,
1507
+ options?: { target?: TelegramQueueTarget; isDeliveryActive?: () => boolean },
1508
+ ) => () => Promise<void>;
1500
1509
  clearPreview: (
1501
1510
  chatId: number,
1502
1511
  options?: { target?: TelegramQueueTarget },
@@ -1520,11 +1529,14 @@ export interface TelegramAgentEndRuntimeDeps<
1520
1529
  text: string,
1521
1530
  options?: { target?: TelegramQueueTarget },
1522
1531
  ) => Promise<unknown>;
1523
- sendQueuedAttachments: (turn: TTurn) => Promise<void>;
1532
+ sendQueuedAttachments: (
1533
+ turn: TTurn,
1534
+ options?: { isDeliveryActive?: () => boolean },
1535
+ ) => Promise<void>;
1524
1536
  sendRichAttachmentReply?: (
1525
1537
  turn: TTurn,
1526
1538
  markdown: string,
1527
- options?: { replyMarkup?: TReplyMarkup },
1539
+ options?: { replyMarkup?: TReplyMarkup; isDeliveryActive?: () => boolean },
1528
1540
  ) => Promise<boolean>;
1529
1541
  answerGuestQuery?: (
1530
1542
  guestQueryId: string,
@@ -1548,7 +1560,7 @@ export interface TelegramAgentEndRuntimeDeps<
1548
1560
  sendOutboundReplyArtifacts?: (
1549
1561
  turn: TTurn,
1550
1562
  plan: TelegramAgentEndOutboundReplyPlan,
1551
- options?: { replyToPrompt?: boolean },
1563
+ options?: { replyToPrompt?: boolean; isDeliveryActive?: () => boolean },
1552
1564
  ) => Promise<void>;
1553
1565
  recordRuntimeEvent?: (
1554
1566
  category: string,
@@ -1583,6 +1595,12 @@ export interface TelegramAgentEndHookRuntimeDeps<
1583
1595
  TTurn,
1584
1596
  TReplyMarkup
1585
1597
  >["scheduleActiveTurnDelivery"];
1598
+ reserveActiveTurnDelivery?: () => {
1599
+ schedule: (task: () => Promise<void>) => void;
1600
+ cancel: () => void;
1601
+ };
1602
+ preparePreviewDelivery?: TelegramAgentEndRuntimeDeps<TTurn, TReplyMarkup>["preparePreviewDelivery"];
1603
+ preparePreviewClear?: TelegramAgentEndRuntimeDeps<TTurn, TReplyMarkup>["preparePreviewClear"];
1586
1604
  clearPreview: TelegramAgentEndRuntimeDeps<
1587
1605
  TTurn,
1588
1606
  TReplyMarkup
@@ -1597,7 +1615,7 @@ export interface TelegramAgentEndHookRuntimeDeps<
1597
1615
  TReplyMarkup
1598
1616
  >["sendMarkdownReply"];
1599
1617
  sendTextReply: TelegramAgentEndRuntimeDeps<TTurn>["sendTextReply"];
1600
- sendQueuedAttachments: (turn: TTurn) => Promise<void>;
1618
+ sendQueuedAttachments: TelegramAgentEndRuntimeDeps<TTurn>["sendQueuedAttachments"];
1601
1619
  sendRichAttachmentReply?: TelegramAgentEndRuntimeDeps<
1602
1620
  TTurn,
1603
1621
  TReplyMarkup
@@ -1702,48 +1720,57 @@ export function createTelegramAgentEndHook<
1702
1720
  ctx: TContext,
1703
1721
  assistantOverride?: TelegramAgentEndAssistantResult,
1704
1722
  ): Promise<void> => {
1705
- await deps.loadConfig?.();
1706
- if (deps.isSessionActive && !deps.isSessionActive(ctx)) return;
1723
+ if (deps.isSessionActive?.(ctx) === false) return;
1707
1724
  const turn = deps.getActiveTurn();
1708
- await handleTelegramAgentEndRuntime({
1709
- turn,
1710
- assistant:
1711
- assistantOverride ??
1712
- (turn ? deps.extractAssistant(event.messages) : {}),
1713
- foldQueuedPromptsIntoHistory: deps.getFoldQueuedPromptsIntoHistory(),
1714
- resetRuntimeState: deps.resetRuntimeState,
1715
- isSessionActive: () => deps.isSessionActive?.(ctx) ?? true,
1716
- isTurnTransportActive: deps.isTurnTransportActive,
1717
- waitForTypingIdle: deps.waitForTypingIdle,
1718
- waitForActivityIdle: deps.waitForActivityIdle,
1719
- updateStatus: () => deps.updateStatus(ctx),
1720
- dispatchNextQueuedTelegramTurn: () => {
1721
- deps.requestDeferredDispatchNextQueuedTelegramTurn(
1722
- deps.dispatchNextQueuedTelegramTurn,
1723
- );
1724
- },
1725
- scheduleActiveTurnDelivery: deps.scheduleActiveTurnDelivery
1726
- ? (task) =>
1727
- deps.scheduleActiveTurnDelivery?.(async () => {
1728
- if (deps.isSessionActive?.(ctx) === false) return;
1729
- await task();
1730
- })
1731
- : undefined,
1732
- clearPreview: deps.clearPreview,
1733
- setPreviewPendingText: deps.setPreviewPendingText,
1734
- finalizeMarkdownPreview: deps.finalizeMarkdownPreview,
1735
- sendMarkdownReply: deps.sendMarkdownReply,
1736
- sendTextReply: deps.sendTextReply,
1737
- sendQueuedAttachments: deps.sendQueuedAttachments,
1738
- sendRichAttachmentReply: deps.sendRichAttachmentReply,
1739
- answerGuestQuery: deps.answerGuestQuery,
1740
- sendGuestReply: deps.sendGuestReply,
1741
- sendGuestAttachment: deps.sendGuestAttachment,
1742
- sendGuestVoiceReply: deps.sendGuestVoiceReply,
1743
- planOutboundReply: deps.planOutboundReply,
1744
- sendOutboundReplyArtifacts: deps.sendOutboundReplyArtifacts,
1745
- recordRuntimeEvent: deps.recordRuntimeEvent,
1746
- });
1725
+ const assistant = assistantOverride ?? (turn ? deps.extractAssistant(event.messages) : {});
1726
+ const hasPublication = !!assistant.text || assistant.stopReason === "error" || !!turn?.queuedAttachments.length;
1727
+ const reservation = turn && !turn.guestQueryId && hasPublication ? deps.reserveActiveTurnDelivery?.() : undefined;
1728
+ const scheduleDelivery = reservation?.schedule ?? deps.scheduleActiveTurnDelivery;
1729
+ try {
1730
+ await deps.loadConfig?.();
1731
+ if (deps.isSessionActive?.(ctx) === false || deps.getActiveTurn() !== turn) return;
1732
+ await handleTelegramAgentEndRuntime({
1733
+ turn,
1734
+ assistant,
1735
+ foldQueuedPromptsIntoHistory: deps.getFoldQueuedPromptsIntoHistory(),
1736
+ resetRuntimeState: deps.resetRuntimeState,
1737
+ isSessionActive: () => deps.isSessionActive?.(ctx) ?? true,
1738
+ isTurnTransportActive: deps.isTurnTransportActive,
1739
+ waitForTypingIdle: deps.waitForTypingIdle,
1740
+ waitForActivityIdle: deps.waitForActivityIdle,
1741
+ updateStatus: () => deps.updateStatus(ctx),
1742
+ dispatchNextQueuedTelegramTurn: () => {
1743
+ deps.requestDeferredDispatchNextQueuedTelegramTurn(
1744
+ deps.dispatchNextQueuedTelegramTurn,
1745
+ );
1746
+ },
1747
+ scheduleActiveTurnDelivery: scheduleDelivery
1748
+ ? (task) =>
1749
+ scheduleDelivery(async () => {
1750
+ if (deps.isSessionActive?.(ctx) === false) return;
1751
+ await task();
1752
+ })
1753
+ : undefined,
1754
+ preparePreviewDelivery: deps.preparePreviewDelivery,
1755
+ preparePreviewClear: deps.preparePreviewClear,
1756
+ clearPreview: deps.clearPreview,
1757
+ setPreviewPendingText: deps.setPreviewPendingText,
1758
+ finalizeMarkdownPreview: deps.finalizeMarkdownPreview,
1759
+ sendMarkdownReply: deps.sendMarkdownReply,
1760
+ sendTextReply: deps.sendTextReply,
1761
+ sendQueuedAttachments: deps.sendQueuedAttachments,
1762
+ sendRichAttachmentReply: deps.sendRichAttachmentReply,
1763
+ answerGuestQuery: deps.answerGuestQuery,
1764
+ sendGuestReply: deps.sendGuestReply,
1765
+ sendGuestAttachment: deps.sendGuestAttachment,
1766
+ sendGuestVoiceReply: deps.sendGuestVoiceReply,
1767
+ planOutboundReply: deps.planOutboundReply,
1768
+ sendOutboundReplyArtifacts: deps.sendOutboundReplyArtifacts,
1769
+ recordRuntimeEvent: deps.recordRuntimeEvent,
1770
+ });
1771
+ } finally {
1772
+ reservation?.cancel();
1773
+ }
1747
1774
  };
1748
1775
  }
1749
1776
 
@@ -1786,6 +1813,14 @@ export async function handleTelegramAgentEndRuntime<
1786
1813
  const isDeliveryActive = (): boolean =>
1787
1814
  deps.isSessionActive?.() !== false &&
1788
1815
  (!turn || deps.isTurnTransportActive?.(turn) !== false);
1816
+ const preview = turn && !turn.guestQueryId ? deps.preparePreviewDelivery?.(isDeliveryActive) : undefined;
1817
+ const setPreviewPendingText = preview?.setPreviewPendingText ?? deps.setPreviewPendingText;
1818
+ const finalizeMarkdownPreview = preview?.finalizeMarkdownPreview ?? deps.finalizeMarkdownPreview;
1819
+ const clearPreview = turn
1820
+ ? preview ? () => preview.clearPreview(turn.chatId, { target: turn.target })
1821
+ : deps.preparePreviewClear?.(turn.chatId, { target: turn.target, isDeliveryActive })
1822
+ ?? (() => deps.clearPreview(turn.chatId, { target: turn.target }))
1823
+ : undefined;
1789
1824
  const updateStatusIgnoringStaleContext = (): void => {
1790
1825
  try {
1791
1826
  deps.updateStatus();
@@ -1869,28 +1904,28 @@ export async function handleTelegramAgentEndRuntime<
1869
1904
  if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
1870
1905
  return;
1871
1906
  }
1872
- if (endPlan.shouldClearPreview) {
1873
- await deps.clearPreview(turn.chatId, { target: turn.target });
1874
- }
1875
1907
  if (!isDeliveryActive()) return;
1876
- if (endPlan.shouldSendErrorMessage) {
1877
- await deps.sendTextReply(
1878
- turn.chatId,
1879
- turn.replyToMessageId,
1880
- assistant.errorMessage ||
1881
- "Telegram bridge: Pi failed while processing the request.",
1882
- { target: turn.target },
1883
- );
1884
- if (!isDeliveryActive()) return;
1885
- if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
1886
- return;
1887
- }
1888
1908
  const deliverActiveTurn = async () => {
1889
1909
  await deps.waitForActivityIdle?.();
1890
1910
  if (!isDeliveryActive()) return;
1891
- if (finalText) deps.setPreviewPendingText(finalText);
1892
- if (!finalText && hasOutboundArtifacts)
1893
- await deps.clearPreview(turn.chatId, { target: turn.target });
1911
+ if (endPlan.shouldClearPreview || (!finalText && hasOutboundArtifacts)) {
1912
+ await clearPreview?.();
1913
+ if (!isDeliveryActive()) return;
1914
+ }
1915
+ if (endPlan.shouldSendErrorMessage) {
1916
+ await deps.sendTextReply(
1917
+ turn.chatId,
1918
+ turn.replyToMessageId,
1919
+ assistant.errorMessage ||
1920
+ "Telegram bridge: Pi failed while processing the request.",
1921
+ { target: turn.target },
1922
+ );
1923
+ if (!isDeliveryActive()) return;
1924
+ if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
1925
+ return;
1926
+ }
1927
+ if (finalText) setPreviewPendingText(finalText);
1928
+
1894
1929
  if (!isDeliveryActive()) return;
1895
1930
  let richAttachmentDelivered = false;
1896
1931
  if (
@@ -1903,15 +1938,16 @@ export async function handleTelegramAgentEndRuntime<
1903
1938
  richAttachmentDelivered = await deps.sendRichAttachmentReply(
1904
1939
  turn,
1905
1940
  finalText,
1906
- { replyMarkup },
1941
+ { replyMarkup, isDeliveryActive },
1907
1942
  );
1908
1943
  if (!isDeliveryActive()) return;
1909
1944
  if (richAttachmentDelivered) {
1910
- await deps.clearPreview(turn.chatId, { target: turn.target });
1945
+ await clearPreview?.();
1911
1946
  if (!isDeliveryActive()) return;
1912
- deps.setPreviewPendingText("");
1947
+ setPreviewPendingText("");
1913
1948
  }
1914
1949
  } catch (error) {
1950
+ if (!isDeliveryActive()) return;
1915
1951
  deps.recordRuntimeEvent?.("delivery", error, {
1916
1952
  phase: "rich-attachment-commit-unknown",
1917
1953
  chatId: turn.chatId,
@@ -1923,7 +1959,7 @@ export async function handleTelegramAgentEndRuntime<
1923
1959
  if (!isDeliveryActive()) return;
1924
1960
  if (!richAttachmentDelivered && endPlan.kind === "text" && finalText) {
1925
1961
  try {
1926
- const finalized = await deps.finalizeMarkdownPreview(
1962
+ const finalized = await finalizeMarkdownPreview(
1927
1963
  turn.chatId,
1928
1964
  finalText,
1929
1965
  turn.replyToMessageId,
@@ -1931,7 +1967,7 @@ export async function handleTelegramAgentEndRuntime<
1931
1967
  );
1932
1968
  if (!isDeliveryActive()) return;
1933
1969
  if (!finalized) {
1934
- await deps.clearPreview(turn.chatId, { target: turn.target });
1970
+ await clearPreview?.();
1935
1971
  if (!isDeliveryActive()) return;
1936
1972
  await deps.sendMarkdownReply(
1937
1973
  turn.chatId,
@@ -1941,7 +1977,7 @@ export async function handleTelegramAgentEndRuntime<
1941
1977
  );
1942
1978
  }
1943
1979
  if (!isDeliveryActive()) return;
1944
- deps.setPreviewPendingText("");
1980
+ setPreviewPendingText("");
1945
1981
  } catch (error) {
1946
1982
  deps.recordRuntimeEvent?.("delivery", error, {
1947
1983
  phase: "final-text",
@@ -1955,6 +1991,7 @@ export async function handleTelegramAgentEndRuntime<
1955
1991
  try {
1956
1992
  await deps.sendOutboundReplyArtifacts(turn, outboundReply, {
1957
1993
  replyToPrompt: !finalText,
1994
+ isDeliveryActive,
1958
1995
  });
1959
1996
  if (!isDeliveryActive()) return;
1960
1997
  } catch (error) {
@@ -1962,8 +1999,12 @@ export async function handleTelegramAgentEndRuntime<
1962
1999
  phase: "voice-artifacts",
1963
2000
  chatId: turn.chatId,
1964
2001
  });
1965
- // Fallback to planned text when voice delivery fails and text wasn't already delivered
1966
2002
  if (!isDeliveryActive()) return;
2003
+ if (isTelegramApiCommitUnknownError(error)) {
2004
+ if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
2005
+ return;
2006
+ }
2007
+ // Fallback only when voice delivery is not uncertain and text wasn't already delivered.
1967
2008
  if (rawFinalText?.trim() && !finalText && hasOutboundArtifacts) {
1968
2009
  try {
1969
2010
  const fallbackMarkdown =
@@ -2000,13 +2041,13 @@ export async function handleTelegramAgentEndRuntime<
2000
2041
  );
2001
2042
  }
2002
2043
  if (!isDeliveryActive()) return;
2003
- if (!richAttachmentDelivered) await deps.sendQueuedAttachments(turn);
2044
+ if (!richAttachmentDelivered) await deps.sendQueuedAttachments(turn, { isDeliveryActive });
2004
2045
  if (!isDeliveryActive()) return;
2005
2046
  if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
2006
2047
  };
2007
2048
  if (
2008
2049
  deps.scheduleActiveTurnDelivery &&
2009
- (endPlan.kind === "text" || endPlan.kind === "attachments-only")
2050
+ (endPlan.kind === "text" || endPlan.kind === "attachments-only" || endPlan.shouldSendErrorMessage || endPlan.shouldClearPreview)
2010
2051
  ) {
2011
2052
  deps.scheduleActiveTurnDelivery(deliverActiveTurn);
2012
2053
  return;
@@ -9,6 +9,7 @@ import {
9
9
  getTelegramTargetThreadParams,
10
10
  type TelegramTarget,
11
11
  } from "./target.ts";
12
+ import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
12
13
  import type {
13
14
  TelegramInputRichMessage,
14
15
  TelegramReplyParameters,
@@ -67,6 +68,7 @@ export function createReplyDedupRuntime(): ReplyDedupRuntime {
67
68
  // --- Transport-level dedup ---
68
69
 
69
70
  const lastRepliedToMessageIdByTarget = new Map<string, number>();
71
+ let replyDedupGeneration = 0;
70
72
 
71
73
  function getReplyDedupTargetKey(
72
74
  chatId: number,
@@ -79,6 +81,7 @@ function getReplyDedupTargetKey(
79
81
  }
80
82
 
81
83
  export function resetTransportReplyDedup(): void {
84
+ replyDedupGeneration += 1;
82
85
  lastRepliedToMessageIdByTarget.clear();
83
86
  }
84
87
 
@@ -99,13 +102,29 @@ export function buildTelegramReplyParameters(
99
102
  };
100
103
  }
101
104
 
102
- export function buildTelegramMultipartReplyParameters(
105
+ // Answer publications are caller-serialized. A rejected send releases its anchor;
106
+ // an uncertain ACK retains it because Telegram may already have delivered it.
107
+ export async function withTelegramReplyParameters<T>(
103
108
  chatId: number,
104
109
  messageId: number | undefined,
105
- target?: TelegramTarget,
106
- ): string | undefined {
110
+ target: TelegramTarget | undefined,
111
+ send: (parameters: TelegramReplyParameters | undefined) => Promise<T>,
112
+ ): Promise<T> {
113
+ const key = getReplyDedupTargetKey(chatId, target);
114
+ const generation = replyDedupGeneration;
115
+ const previous = lastRepliedToMessageIdByTarget.get(key);
107
116
  const parameters = buildTelegramReplyParameters(chatId, messageId, target);
108
- return parameters ? JSON.stringify(parameters) : undefined;
117
+ try {
118
+ return await send(parameters);
119
+ } catch (error) {
120
+ if (parameters && !isTelegramApiCommitUnknownError(error)
121
+ && generation === replyDedupGeneration
122
+ && lastRepliedToMessageIdByTarget.get(key) === messageId) {
123
+ if (previous === undefined) lastRepliedToMessageIdByTarget.delete(key);
124
+ else lastRepliedToMessageIdByTarget.set(key, previous);
125
+ }
126
+ throw error;
127
+ }
109
128
  }
110
129
 
111
130
  function getAgentMessageField(message: unknown, field: string): unknown {
@@ -239,24 +258,18 @@ export async function sendTelegramRenderedChunks<TReplyMarkup>(
239
258
  assertTelegramInlineKeyboardCallbackData(options?.replyMarkup);
240
259
  let lastMessageId: number | undefined;
241
260
  for (const [index, chunk] of chunks.entries()) {
242
- const replyParameters =
243
- index === 0
244
- ? buildTelegramReplyParameters(
245
- chatId,
246
- options?.replyToMessageId,
247
- options?.target,
248
- )
249
- : undefined;
250
- const body = {
251
- chat_id: chatId,
252
- text: chunk.text,
253
- parse_mode: chunk.parseMode,
254
- reply_markup:
255
- index === chunks.length - 1 ? options?.replyMarkup : undefined,
256
- ...(replyParameters ? { reply_parameters: replyParameters } : {}),
257
- ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
258
- };
259
- const sent = await deps.sendMessage(body);
261
+ const sent = await withTelegramReplyParameters(
262
+ chatId, index === 0 ? options?.replyToMessageId : undefined, options?.target,
263
+ (replyParameters) => deps.sendMessage({
264
+ chat_id: chatId,
265
+ text: chunk.text,
266
+ parse_mode: chunk.parseMode,
267
+ reply_markup:
268
+ index === chunks.length - 1 ? options?.replyMarkup : undefined,
269
+ ...(replyParameters ? { reply_parameters: replyParameters } : {}),
270
+ ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
271
+ }),
272
+ );
260
273
  lastMessageId = sent.message_id;
261
274
  deps.recordOwnership?.({
262
275
  chatId,
@@ -655,22 +668,17 @@ export async function sendTelegramNativeMarkdownReply<TReplyMarkup = unknown>(
655
668
  let lastMessageId: number | undefined;
656
669
  const chunks = splitTelegramNativeMarkdown(markdown);
657
670
  for (const [index, chunk] of chunks.entries()) {
658
- const replyParameters =
659
- index === 0
660
- ? buildTelegramReplyParameters(
661
- chatId,
662
- replyToMessageId,
663
- options?.target,
664
- )
665
- : undefined;
666
- const sent = await deps.sendRichMessage({
667
- chat_id: chatId,
668
- rich_message: { markdown: chunk, skip_entity_detection: true },
669
- reply_markup:
670
- index === chunks.length - 1 ? options?.replyMarkup : undefined,
671
- ...(replyParameters ? { reply_parameters: replyParameters } : {}),
672
- ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
673
- });
671
+ const sent = await withTelegramReplyParameters(
672
+ chatId, index === 0 ? replyToMessageId : undefined, options?.target,
673
+ (replyParameters) => deps.sendRichMessage({
674
+ chat_id: chatId,
675
+ rich_message: { markdown: chunk, skip_entity_detection: true },
676
+ reply_markup:
677
+ index === chunks.length - 1 ? options?.replyMarkup : undefined,
678
+ ...(replyParameters ? { reply_parameters: replyParameters } : {}),
679
+ ...(options?.target ? getTelegramTargetThreadParams(options.target) : {}),
680
+ }),
681
+ );
674
682
  lastMessageId = sent.message_id;
675
683
  deps.recordOwnership?.({
676
684
  chatId,