@llblab/pi-telegram 0.6.2 → 0.7.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/AGENTS.md +152 -0
- package/BACKLOG.md +5 -0
- package/CHANGELOG.md +142 -0
- package/README.md +49 -42
- package/docs/architecture.md +49 -40
- package/docs/attachment-handlers.md +4 -6
- package/docs/command-templates.md +53 -9
- package/docs/locks.md +4 -0
- package/docs/outbound-handlers.md +20 -14
- package/index.ts +217 -153
- package/lib/api.ts +1 -0
- package/lib/attachment-handlers.ts +16 -9
- package/lib/attachments.ts +1 -0
- package/lib/command-templates.ts +32 -3
- package/lib/commands.ts +367 -80
- package/lib/config.ts +10 -8
- package/lib/keyboard.ts +14 -0
- package/lib/lifecycle.ts +26 -0
- package/lib/locks.ts +3 -2
- package/lib/media.ts +1 -0
- package/lib/menu-model.ts +881 -0
- package/lib/menu-queue.ts +608 -0
- package/lib/menu-status.ts +226 -0
- package/lib/menu-thinking.ts +171 -0
- package/lib/menu.ts +143 -1019
- package/lib/model.ts +1 -0
- package/lib/outbound-handlers.ts +120 -45
- package/lib/pi.ts +8 -0
- package/lib/polling.ts +1 -0
- package/lib/preview.ts +97 -50
- package/lib/prompt-templates.ts +150 -0
- package/lib/prompts.ts +17 -9
- package/lib/queue.ts +51 -15
- package/lib/rendering.ts +1 -0
- package/lib/replies.ts +86 -2
- package/lib/routing.ts +76 -14
- package/lib/runtime.ts +2 -0
- package/lib/setup.ts +1 -0
- package/lib/status.ts +15 -6
- package/lib/turns.ts +1 -0
- package/lib/updates.ts +36 -6
- package/package.json +4 -1
package/lib/model.ts
CHANGED
package/lib/outbound-handlers.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram outbound handler helpers
|
|
3
|
+
* Zones: telegram outbound, assistant markup, command templates, callback routing
|
|
3
4
|
* Owns assistant-authored outbound markup extraction, configured artifact generation, callback actions, and Telegram outbound delivery
|
|
4
5
|
*/
|
|
5
6
|
|
|
@@ -8,6 +9,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
8
9
|
import { homedir } from "node:os";
|
|
9
10
|
import { basename, join, resolve } from "node:path";
|
|
10
11
|
|
|
12
|
+
import type { TelegramInlineKeyboardMarkup } from "./keyboard.ts";
|
|
11
13
|
import type { PendingTelegramTurn } from "./queue.ts";
|
|
12
14
|
import { buildTelegramMultipartReplyParameters } from "./replies.ts";
|
|
13
15
|
import { truncateTelegramQueueSummary } from "./turns.ts";
|
|
@@ -52,6 +54,7 @@ export interface TelegramVoiceExecOptions {
|
|
|
52
54
|
timeout?: number;
|
|
53
55
|
signal?: AbortSignal;
|
|
54
56
|
stdin?: string;
|
|
57
|
+
retry?: number;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
export interface TelegramVoiceExecResult {
|
|
@@ -106,6 +109,16 @@ interface TelegramTopLevelFenceState {
|
|
|
106
109
|
length: number;
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
function isTelegramActionCommentContent(content: string): boolean {
|
|
113
|
+
const normalizedContent = content.replace(/^\s+/, "");
|
|
114
|
+
const [head = ""] = normalizedContent.split(/\r?\n/, 1);
|
|
115
|
+
return ["telegram_voice", "telegram_button"].some((command) => {
|
|
116
|
+
if (!head.startsWith(command)) return false;
|
|
117
|
+
const nextChar = head[command.length];
|
|
118
|
+
return nextChar === undefined || /\s|:/.test(nextChar);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
109
122
|
function getMarkdownLineEnd(markdown: string, offset: number): number {
|
|
110
123
|
const newlineIndex = markdown.indexOf("\n", offset);
|
|
111
124
|
return newlineIndex === -1 ? markdown.length : newlineIndex + 1;
|
|
@@ -144,6 +157,28 @@ function isTopLevelClosingFence(
|
|
|
144
157
|
);
|
|
145
158
|
}
|
|
146
159
|
|
|
160
|
+
function collectInlineClosedTelegramActionBody(
|
|
161
|
+
markdown: string,
|
|
162
|
+
bodyStart: number,
|
|
163
|
+
commentContent: string,
|
|
164
|
+
): { content: string; end: number } | undefined {
|
|
165
|
+
const bodyLineEnd = getMarkdownLineEnd(markdown, bodyStart);
|
|
166
|
+
const bodyLine = getMarkdownLineText(markdown, bodyStart, bodyLineEnd);
|
|
167
|
+
const closeLineEnd = getMarkdownLineEnd(markdown, bodyLineEnd);
|
|
168
|
+
const closeLine = getMarkdownLineText(markdown, bodyLineEnd, closeLineEnd);
|
|
169
|
+
const hasRecoverableBody =
|
|
170
|
+
isTelegramActionCommentContent(commentContent) &&
|
|
171
|
+
bodyLine.trim() !== "" &&
|
|
172
|
+
!bodyLine.startsWith("<!--") &&
|
|
173
|
+
!bodyLine.startsWith("-->") &&
|
|
174
|
+
closeLine === "-->";
|
|
175
|
+
if (!hasRecoverableBody) return undefined;
|
|
176
|
+
return {
|
|
177
|
+
content: `${commentContent.trimEnd()}\n${bodyLine}`,
|
|
178
|
+
end: bodyLineEnd + 3,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
147
182
|
function collectTopLevelHtmlComments(markdown: string): {
|
|
148
183
|
comments: TelegramTopLevelHtmlComment[];
|
|
149
184
|
openCommentStart?: number;
|
|
@@ -168,9 +203,23 @@ function collectTopLevelHtmlComments(markdown: string): {
|
|
|
168
203
|
if (line.startsWith("<!--")) {
|
|
169
204
|
const closeIndex = markdown.indexOf("-->", offset + 4);
|
|
170
205
|
if (closeIndex === -1) return { comments, openCommentStart: offset };
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
206
|
+
let end = closeIndex + 3;
|
|
207
|
+
let raw = markdown.slice(offset, end);
|
|
208
|
+
let content = raw.slice(4, -3);
|
|
209
|
+
const closeColumn = closeIndex - offset;
|
|
210
|
+
const closesOnOpeningLine = closeIndex < lineEnd;
|
|
211
|
+
const hasOnlyWhitespaceAfterClose =
|
|
212
|
+
line.slice(closeColumn + 3).trim() === "";
|
|
213
|
+
const inlineBody =
|
|
214
|
+
closesOnOpeningLine && hasOnlyWhitespaceAfterClose
|
|
215
|
+
? collectInlineClosedTelegramActionBody(markdown, lineEnd, content)
|
|
216
|
+
: undefined;
|
|
217
|
+
if (inlineBody) {
|
|
218
|
+
end = inlineBody.end;
|
|
219
|
+
raw = markdown.slice(offset, end);
|
|
220
|
+
content = inlineBody.content;
|
|
221
|
+
}
|
|
222
|
+
comments.push({ raw, content, start: offset, end });
|
|
174
223
|
offset = getMarkdownLineEnd(markdown, end);
|
|
175
224
|
continue;
|
|
176
225
|
}
|
|
@@ -239,18 +288,29 @@ function parseTopLevelTelegramComment(
|
|
|
239
288
|
};
|
|
240
289
|
}
|
|
241
290
|
|
|
291
|
+
function parseTelegramCommentAttributes(input: string): Record<string, string> {
|
|
292
|
+
const attributes: Record<string, string> = {};
|
|
293
|
+
for (const match of input.matchAll(
|
|
294
|
+
/([A-Za-z_][A-Za-z0-9_-]*)=(?:"([^"]*)"|'([^']*)'|(\S+))/g,
|
|
295
|
+
)) {
|
|
296
|
+
const key = match[1];
|
|
297
|
+
const value = (match[2] ?? match[3] ?? match[4] ?? "").trim();
|
|
298
|
+
if (value) attributes[key] = value;
|
|
299
|
+
}
|
|
300
|
+
return attributes;
|
|
301
|
+
}
|
|
302
|
+
|
|
242
303
|
function parseVoiceReplyAttributes(input: string): {
|
|
243
304
|
lang?: string;
|
|
244
305
|
rate?: string;
|
|
306
|
+
text?: string;
|
|
245
307
|
} {
|
|
246
|
-
const attributes
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
return attributes;
|
|
308
|
+
const attributes = parseTelegramCommentAttributes(input);
|
|
309
|
+
return {
|
|
310
|
+
...(attributes.lang ? { lang: attributes.lang } : {}),
|
|
311
|
+
...(attributes.rate ? { rate: attributes.rate } : {}),
|
|
312
|
+
...(attributes.text ? { text: attributes.text } : {}),
|
|
313
|
+
};
|
|
254
314
|
}
|
|
255
315
|
|
|
256
316
|
function parseVoiceCommentBody(
|
|
@@ -267,7 +327,8 @@ function parseVoiceCommentBody(
|
|
|
267
327
|
if (trimmedHead.startsWith(":")) {
|
|
268
328
|
return { attrs: "", text: trimmedHead.slice(1).trim() };
|
|
269
329
|
}
|
|
270
|
-
|
|
330
|
+
const attrs = parseVoiceReplyAttributes(trimmedHead);
|
|
331
|
+
return { attrs: trimmedHead, text: attrs.text ?? "" };
|
|
271
332
|
}
|
|
272
333
|
|
|
273
334
|
function normalizeMarkdownAfterVoiceExtraction(markdown: string): string {
|
|
@@ -415,6 +476,7 @@ async function runVoiceReplyCommand(
|
|
|
415
476
|
{
|
|
416
477
|
cwd: options.cwd,
|
|
417
478
|
timeout: options.timeout,
|
|
479
|
+
...(typeof config === "object" && config.retry !== undefined ? { retry: config.retry } : {}),
|
|
418
480
|
...(options.stdin !== undefined ? { stdin: options.stdin } : {}),
|
|
419
481
|
},
|
|
420
482
|
);
|
|
@@ -539,22 +601,27 @@ async function generateTelegramVoiceReplyFileWithHandler(
|
|
|
539
601
|
const startedAt = Date.now();
|
|
540
602
|
let stdout = "";
|
|
541
603
|
for (const [index, step] of steps.entries()) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
604
|
+
try {
|
|
605
|
+
const result = await runVoiceReplyCommand(
|
|
606
|
+
`Outbound voice template step ${index + 1}`,
|
|
607
|
+
step,
|
|
608
|
+
values,
|
|
609
|
+
{
|
|
610
|
+
cwd: options.cwd,
|
|
611
|
+
timeout: getVoiceReplyCompositionStepTimeout(
|
|
612
|
+
options.timeout,
|
|
613
|
+
step,
|
|
614
|
+
startedAt,
|
|
615
|
+
),
|
|
616
|
+
execCommand: options.execCommand,
|
|
617
|
+
...(index === 0 ? {} : { stdin: stdout }),
|
|
618
|
+
},
|
|
619
|
+
);
|
|
620
|
+
stdout = result.stdout;
|
|
621
|
+
} catch (error) {
|
|
622
|
+
if (typeof step === "object" && step.critical) throw error;
|
|
623
|
+
stdout = "";
|
|
624
|
+
}
|
|
558
625
|
}
|
|
559
626
|
return getVoiceReplyOutputPath(options.handler, values, stdout);
|
|
560
627
|
}
|
|
@@ -664,9 +731,7 @@ export interface TelegramOutboundButtonStoredAction extends TelegramOutboundButt
|
|
|
664
731
|
createdAt: number;
|
|
665
732
|
}
|
|
666
733
|
|
|
667
|
-
export
|
|
668
|
-
inline_keyboard: Array<Array<{ text: string; callback_data: string }>>;
|
|
669
|
-
}
|
|
734
|
+
export type TelegramOutboundButtonMarkup = TelegramInlineKeyboardMarkup;
|
|
670
735
|
|
|
671
736
|
export interface TelegramButtonReplyPlan {
|
|
672
737
|
markdown: string;
|
|
@@ -712,26 +777,36 @@ function normalizeMarkdownAfterButtonExtraction(markdown: string): string {
|
|
|
712
777
|
return markdown.replace(/\n{3,}/g, "\n\n").trim();
|
|
713
778
|
}
|
|
714
779
|
|
|
715
|
-
function parseButtonsCommentAttributes(input: string): {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
)
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
724
|
-
return attributes;
|
|
780
|
+
function parseButtonsCommentAttributes(input: string): {
|
|
781
|
+
label?: string;
|
|
782
|
+
prompt?: string;
|
|
783
|
+
} {
|
|
784
|
+
const attributes = parseTelegramCommentAttributes(input);
|
|
785
|
+
return {
|
|
786
|
+
...(attributes.label ? { label: attributes.label } : {}),
|
|
787
|
+
...(attributes.prompt ? { prompt: attributes.prompt } : {}),
|
|
788
|
+
};
|
|
725
789
|
}
|
|
726
790
|
|
|
727
791
|
function parseButtonsCommentRows(
|
|
728
792
|
head: string,
|
|
729
793
|
body: string | undefined,
|
|
730
794
|
): TelegramOutboundButtonAction[][] {
|
|
731
|
-
const
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
795
|
+
const trimmedHead = head.trim();
|
|
796
|
+
if (body === undefined) {
|
|
797
|
+
if (trimmedHead.startsWith(":")) {
|
|
798
|
+
const label = trimmedHead.slice(1).trim();
|
|
799
|
+
return label ? [[{ text: label, prompt: label }]] : [];
|
|
800
|
+
}
|
|
801
|
+
const attributes = parseButtonsCommentAttributes(head);
|
|
802
|
+
return attributes.label && attributes.prompt
|
|
803
|
+
? [[{ text: attributes.label, prompt: attributes.prompt }]]
|
|
804
|
+
: [];
|
|
805
|
+
}
|
|
806
|
+
const label = parseButtonsCommentAttributes(head).label;
|
|
807
|
+
const prompt = body.trim();
|
|
808
|
+
if (!label || !prompt) return [];
|
|
809
|
+
return [[{ text: label, prompt }]];
|
|
735
810
|
}
|
|
736
811
|
|
|
737
812
|
export function createTelegramButtonActionStore(
|
package/lib/pi.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi SDK adapter boundary
|
|
3
|
+
* Zones: pi agent sdk boundary, shared adapters
|
|
3
4
|
* Owns direct pi SDK imports and exposes narrow bridge-facing helpers/types for the extension composition layer
|
|
4
5
|
*/
|
|
5
6
|
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
type ExtensionContext,
|
|
13
14
|
type SessionShutdownEvent,
|
|
14
15
|
type SessionStartEvent,
|
|
16
|
+
type SlashCommandInfo,
|
|
15
17
|
SettingsManager,
|
|
16
18
|
} from "@mariozechner/pi-coding-agent";
|
|
17
19
|
|
|
@@ -24,6 +26,7 @@ export type {
|
|
|
24
26
|
ExtensionContext,
|
|
25
27
|
SessionShutdownEvent,
|
|
26
28
|
SessionStartEvent,
|
|
29
|
+
SlashCommandInfo,
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
export interface PiSettingsManager {
|
|
@@ -31,9 +34,12 @@ export interface PiSettingsManager {
|
|
|
31
34
|
getEnabledModels: () => string[] | undefined;
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
export type PiSlashCommandInfo = SlashCommandInfo;
|
|
38
|
+
|
|
34
39
|
export interface PiExtensionApiRuntimePorts {
|
|
35
40
|
sendUserMessage: ExtensionAPI["sendUserMessage"];
|
|
36
41
|
exec: ExtensionAPI["exec"];
|
|
42
|
+
getCommands: ExtensionAPI["getCommands"];
|
|
37
43
|
getThinkingLevel: ExtensionAPI["getThinkingLevel"];
|
|
38
44
|
setThinkingLevel: ExtensionAPI["setThinkingLevel"];
|
|
39
45
|
setModel: ExtensionAPI["setModel"];
|
|
@@ -44,6 +50,7 @@ export function createExtensionApiRuntimePorts(
|
|
|
44
50
|
ExtensionAPI,
|
|
45
51
|
| "sendUserMessage"
|
|
46
52
|
| "exec"
|
|
53
|
+
| "getCommands"
|
|
47
54
|
| "getThinkingLevel"
|
|
48
55
|
| "setThinkingLevel"
|
|
49
56
|
| "setModel"
|
|
@@ -52,6 +59,7 @@ export function createExtensionApiRuntimePorts(
|
|
|
52
59
|
return {
|
|
53
60
|
sendUserMessage: (content) => api.sendUserMessage(content),
|
|
54
61
|
exec: (command, args, options) => api.exec(command, args, options),
|
|
62
|
+
getCommands: () => api.getCommands(),
|
|
55
63
|
getThinkingLevel: () => api.getThinkingLevel(),
|
|
56
64
|
setThinkingLevel: (level) => api.setThinkingLevel(level),
|
|
57
65
|
setModel: (model) => api.setModel(model),
|
package/lib/polling.ts
CHANGED
package/lib/preview.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram preview streaming helpers
|
|
3
|
+
* Zones: telegram outbound, streaming preview, rendering
|
|
3
4
|
* Owns preview transport selection, runtime updates, and preview finalization
|
|
4
5
|
*/
|
|
5
6
|
|
|
@@ -44,9 +45,11 @@ export interface TelegramPreviewRuntimeState extends TelegramPreviewState {
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export type TelegramSentPreviewMessage = TelegramSentMessage;
|
|
47
|
-
export type TelegramPreviewReplyMarkup =
|
|
48
|
+
export type TelegramPreviewReplyMarkup = unknown;
|
|
48
49
|
|
|
49
|
-
export interface TelegramPreviewRuntimeDeps
|
|
50
|
+
export interface TelegramPreviewRuntimeDeps<
|
|
51
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
52
|
+
> {
|
|
50
53
|
getState: () => TelegramPreviewRuntimeState | undefined;
|
|
51
54
|
setState: (state: TelegramPreviewRuntimeState | undefined) => void;
|
|
52
55
|
clearScheduledFlush: (state: TelegramPreviewRuntimeState) => void;
|
|
@@ -78,13 +81,13 @@ export interface TelegramPreviewRuntimeDeps {
|
|
|
78
81
|
sendRenderedChunks: (
|
|
79
82
|
chatId: number,
|
|
80
83
|
chunks: TelegramRenderedChunk[],
|
|
81
|
-
options?: { replyMarkup?:
|
|
84
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
82
85
|
) => Promise<number | undefined>;
|
|
83
86
|
editRenderedMessage: (
|
|
84
87
|
chatId: number,
|
|
85
88
|
messageId: number,
|
|
86
89
|
chunks: TelegramRenderedChunk[],
|
|
87
|
-
options?: { replyMarkup?:
|
|
90
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
88
91
|
) => Promise<number | undefined>;
|
|
89
92
|
}
|
|
90
93
|
|
|
@@ -92,7 +95,10 @@ export interface TelegramPreviewActiveTurn {
|
|
|
92
95
|
chatId: number;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
export interface TelegramAssistantMessagePreviewStartDeps<
|
|
98
|
+
export interface TelegramAssistantMessagePreviewStartDeps<
|
|
99
|
+
TMessage,
|
|
100
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
101
|
+
> {
|
|
96
102
|
getActiveTurn: () => TelegramPreviewActiveTurn | undefined;
|
|
97
103
|
isAssistantMessage: (message: TMessage) => boolean;
|
|
98
104
|
getState: () => TelegramPreviewRuntimeState | undefined;
|
|
@@ -102,6 +108,8 @@ export interface TelegramAssistantMessagePreviewStartDeps<TMessage> {
|
|
|
102
108
|
finalizeMarkdownPreview: (
|
|
103
109
|
chatId: number,
|
|
104
110
|
markdown: string,
|
|
111
|
+
replyToMessageId?: number,
|
|
112
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
105
113
|
) => Promise<boolean>;
|
|
106
114
|
}
|
|
107
115
|
|
|
@@ -115,9 +123,11 @@ export interface TelegramAssistantMessagePreviewUpdateDeps<TMessage> {
|
|
|
115
123
|
schedulePreviewFlush: (chatId: number) => void;
|
|
116
124
|
}
|
|
117
125
|
|
|
118
|
-
export type TelegramAssistantMessagePreviewHookDeps<
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
export type TelegramAssistantMessagePreviewHookDeps<
|
|
127
|
+
TMessage,
|
|
128
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
129
|
+
> = TelegramAssistantMessagePreviewStartDeps<TMessage, TReplyMarkup> &
|
|
130
|
+
TelegramAssistantMessagePreviewUpdateDeps<TMessage>;
|
|
121
131
|
|
|
122
132
|
export interface TelegramAssistantMessagePreviewHookEvent<TMessage> {
|
|
123
133
|
message: TMessage;
|
|
@@ -132,7 +142,9 @@ export interface TelegramAssistantMessagePreviewHooks<TMessage> {
|
|
|
132
142
|
) => Promise<void>;
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
export interface TelegramPreviewControllerDeps
|
|
145
|
+
export interface TelegramPreviewControllerDeps<
|
|
146
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
147
|
+
> {
|
|
136
148
|
getDefaultReplyToMessageId?: () => number | undefined;
|
|
137
149
|
maxMessageLength?: number;
|
|
138
150
|
renderPreviewText?: (markdown: string) => string;
|
|
@@ -162,13 +174,13 @@ export interface TelegramPreviewControllerDeps {
|
|
|
162
174
|
chatId: number,
|
|
163
175
|
chunks: TelegramRenderedChunk[],
|
|
164
176
|
replyToMessageId: number | undefined,
|
|
165
|
-
options?: { replyMarkup?:
|
|
177
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
166
178
|
) => Promise<number | undefined>;
|
|
167
179
|
editRenderedMessage: (
|
|
168
180
|
chatId: number,
|
|
169
181
|
messageId: number,
|
|
170
182
|
chunks: TelegramRenderedChunk[],
|
|
171
|
-
options?: { replyMarkup?:
|
|
183
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
172
184
|
) => Promise<number | undefined>;
|
|
173
185
|
throttleMs?: number;
|
|
174
186
|
maxDraftId?: number;
|
|
@@ -179,7 +191,9 @@ export interface TelegramPreviewControllerDeps {
|
|
|
179
191
|
clearTimer?: (timer: ReturnType<typeof setTimeout>) => void;
|
|
180
192
|
}
|
|
181
193
|
|
|
182
|
-
export interface TelegramPreviewController
|
|
194
|
+
export interface TelegramPreviewController<
|
|
195
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
196
|
+
> {
|
|
183
197
|
getState: () => TelegramPreviewRuntimeState | undefined;
|
|
184
198
|
setState: (state: TelegramPreviewRuntimeState | undefined) => void;
|
|
185
199
|
setPendingText: (text: string) => void;
|
|
@@ -193,7 +207,7 @@ export interface TelegramPreviewController {
|
|
|
193
207
|
chatId: number,
|
|
194
208
|
markdown: string,
|
|
195
209
|
replyToMessageId?: number,
|
|
196
|
-
options?: { replyMarkup?:
|
|
210
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
197
211
|
) => Promise<boolean>;
|
|
198
212
|
}
|
|
199
213
|
|
|
@@ -230,27 +244,31 @@ export function createTelegramPreviewMessageTransport(
|
|
|
230
244
|
};
|
|
231
245
|
}
|
|
232
246
|
|
|
233
|
-
export interface TelegramPreviewRenderedChunkTransportDeps
|
|
247
|
+
export interface TelegramPreviewRenderedChunkTransportDeps<
|
|
248
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
249
|
+
> {
|
|
234
250
|
sendRenderedChunks: (
|
|
235
251
|
chatId: number,
|
|
236
252
|
chunks: TelegramRenderedChunk[],
|
|
237
253
|
options?: {
|
|
238
254
|
replyToMessageId?: number;
|
|
239
|
-
replyMarkup?:
|
|
255
|
+
replyMarkup?: TReplyMarkup;
|
|
240
256
|
},
|
|
241
257
|
) => Promise<number | undefined>;
|
|
242
258
|
editRenderedMessage: (
|
|
243
259
|
chatId: number,
|
|
244
260
|
messageId: number,
|
|
245
261
|
chunks: TelegramRenderedChunk[],
|
|
246
|
-
options?: { replyMarkup?:
|
|
262
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
247
263
|
) => Promise<number | undefined>;
|
|
248
264
|
}
|
|
249
265
|
|
|
250
|
-
export function createTelegramPreviewRenderedChunkTransport
|
|
251
|
-
|
|
266
|
+
export function createTelegramPreviewRenderedChunkTransport<
|
|
267
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
268
|
+
>(
|
|
269
|
+
deps: TelegramPreviewRenderedChunkTransportDeps<TReplyMarkup>,
|
|
252
270
|
): Pick<
|
|
253
|
-
TelegramPreviewControllerDeps
|
|
271
|
+
TelegramPreviewControllerDeps<TReplyMarkup>,
|
|
254
272
|
"sendRenderedChunks" | "editRenderedMessage"
|
|
255
273
|
> {
|
|
256
274
|
return {
|
|
@@ -264,19 +282,23 @@ export function createTelegramPreviewRenderedChunkTransport(
|
|
|
264
282
|
};
|
|
265
283
|
}
|
|
266
284
|
|
|
267
|
-
export type TelegramPreviewControllerRuntimeDeps
|
|
268
|
-
|
|
285
|
+
export type TelegramPreviewControllerRuntimeDeps<
|
|
286
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
287
|
+
> = Omit<
|
|
288
|
+
TelegramPreviewControllerDeps<TReplyMarkup>,
|
|
269
289
|
| "sendMessage"
|
|
270
290
|
| "editMessageText"
|
|
271
291
|
| "sendRenderedChunks"
|
|
272
292
|
| "editRenderedMessage"
|
|
273
293
|
> &
|
|
274
294
|
TelegramPreviewMessageTransportDeps &
|
|
275
|
-
TelegramPreviewRenderedChunkTransportDeps
|
|
295
|
+
TelegramPreviewRenderedChunkTransportDeps<TReplyMarkup>;
|
|
276
296
|
|
|
277
|
-
export function createTelegramPreviewControllerRuntime
|
|
278
|
-
|
|
279
|
-
|
|
297
|
+
export function createTelegramPreviewControllerRuntime<
|
|
298
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
299
|
+
>(
|
|
300
|
+
deps: TelegramPreviewControllerRuntimeDeps<TReplyMarkup>,
|
|
301
|
+
): TelegramPreviewController<TReplyMarkup> {
|
|
280
302
|
return createTelegramPreviewController({
|
|
281
303
|
getDefaultReplyToMessageId: deps.getDefaultReplyToMessageId,
|
|
282
304
|
maxMessageLength: deps.maxMessageLength,
|
|
@@ -302,18 +324,25 @@ export function createTelegramPreviewControllerRuntime(
|
|
|
302
324
|
|
|
303
325
|
export interface TelegramAssistantPreviewRuntimeDeps<
|
|
304
326
|
TMessage,
|
|
305
|
-
|
|
327
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
328
|
+
> extends TelegramPreviewControllerRuntimeDeps<TReplyMarkup> {
|
|
306
329
|
getActiveTurn: () => TelegramPreviewActiveTurn | undefined;
|
|
307
330
|
isAssistantMessage: (message: TMessage) => boolean;
|
|
308
331
|
getMessageText: (message: TMessage) => string;
|
|
309
332
|
}
|
|
310
333
|
|
|
311
|
-
export type TelegramAssistantPreviewRuntime<
|
|
312
|
-
|
|
334
|
+
export type TelegramAssistantPreviewRuntime<
|
|
335
|
+
TMessage,
|
|
336
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
337
|
+
> = TelegramPreviewController<TReplyMarkup> &
|
|
338
|
+
TelegramAssistantMessagePreviewHooks<TMessage>;
|
|
313
339
|
|
|
314
|
-
export function createTelegramAssistantPreviewRuntime<
|
|
315
|
-
|
|
316
|
-
|
|
340
|
+
export function createTelegramAssistantPreviewRuntime<
|
|
341
|
+
TMessage,
|
|
342
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
343
|
+
>(
|
|
344
|
+
deps: TelegramAssistantPreviewRuntimeDeps<TMessage, TReplyMarkup>,
|
|
345
|
+
): TelegramAssistantPreviewRuntime<TMessage, TReplyMarkup> {
|
|
317
346
|
const controller = createTelegramPreviewControllerRuntime(deps);
|
|
318
347
|
return {
|
|
319
348
|
...controller,
|
|
@@ -331,9 +360,11 @@ export function createTelegramAssistantPreviewRuntime<TMessage>(
|
|
|
331
360
|
};
|
|
332
361
|
}
|
|
333
362
|
|
|
334
|
-
export function createTelegramPreviewController
|
|
335
|
-
|
|
336
|
-
|
|
363
|
+
export function createTelegramPreviewController<
|
|
364
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
365
|
+
>(
|
|
366
|
+
deps: TelegramPreviewControllerDeps<TReplyMarkup>,
|
|
367
|
+
): TelegramPreviewController<TReplyMarkup> {
|
|
337
368
|
let state: TelegramPreviewRuntimeState | undefined;
|
|
338
369
|
const clearTimer = deps.clearTimer ?? clearTimeout;
|
|
339
370
|
const setTimer =
|
|
@@ -349,7 +380,7 @@ export function createTelegramPreviewController(
|
|
|
349
380
|
let nextDraftId = 0;
|
|
350
381
|
const getRuntimeDeps = (
|
|
351
382
|
replyToMessageId?: number,
|
|
352
|
-
): TelegramPreviewRuntimeDeps => ({
|
|
383
|
+
): TelegramPreviewRuntimeDeps<TReplyMarkup> => ({
|
|
353
384
|
getState: () => state,
|
|
354
385
|
setState: (nextState) => {
|
|
355
386
|
state = nextState;
|
|
@@ -420,8 +451,11 @@ export function createTelegramPreviewController(
|
|
|
420
451
|
};
|
|
421
452
|
}
|
|
422
453
|
|
|
423
|
-
export function createTelegramAssistantMessagePreviewHooks<
|
|
424
|
-
|
|
454
|
+
export function createTelegramAssistantMessagePreviewHooks<
|
|
455
|
+
TMessage,
|
|
456
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
457
|
+
>(
|
|
458
|
+
deps: TelegramAssistantMessagePreviewHookDeps<TMessage, TReplyMarkup>,
|
|
425
459
|
): TelegramAssistantMessagePreviewHooks<TMessage> {
|
|
426
460
|
return {
|
|
427
461
|
onMessageStart: async (
|
|
@@ -437,9 +471,12 @@ export function createTelegramAssistantMessagePreviewHooks<TMessage>(
|
|
|
437
471
|
};
|
|
438
472
|
}
|
|
439
473
|
|
|
440
|
-
export async function handleTelegramAssistantMessagePreviewStart<
|
|
474
|
+
export async function handleTelegramAssistantMessagePreviewStart<
|
|
475
|
+
TMessage,
|
|
476
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
477
|
+
>(
|
|
441
478
|
message: TMessage,
|
|
442
|
-
deps: TelegramAssistantMessagePreviewStartDeps<TMessage>,
|
|
479
|
+
deps: TelegramAssistantMessagePreviewStartDeps<TMessage, TReplyMarkup>,
|
|
443
480
|
): Promise<void> {
|
|
444
481
|
const turn = deps.getActiveTurn();
|
|
445
482
|
if (!turn || !deps.isAssistantMessage(message)) return;
|
|
@@ -517,9 +554,11 @@ export function shouldUseTelegramDraftPreview(options: {
|
|
|
517
554
|
);
|
|
518
555
|
}
|
|
519
556
|
|
|
520
|
-
export async function clearTelegramPreview
|
|
557
|
+
export async function clearTelegramPreview<
|
|
558
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
559
|
+
>(
|
|
521
560
|
chatId: number,
|
|
522
|
-
deps: TelegramPreviewRuntimeDeps
|
|
561
|
+
deps: TelegramPreviewRuntimeDeps<TReplyMarkup>,
|
|
523
562
|
): Promise<void> {
|
|
524
563
|
void chatId;
|
|
525
564
|
const state = deps.getState();
|
|
@@ -528,10 +567,12 @@ export async function clearTelegramPreview(
|
|
|
528
567
|
deps.setState(undefined);
|
|
529
568
|
}
|
|
530
569
|
|
|
531
|
-
async function performTelegramPreviewFlush
|
|
570
|
+
async function performTelegramPreviewFlush<
|
|
571
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
572
|
+
>(
|
|
532
573
|
chatId: number,
|
|
533
574
|
state: TelegramPreviewRuntimeState,
|
|
534
|
-
deps: TelegramPreviewRuntimeDeps
|
|
575
|
+
deps: TelegramPreviewRuntimeDeps<TReplyMarkup>,
|
|
535
576
|
): Promise<void> {
|
|
536
577
|
const snapshot = buildTelegramPreviewSnapshot({
|
|
537
578
|
state,
|
|
@@ -580,9 +621,11 @@ async function performTelegramPreviewFlush(
|
|
|
580
621
|
state.lastSentStrategy = snapshot.strategy;
|
|
581
622
|
}
|
|
582
623
|
|
|
583
|
-
export async function flushTelegramPreview
|
|
624
|
+
export async function flushTelegramPreview<
|
|
625
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
626
|
+
>(
|
|
584
627
|
chatId: number,
|
|
585
|
-
deps: TelegramPreviewRuntimeDeps
|
|
628
|
+
deps: TelegramPreviewRuntimeDeps<TReplyMarkup>,
|
|
586
629
|
): Promise<void> {
|
|
587
630
|
const state = deps.getState();
|
|
588
631
|
if (!state) return;
|
|
@@ -607,9 +650,11 @@ export async function flushTelegramPreview(
|
|
|
607
650
|
}
|
|
608
651
|
}
|
|
609
652
|
|
|
610
|
-
export async function finalizeTelegramPreview
|
|
653
|
+
export async function finalizeTelegramPreview<
|
|
654
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
655
|
+
>(
|
|
611
656
|
chatId: number,
|
|
612
|
-
deps: TelegramPreviewRuntimeDeps
|
|
657
|
+
deps: TelegramPreviewRuntimeDeps<TReplyMarkup>,
|
|
613
658
|
): Promise<boolean> {
|
|
614
659
|
const state = deps.getState();
|
|
615
660
|
if (!state) return false;
|
|
@@ -628,11 +673,13 @@ export async function finalizeTelegramPreview(
|
|
|
628
673
|
return state.messageId !== undefined;
|
|
629
674
|
}
|
|
630
675
|
|
|
631
|
-
export async function finalizeTelegramMarkdownPreview
|
|
676
|
+
export async function finalizeTelegramMarkdownPreview<
|
|
677
|
+
TReplyMarkup = TelegramPreviewReplyMarkup,
|
|
678
|
+
>(
|
|
632
679
|
chatId: number,
|
|
633
680
|
markdown: string,
|
|
634
|
-
deps: TelegramPreviewRuntimeDeps
|
|
635
|
-
options?: { replyMarkup?:
|
|
681
|
+
deps: TelegramPreviewRuntimeDeps<TReplyMarkup>,
|
|
682
|
+
options?: { replyMarkup?: TReplyMarkup },
|
|
636
683
|
): Promise<boolean> {
|
|
637
684
|
const state = deps.getState();
|
|
638
685
|
if (!state) return false;
|