@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
|
@@ -8,12 +8,13 @@ import { unlink } from "node:fs/promises";
|
|
|
8
8
|
import { basename, extname } from "node:path";
|
|
9
9
|
|
|
10
10
|
import { assertTelegramInlineKeyboardCallbackData } from "./keyboard.ts";
|
|
11
|
-
import {
|
|
11
|
+
import { withTelegramReplyParameters } from "./replies.ts";
|
|
12
12
|
import {
|
|
13
13
|
getTelegramTargetThreadParams,
|
|
14
14
|
type TelegramTarget,
|
|
15
15
|
} from "./target.ts";
|
|
16
16
|
import { getTelegramVoiceSynthesisProviders } from "./voice.ts";
|
|
17
|
+
import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
|
|
17
18
|
|
|
18
19
|
export interface TelegramVoiceReplyTurnView {
|
|
19
20
|
chatId: number;
|
|
@@ -47,6 +48,7 @@ export interface TelegramVoiceReplySenderDeps {
|
|
|
47
48
|
) => Promise<unknown>;
|
|
48
49
|
sendChatAction?: (chatId: number, action: string) => Promise<unknown>;
|
|
49
50
|
sendRecordVoiceAction?: (chatId: number) => Promise<unknown>;
|
|
51
|
+
isDeliveryActive?: () => boolean;
|
|
50
52
|
getHandlers?: () => unknown[] | undefined;
|
|
51
53
|
cwd?: string;
|
|
52
54
|
tempDir?: string;
|
|
@@ -78,17 +80,6 @@ export interface TelegramVoiceReplySenderPorts<THandler = unknown> {
|
|
|
78
80
|
getProgrammaticVoiceHandlers?: () => TelegramOutboundProgrammaticVoiceHandler[];
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
function buildVoiceReplyParameters(
|
|
82
|
-
chatId: number,
|
|
83
|
-
replyToPrompt: boolean | undefined,
|
|
84
|
-
replyToMessageId: number | undefined,
|
|
85
|
-
target?: TelegramTarget,
|
|
86
|
-
): string | undefined {
|
|
87
|
-
if (replyToPrompt === false || replyToMessageId === undefined)
|
|
88
|
-
return undefined;
|
|
89
|
-
return buildTelegramMultipartReplyParameters(chatId, replyToMessageId, target);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
83
|
async function ensureTelegramVoiceFileFormat(
|
|
93
84
|
filePath: string,
|
|
94
85
|
): Promise<string> {
|
|
@@ -123,39 +114,39 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
123
114
|
replyMarkup?: unknown;
|
|
124
115
|
},
|
|
125
116
|
): Promise<void> => {
|
|
117
|
+
if (deps.isDeliveryActive?.() === false) return;
|
|
126
118
|
const voiceFilePath = await ensureTelegramVoiceFileFormat(filePath);
|
|
127
119
|
assertTelegramInlineKeyboardCallbackData(options?.replyMarkup);
|
|
120
|
+
if (deps.isDeliveryActive?.() === false) return;
|
|
128
121
|
await sendVoiceChatAction(deps, turn.chatId);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
options?.replyToPrompt,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
voiceFilePath,
|
|
158
|
-
basename(voiceFilePath),
|
|
122
|
+
if (deps.isDeliveryActive?.() === false) return;
|
|
123
|
+
await withTelegramReplyParameters(
|
|
124
|
+
turn.chatId, options?.replyToPrompt === false ? undefined : turn.replyToMessageId, turn.target,
|
|
125
|
+
(replyParameters) => deps.sendMultipart(
|
|
126
|
+
"sendVoice",
|
|
127
|
+
{
|
|
128
|
+
chat_id: String(turn.chatId),
|
|
129
|
+
...(replyParameters ? { reply_parameters: JSON.stringify(replyParameters) } : {}),
|
|
130
|
+
...(turn.target
|
|
131
|
+
? Object.fromEntries(
|
|
132
|
+
Object.entries(getTelegramTargetThreadParams(turn.target)).map(
|
|
133
|
+
([key, value]) => [key, String(value)],
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
: {}),
|
|
137
|
+
...(options?.replyMarkup !== undefined && options.replyMarkup !== null
|
|
138
|
+
? {
|
|
139
|
+
reply_markup:
|
|
140
|
+
typeof options.replyMarkup === "string"
|
|
141
|
+
? options.replyMarkup
|
|
142
|
+
: JSON.stringify(options.replyMarkup),
|
|
143
|
+
}
|
|
144
|
+
: {}),
|
|
145
|
+
},
|
|
146
|
+
"voice",
|
|
147
|
+
voiceFilePath,
|
|
148
|
+
basename(voiceFilePath),
|
|
149
|
+
),
|
|
159
150
|
);
|
|
160
151
|
};
|
|
161
152
|
|
|
@@ -171,6 +162,7 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
171
162
|
): Promise<void> => {
|
|
172
163
|
for (const handler of ports.findVoiceHandlers?.(deps.getHandlers?.()) ??
|
|
173
164
|
[]) {
|
|
165
|
+
if (deps.isDeliveryActive?.() === false) return;
|
|
174
166
|
try {
|
|
175
167
|
const filePath = await ports.generateVoiceFile?.(text, {
|
|
176
168
|
lang: options?.lang,
|
|
@@ -187,6 +179,7 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
187
179
|
});
|
|
188
180
|
return;
|
|
189
181
|
} catch (error) {
|
|
182
|
+
if (isTelegramApiCommitUnknownError(error)) throw error;
|
|
190
183
|
deps.recordRuntimeEvent?.("voice", error, {
|
|
191
184
|
phase: "template-handler-send",
|
|
192
185
|
});
|
|
@@ -194,6 +187,7 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
194
187
|
}
|
|
195
188
|
|
|
196
189
|
for (const handler of ports.getProgrammaticVoiceHandlers?.() ?? []) {
|
|
190
|
+
if (deps.isDeliveryActive?.() === false) return;
|
|
197
191
|
try {
|
|
198
192
|
const filePath = await handler(text, {
|
|
199
193
|
lang: options?.lang,
|
|
@@ -206,6 +200,7 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
206
200
|
});
|
|
207
201
|
return;
|
|
208
202
|
} catch (error) {
|
|
203
|
+
if (isTelegramApiCommitUnknownError(error)) throw error;
|
|
209
204
|
deps.recordRuntimeEvent?.("voice", error, {
|
|
210
205
|
phase: "programmatic-handler-send",
|
|
211
206
|
});
|
|
@@ -215,6 +210,7 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
215
210
|
const providers = getTelegramVoiceSynthesisProviders();
|
|
216
211
|
|
|
217
212
|
for (const provider of providers) {
|
|
213
|
+
if (deps.isDeliveryActive?.() === false) return;
|
|
218
214
|
let voiceFilePath: string | undefined;
|
|
219
215
|
let originalFilePath: string | undefined;
|
|
220
216
|
|
|
@@ -252,6 +248,7 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
|
|
|
252
248
|
});
|
|
253
249
|
return;
|
|
254
250
|
} catch (error) {
|
|
251
|
+
if (isTelegramApiCommitUnknownError(error)) throw error;
|
|
255
252
|
deps.recordRuntimeEvent?.("voice", error, { phase: "send" });
|
|
256
253
|
} finally {
|
|
257
254
|
if (voiceFilePath && voiceFilePath !== originalFilePath) {
|
|
@@ -11,6 +11,8 @@ import { join } from "node:path";
|
|
|
11
11
|
import type { TelegramAssistantSegmentEvent } from "./activity.ts";
|
|
12
12
|
import { resolveTelegramTempDir } from "./paths.ts";
|
|
13
13
|
import * as Replies from "./replies.ts";
|
|
14
|
+
import type { TelegramPreparedPreviewDelivery } from "./preview.ts";
|
|
15
|
+
import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
|
|
14
16
|
import type {
|
|
15
17
|
TelegramEditMessageTextBody,
|
|
16
18
|
TelegramSendMessageBody,
|
|
@@ -137,6 +139,7 @@ export interface TelegramVoiceReplySenderDeps {
|
|
|
137
139
|
) => Promise<unknown>;
|
|
138
140
|
sendChatAction?: (chatId: number, action: string) => Promise<unknown>;
|
|
139
141
|
sendRecordVoiceAction?: (chatId: number) => Promise<unknown>;
|
|
142
|
+
isDeliveryActive?: () => boolean;
|
|
140
143
|
getHandlers?: () => TelegramOutboundHandlerConfig[] | undefined;
|
|
141
144
|
cwd?: string;
|
|
142
145
|
tempDir?: string;
|
|
@@ -247,12 +250,8 @@ export interface TelegramOutboundTextPreviewRuntimeDeps<
|
|
|
247
250
|
> {
|
|
248
251
|
execCommand: TelegramVoiceReplySenderDeps["execCommand"];
|
|
249
252
|
getHandlers?: () => TelegramOutboundHandlerConfig[] | undefined;
|
|
250
|
-
finalizeMarkdownPreview:
|
|
251
|
-
|
|
252
|
-
markdown: string,
|
|
253
|
-
replyToMessageId: number,
|
|
254
|
-
options?: { replyMarkup?: TReplyMarkup },
|
|
255
|
-
) => Promise<boolean>;
|
|
253
|
+
finalizeMarkdownPreview: TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"];
|
|
254
|
+
preparePreviewDelivery?: (isDeliveryActive: () => boolean) => TelegramPreparedPreviewDelivery<TReplyMarkup>;
|
|
256
255
|
cwd?: string;
|
|
257
256
|
recordRuntimeEvent?: TelegramVoiceReplySenderDeps["recordRuntimeEvent"];
|
|
258
257
|
}
|
|
@@ -727,12 +726,13 @@ export function createTelegramOutboundTextPreviewRuntime<
|
|
|
727
726
|
TReplyMarkup = unknown,
|
|
728
727
|
>(
|
|
729
728
|
deps: TelegramOutboundTextPreviewRuntimeDeps<TReplyMarkup>,
|
|
730
|
-
):
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
729
|
+
): {
|
|
730
|
+
finalizeMarkdownPreview: TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"];
|
|
731
|
+
preparePreviewDelivery: (isDeliveryActive: () => boolean) => TelegramPreparedPreviewDelivery<TReplyMarkup> | undefined;
|
|
732
|
+
} {
|
|
733
|
+
const wrap = (
|
|
734
|
+
finalize: TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"],
|
|
735
|
+
): TelegramPreparedPreviewDelivery<TReplyMarkup>["finalizeMarkdownPreview"] => async (
|
|
736
736
|
chatId,
|
|
737
737
|
markdown,
|
|
738
738
|
replyToMessageId,
|
|
@@ -745,7 +745,7 @@ export function createTelegramOutboundTextPreviewRuntime<
|
|
|
745
745
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
746
746
|
replyMarkup: options?.replyMarkup,
|
|
747
747
|
});
|
|
748
|
-
return
|
|
748
|
+
return finalize(
|
|
749
749
|
chatId,
|
|
750
750
|
transformed.text,
|
|
751
751
|
replyToMessageId,
|
|
@@ -756,6 +756,12 @@ export function createTelegramOutboundTextPreviewRuntime<
|
|
|
756
756
|
: {}),
|
|
757
757
|
},
|
|
758
758
|
);
|
|
759
|
+
};
|
|
760
|
+
return {
|
|
761
|
+
finalizeMarkdownPreview: wrap(deps.finalizeMarkdownPreview),
|
|
762
|
+
preparePreviewDelivery(isDeliveryActive) {
|
|
763
|
+
const prepared = deps.preparePreviewDelivery?.(isDeliveryActive);
|
|
764
|
+
return prepared ? { ...prepared, finalizeMarkdownPreview: wrap(prepared.finalizeMarkdownPreview) } : undefined;
|
|
759
765
|
},
|
|
760
766
|
};
|
|
761
767
|
}
|
|
@@ -877,15 +883,17 @@ export function createTelegramOutboundReplyPlanner(
|
|
|
877
883
|
export function createTelegramOutboundReplyArtifactSender(
|
|
878
884
|
deps: TelegramVoiceReplySenderDeps,
|
|
879
885
|
) {
|
|
880
|
-
const sendVoiceReply = createTelegramVoiceReplySender(deps);
|
|
881
886
|
return async (
|
|
882
887
|
turn: TelegramVoiceReplyTurnView,
|
|
883
888
|
plan: Pick<
|
|
884
889
|
TelegramOutboundReplyPlan,
|
|
885
890
|
"voiceText" | "voiceReplies" | "lang" | "rate" | "replyMarkup"
|
|
886
891
|
>,
|
|
887
|
-
options?: { replyToPrompt?: boolean },
|
|
892
|
+
options?: { replyToPrompt?: boolean; isDeliveryActive?: () => boolean },
|
|
888
893
|
): Promise<void> => {
|
|
894
|
+
const isDeliveryActive = () =>
|
|
895
|
+
deps.isDeliveryActive?.() !== false && options?.isDeliveryActive?.() !== false;
|
|
896
|
+
const sendVoiceReply = createTelegramVoiceReplySender({ ...deps, isDeliveryActive });
|
|
889
897
|
// Normalize voice replies: either use explicit voiceReplies array or fall back to voiceText
|
|
890
898
|
const voiceReplies = plan.voiceReplies?.length
|
|
891
899
|
? plan.voiceReplies
|
|
@@ -896,6 +904,7 @@ export function createTelegramOutboundReplyArtifactSender(
|
|
|
896
904
|
let anyDelivered = false;
|
|
897
905
|
|
|
898
906
|
for (const reply of voiceReplies) {
|
|
907
|
+
if (!isDeliveryActive()) return;
|
|
899
908
|
try {
|
|
900
909
|
await sendVoiceReply(turn, reply.text, {
|
|
901
910
|
lang: reply.lang ?? plan.lang,
|
|
@@ -905,11 +914,13 @@ export function createTelegramOutboundReplyArtifactSender(
|
|
|
905
914
|
replyMarkup: !anyDelivered ? plan.replyMarkup : undefined,
|
|
906
915
|
});
|
|
907
916
|
anyDelivered = true;
|
|
908
|
-
} catch {
|
|
917
|
+
} catch (error) {
|
|
918
|
+
if (isTelegramApiCommitUnknownError(error)) throw error;
|
|
909
919
|
// sendVoiceReply already recorded the error; continue to next reply
|
|
910
920
|
}
|
|
911
921
|
}
|
|
912
922
|
|
|
923
|
+
if (!isDeliveryActive()) return;
|
|
913
924
|
if (!anyDelivered) {
|
|
914
925
|
throw new Error(
|
|
915
926
|
"Failed to send voice reply: every voice synthesis provider failed.",
|
|
@@ -1011,7 +1022,7 @@ export function createTelegramAssistantOutputSender<
|
|
|
1011
1022
|
};
|
|
1012
1023
|
await outboundRuntime.sendMarkdownReply(
|
|
1013
1024
|
target.chatId,
|
|
1014
|
-
undefined,
|
|
1025
|
+
event.source === "telegram" ? event.replyToMessageId : undefined,
|
|
1015
1026
|
buttonReply.markdown,
|
|
1016
1027
|
{
|
|
1017
1028
|
target,
|