@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.
- package/CHANGELOG.md +8 -0
- package/README.md +3 -3
- package/node_modules/@llblab/pi-grow-loop/AGENTS.md +2 -2
- package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +4 -0
- package/node_modules/@llblab/pi-grow-loop/README.md +6 -6
- package/node_modules/@llblab/pi-grow-loop/index.ts +6 -3
- package/node_modules/@llblab/pi-grow-loop/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +20 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +4 -0
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +27 -5
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +1 -0
- package/node_modules/@llblab/pi-telegram/index.ts +24 -19
- package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +43 -25
- package/node_modules/@llblab/pi-telegram/lib/activity.ts +79 -6
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +110 -91
- package/node_modules/@llblab/pi-telegram/lib/config.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +18 -18
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +14 -3
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +41 -37
- package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +39 -42
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +28 -17
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +134 -73
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +113 -72
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +46 -38
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +192 -58
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +36 -3
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +27 -35
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/skills/abcd-context/AGENTS.md +1 -0
- package/node_modules/@llblab/skills/abcd-context/CHANGELOG.md +6 -2
- package/node_modules/@llblab/skills/abcd-context/SKILL.md +1 -1
- package/node_modules/@llblab/skills/abcd-context/docs/validation-design.md +11 -5
- package/node_modules/@llblab/skills/abcd-context/scripts/_self-test.mjs +61 -0
- package/node_modules/@llblab/skills/abcd-context/scripts/validate-context.mjs +67 -0
- package/node_modules/@llblab/skills/package.json +1 -1
- package/node_modules/@llblab/skills/release-flow/SKILL.md +2 -4
- 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: (
|
|
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:
|
|
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
|
-
|
|
1706
|
-
if (deps.isSessionActive && !deps.isSessionActive(ctx)) return;
|
|
1723
|
+
if (deps.isSessionActive?.(ctx) === false) return;
|
|
1707
1724
|
const turn = deps.getActiveTurn();
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
deps.
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
deps.
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
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 (
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
|
1945
|
+
await clearPreview?.();
|
|
1911
1946
|
if (!isDeliveryActive()) return;
|
|
1912
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
106
|
-
|
|
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
|
-
|
|
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
|
|
243
|
-
index === 0
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
659
|
-
index === 0
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
:
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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,
|