@llblab/pi-kit 0.14.1 → 0.16.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 +11 -0
- package/README.md +3 -3
- package/node_modules/@llblab/pi-codex-usage/CHANGELOG.md +5 -0
- package/node_modules/@llblab/pi-codex-usage/README.md +6 -0
- package/node_modules/@llblab/pi-codex-usage/index.ts +81 -7
- package/node_modules/@llblab/pi-codex-usage/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +9 -9
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +0 -13
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +15 -0
- package/node_modules/@llblab/pi-state-flow/README.md +7 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +5 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +17 -16
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +8 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +24 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +1 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +8 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +4 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +5 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +93 -35
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +20 -23
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +8 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +29 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +34 -18
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +14 -16
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +27 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +182 -10
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +34 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +20 -24
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +6 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +5 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +55 -20
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +8 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +10 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +7 -3
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +11 -3
- package/node_modules/@llblab/pi-state-flow/docs/README.md +1 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +5 -3
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +3 -2
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +504 -0
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +11 -8
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +18 -15
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +4 -3
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +8 -4
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +5 -0
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +86 -34
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +23 -22
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +8 -4
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +31 -3
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +33 -16
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +14 -16
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +173 -9
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +32 -4
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +22 -25
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +11 -6
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +46 -18
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +19 -6
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +7 -3
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +11 -3
- package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +12 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +12 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +3 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +7 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +134 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +297 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +11 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +68 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +17 -13
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +5 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +59 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +23 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +18 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +152 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +5 -3
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +3 -3
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +4 -3
- package/node_modules/@llblab/pi-telegram/docs/sections.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +2 -1
- package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/activity.ts +14 -0
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +10 -2
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +466 -21
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +15 -0
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +77 -9
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +25 -10
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +3 -0
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +7 -1
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +2 -0
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +90 -19
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +30 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +199 -6
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +5 -2
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram command routing helpers
|
|
3
3
|
* Zones: telegram controls, pi agent commands, queue controls
|
|
4
|
-
* Owns Telegram slash-command normalization, bot command metadata,
|
|
4
|
+
* Owns Telegram slash-command normalization, bot command metadata, pi-side command registration, and command-initiated session replacement orchestration behind runtime ports
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
@@ -9,9 +9,11 @@ import {
|
|
|
9
9
|
type TelegramConfigStore,
|
|
10
10
|
TELEGRAM_DEFAULT_PROFILE_NAME,
|
|
11
11
|
} from "./config.ts";
|
|
12
|
+
import type * as Pi from "./pi.ts";
|
|
12
13
|
import type { ExtensionAPI, ExtensionCommandContext } from "./pi.ts";
|
|
13
14
|
import { escapeHtml } from "./rendering.ts";
|
|
14
15
|
import type { TelegramBridgeStatusLineOptions } from "./status.ts";
|
|
16
|
+
import type { TelegramSessionReplacementIntent } from "./threads.ts";
|
|
15
17
|
import {
|
|
16
18
|
createTelegramControlItemBuilder,
|
|
17
19
|
createTelegramControlQueueController,
|
|
@@ -172,6 +174,7 @@ export const TELEGRAM_COMMAND_EMOJI = {
|
|
|
172
174
|
abort: "⏹️",
|
|
173
175
|
stop: "🟥",
|
|
174
176
|
name: "🏷️",
|
|
177
|
+
new: "🆕",
|
|
175
178
|
} as const;
|
|
176
179
|
|
|
177
180
|
export type TelegramCommandEmojiName = keyof typeof TELEGRAM_COMMAND_EMOJI;
|
|
@@ -258,13 +261,6 @@ export const TELEGRAM_BUILTIN_BOT_COMMANDS: readonly TelegramBotCommandDefinitio
|
|
|
258
261
|
"Open menu / Pair bridge",
|
|
259
262
|
),
|
|
260
263
|
},
|
|
261
|
-
{
|
|
262
|
-
command: "name",
|
|
263
|
-
description: formatTelegramBotCommandDescription(
|
|
264
|
-
"name",
|
|
265
|
-
"Rename this thread",
|
|
266
|
-
),
|
|
267
|
-
},
|
|
268
264
|
{
|
|
269
265
|
command: "compact",
|
|
270
266
|
description: formatTelegramBotCommandDescription(
|
|
@@ -273,10 +269,10 @@ export const TELEGRAM_BUILTIN_BOT_COMMANDS: readonly TelegramBotCommandDefinitio
|
|
|
273
269
|
),
|
|
274
270
|
},
|
|
275
271
|
{
|
|
276
|
-
command: "
|
|
272
|
+
command: "new",
|
|
277
273
|
description: formatTelegramBotCommandDescription(
|
|
278
|
-
"
|
|
279
|
-
"
|
|
274
|
+
"new",
|
|
275
|
+
"Start a new session",
|
|
280
276
|
),
|
|
281
277
|
},
|
|
282
278
|
{
|
|
@@ -286,6 +282,13 @@ export const TELEGRAM_BUILTIN_BOT_COMMANDS: readonly TelegramBotCommandDefinitio
|
|
|
286
282
|
"Queue continue prompt",
|
|
287
283
|
),
|
|
288
284
|
},
|
|
285
|
+
{
|
|
286
|
+
command: "next",
|
|
287
|
+
description: formatTelegramBotCommandDescription(
|
|
288
|
+
"next",
|
|
289
|
+
"Force next turn",
|
|
290
|
+
),
|
|
291
|
+
},
|
|
289
292
|
{
|
|
290
293
|
command: "abort",
|
|
291
294
|
description: formatTelegramBotCommandDescription("abort", "Abort Pi"),
|
|
@@ -331,17 +334,17 @@ export async function registerTelegramBotCommands(
|
|
|
331
334
|
await deps.setMyCommands(TELEGRAM_BOT_COMMANDS);
|
|
332
335
|
return;
|
|
333
336
|
}
|
|
334
|
-
const
|
|
335
|
-
(command) => command.command === "
|
|
337
|
+
const nextCommandIndex = TELEGRAM_BOT_COMMANDS.findIndex(
|
|
338
|
+
(command) => command.command === "next",
|
|
336
339
|
);
|
|
337
|
-
if (
|
|
340
|
+
if (nextCommandIndex === -1) {
|
|
338
341
|
await deps.setMyCommands([...TELEGRAM_BOT_COMMANDS, ...extensionCommands]);
|
|
339
342
|
return;
|
|
340
343
|
}
|
|
341
344
|
await deps.setMyCommands([
|
|
342
|
-
...TELEGRAM_BOT_COMMANDS.slice(0,
|
|
345
|
+
...TELEGRAM_BOT_COMMANDS.slice(0, nextCommandIndex + 1),
|
|
343
346
|
...extensionCommands,
|
|
344
|
-
...TELEGRAM_BOT_COMMANDS.slice(
|
|
347
|
+
...TELEGRAM_BOT_COMMANDS.slice(nextCommandIndex + 1),
|
|
345
348
|
]);
|
|
346
349
|
}
|
|
347
350
|
|
|
@@ -658,6 +661,7 @@ export function registerTelegramBridgeCommands(
|
|
|
658
661
|
export const TELEGRAM_RESERVED_COMMAND_NAMES = [
|
|
659
662
|
"stop",
|
|
660
663
|
"name",
|
|
664
|
+
"new",
|
|
661
665
|
"abort",
|
|
662
666
|
"next",
|
|
663
667
|
"continue",
|
|
@@ -691,6 +695,7 @@ export type TelegramCommandAction =
|
|
|
691
695
|
| { kind: "ignore"; executionMode: "ignored" }
|
|
692
696
|
| { kind: "stop"; executionMode: "immediate" }
|
|
693
697
|
| { kind: "name"; executionMode: "immediate" }
|
|
698
|
+
| { kind: "new"; executionMode: "immediate" }
|
|
694
699
|
| { kind: "abort"; executionMode: "immediate" }
|
|
695
700
|
| { kind: "next"; executionMode: "immediate" }
|
|
696
701
|
| { kind: "continue"; executionMode: "immediate" }
|
|
@@ -711,6 +716,7 @@ export type TelegramCommandExecutionMode = "ignored" | "immediate";
|
|
|
711
716
|
export interface TelegramCommandActionDeps<TMessage, TContext> {
|
|
712
717
|
handleStop: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
713
718
|
handleName: (message: TMessage, ctx: TContext, name: string) => Promise<void>;
|
|
719
|
+
handleNew: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
714
720
|
handleAbort: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
715
721
|
handleNext: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
716
722
|
handleContinue: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
@@ -798,6 +804,23 @@ export interface TelegramCompactConfirmationCallbackQuery {
|
|
|
798
804
|
};
|
|
799
805
|
}
|
|
800
806
|
|
|
807
|
+
export interface TelegramNewConfirmationCallbackDeps<TContext> {
|
|
808
|
+
ctx: TContext;
|
|
809
|
+
answerCallbackQuery: (
|
|
810
|
+
callbackQueryId: string,
|
|
811
|
+
text?: string,
|
|
812
|
+
) => Promise<void>;
|
|
813
|
+
editInteractiveMessage: (
|
|
814
|
+
chatId: number,
|
|
815
|
+
messageId: number,
|
|
816
|
+
text: string,
|
|
817
|
+
mode: "markdown" | "html" | "plain",
|
|
818
|
+
replyMarkup: TelegramCompactConfirmationReplyMarkup,
|
|
819
|
+
) => Promise<void>;
|
|
820
|
+
deleteMessage: (chatId: number, messageId: number) => Promise<void>;
|
|
821
|
+
runNew: (ctx: TContext) => Promise<void>;
|
|
822
|
+
}
|
|
823
|
+
|
|
801
824
|
export interface TelegramCompactConfirmationCallbackDeps<TContext> {
|
|
802
825
|
ctx: TContext;
|
|
803
826
|
answerCallbackQuery: (
|
|
@@ -1158,6 +1181,7 @@ export interface TelegramCommandRuntimeDeps<
|
|
|
1158
1181
|
) => void;
|
|
1159
1182
|
stopTypingLoop?: () => void;
|
|
1160
1183
|
enqueueContinueTurn: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
1184
|
+
requestNewSession?: (message: TMessage) => void;
|
|
1161
1185
|
compact: (
|
|
1162
1186
|
ctx: TContext,
|
|
1163
1187
|
callbacks: { onComplete: () => void; onError: (error: unknown) => void },
|
|
@@ -1205,10 +1229,10 @@ export const TELEGRAM_APP_MENU_INTRO_HTML = [
|
|
|
1205
1229
|
"<b>Pi Telegram</b>",
|
|
1206
1230
|
"",
|
|
1207
1231
|
`${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
|
|
1208
|
-
`${formatTelegramCommandEmojiPrefix("name")}/name — Rename this thread`,
|
|
1209
1232
|
`${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
|
|
1210
|
-
`${formatTelegramCommandEmojiPrefix("
|
|
1233
|
+
`${formatTelegramCommandEmojiPrefix("new")}/new — Start a new session`,
|
|
1211
1234
|
`${formatTelegramCommandEmojiPrefix("continue")}/continue — Queue continue prompt`,
|
|
1235
|
+
`${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
|
|
1212
1236
|
`${formatTelegramCommandEmojiPrefix("abort")}/abort — Abort Pi`,
|
|
1213
1237
|
`${formatTelegramCommandEmojiPrefix("stop")}/stop — Abort Pi & Clear queue`,
|
|
1214
1238
|
].join("\n");
|
|
@@ -1246,11 +1270,11 @@ function buildTelegramAppMenuIntroHtml(): string {
|
|
|
1246
1270
|
"<b>Pi Telegram</b>",
|
|
1247
1271
|
"",
|
|
1248
1272
|
`${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
|
|
1249
|
-
`${formatTelegramCommandEmojiPrefix("name")}/name — Rename this thread`,
|
|
1250
1273
|
`${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
|
|
1251
|
-
|
|
1252
|
-
`${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
|
|
1274
|
+
`${formatTelegramCommandEmojiPrefix("new")}/new — Start a new session`,
|
|
1253
1275
|
`${formatTelegramCommandEmojiPrefix("continue")}/continue — Queue continue prompt`,
|
|
1276
|
+
`${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
|
|
1277
|
+
...extensionLines,
|
|
1254
1278
|
`${formatTelegramCommandEmojiPrefix("abort")}/abort — Abort Pi`,
|
|
1255
1279
|
`${formatTelegramCommandEmojiPrefix("stop")}/stop — Abort Pi & Clear queue`,
|
|
1256
1280
|
].join("\n");
|
|
@@ -1316,6 +1340,7 @@ export function parseTelegramCommand(
|
|
|
1316
1340
|
export const TELEGRAM_COMMAND_ACTIONS = {
|
|
1317
1341
|
stop: { kind: "stop", executionMode: "immediate" },
|
|
1318
1342
|
name: { kind: "name", executionMode: "immediate" },
|
|
1343
|
+
new: { kind: "new", executionMode: "immediate" },
|
|
1319
1344
|
abort: { kind: "abort", executionMode: "immediate" },
|
|
1320
1345
|
next: { kind: "next", executionMode: "immediate" },
|
|
1321
1346
|
continue: { kind: "continue", executionMode: "immediate" },
|
|
@@ -1484,6 +1509,64 @@ function dispatchNextQueuedTelegramTurnAfterCompact(
|
|
|
1484
1509
|
deps.dispatchNextQueuedTelegramTurn();
|
|
1485
1510
|
}
|
|
1486
1511
|
|
|
1512
|
+
export function buildTelegramNewConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup {
|
|
1513
|
+
return {
|
|
1514
|
+
inline_keyboard: [
|
|
1515
|
+
[
|
|
1516
|
+
{ text: "🆕 Yes, start new", callback_data: "new:confirm" },
|
|
1517
|
+
{ text: "❌ No", callback_data: "new:cancel" },
|
|
1518
|
+
],
|
|
1519
|
+
],
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
export function getTelegramNewConfirmationHtml(): string {
|
|
1524
|
+
return "<b>Start a new session?</b>";
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
export async function openTelegramNewConfirmation(
|
|
1528
|
+
target: TelegramCommandMessageTarget,
|
|
1529
|
+
deps: TelegramCompactConfirmationDeps,
|
|
1530
|
+
): Promise<void> {
|
|
1531
|
+
await deps.sendInteractiveMessage(
|
|
1532
|
+
target.chatId,
|
|
1533
|
+
getTelegramNewConfirmationHtml(),
|
|
1534
|
+
"html",
|
|
1535
|
+
buildTelegramNewConfirmationReplyMarkup(),
|
|
1536
|
+
target.threadId !== undefined
|
|
1537
|
+
? { target: { chatId: target.chatId, threadId: target.threadId } }
|
|
1538
|
+
: undefined,
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
export async function handleTelegramNewConfirmationCallback<TContext>(
|
|
1543
|
+
query: TelegramCompactConfirmationCallbackQuery,
|
|
1544
|
+
deps: TelegramNewConfirmationCallbackDeps<TContext>,
|
|
1545
|
+
): Promise<boolean> {
|
|
1546
|
+
if (query.data !== "new:confirm" && query.data !== "new:cancel") return false;
|
|
1547
|
+
const chatId = query.message?.chat?.id;
|
|
1548
|
+
const messageId = query.message?.message_id;
|
|
1549
|
+
if (typeof chatId !== "number" || typeof messageId !== "number") {
|
|
1550
|
+
await deps.answerCallbackQuery(query.id, "⌛ Interactive message expired.");
|
|
1551
|
+
return true;
|
|
1552
|
+
}
|
|
1553
|
+
if (query.data === "new:cancel") {
|
|
1554
|
+
await deps.editInteractiveMessage(
|
|
1555
|
+
chatId,
|
|
1556
|
+
messageId,
|
|
1557
|
+
"<b>🚫 New session cancelled.</b>",
|
|
1558
|
+
"html",
|
|
1559
|
+
{ inline_keyboard: [] },
|
|
1560
|
+
);
|
|
1561
|
+
await deps.answerCallbackQuery(query.id);
|
|
1562
|
+
return true;
|
|
1563
|
+
}
|
|
1564
|
+
await deps.answerCallbackQuery(query.id);
|
|
1565
|
+
await deps.deleteMessage(chatId, messageId);
|
|
1566
|
+
await deps.runNew(deps.ctx);
|
|
1567
|
+
return true;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1487
1570
|
export function buildTelegramCompactConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup {
|
|
1488
1571
|
return {
|
|
1489
1572
|
inline_keyboard: [
|
|
@@ -1557,6 +1640,53 @@ export async function handleTelegramCompactConfirmationCallback<TContext>(
|
|
|
1557
1640
|
return true;
|
|
1558
1641
|
}
|
|
1559
1642
|
|
|
1643
|
+
export interface TelegramNewCommandDeps extends TelegramRuntimeEventRecorderPort {
|
|
1644
|
+
isIdle: () => boolean;
|
|
1645
|
+
hasPendingMessages: () => boolean;
|
|
1646
|
+
hasActiveTelegramTurn: () => boolean;
|
|
1647
|
+
hasDispatchPending: () => boolean;
|
|
1648
|
+
hasQueuedTelegramItems: () => boolean;
|
|
1649
|
+
isCompactionInProgress: () => boolean;
|
|
1650
|
+
requestNewSession?: () => void;
|
|
1651
|
+
sendTextReply: (
|
|
1652
|
+
text: string,
|
|
1653
|
+
options?: { parseMode?: "HTML" },
|
|
1654
|
+
) => Promise<void>;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
export async function handleTelegramNewCommand(
|
|
1658
|
+
deps: TelegramNewCommandDeps,
|
|
1659
|
+
): Promise<void> {
|
|
1660
|
+
if (
|
|
1661
|
+
!deps.isIdle() ||
|
|
1662
|
+
deps.hasPendingMessages() ||
|
|
1663
|
+
deps.hasActiveTelegramTurn() ||
|
|
1664
|
+
deps.hasDispatchPending() ||
|
|
1665
|
+
deps.hasQueuedTelegramItems() ||
|
|
1666
|
+
deps.isCompactionInProgress()
|
|
1667
|
+
) {
|
|
1668
|
+
await deps.sendTextReply(
|
|
1669
|
+
formatTelegramInformationHeading(
|
|
1670
|
+
"⏳",
|
|
1671
|
+
"Cannot start a new session while Pi or the Telegram queue is busy. Wait for queued turns to finish or send /abort first.",
|
|
1672
|
+
),
|
|
1673
|
+
{ parseMode: "HTML" },
|
|
1674
|
+
);
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
if (!deps.requestNewSession) {
|
|
1678
|
+
await deps.sendTextReply(
|
|
1679
|
+
formatTelegramInformationHeading(
|
|
1680
|
+
"🚫",
|
|
1681
|
+
"Session replacement is unavailable in this Pi runtime.",
|
|
1682
|
+
),
|
|
1683
|
+
{ parseMode: "HTML" },
|
|
1684
|
+
);
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
deps.requestNewSession();
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1560
1690
|
export async function handleTelegramCompactCommand(
|
|
1561
1691
|
deps: TelegramCompactCommandDeps,
|
|
1562
1692
|
): Promise<void> {
|
|
@@ -1673,6 +1803,9 @@ export async function executeTelegramCommandAction<TMessage, TContext>(
|
|
|
1673
1803
|
case "name":
|
|
1674
1804
|
await deps.handleName(message, ctx, commandArgs);
|
|
1675
1805
|
return true;
|
|
1806
|
+
case "new":
|
|
1807
|
+
await deps.handleNew(message, ctx);
|
|
1808
|
+
return true;
|
|
1676
1809
|
case "abort":
|
|
1677
1810
|
await deps.handleAbort(message, ctx);
|
|
1678
1811
|
return true;
|
|
@@ -1776,6 +1909,7 @@ export function createTelegramCommandHandlerTargetRuntime<
|
|
|
1776
1909
|
stopTypingLoop: deps.stopTypingLoop,
|
|
1777
1910
|
enqueueContinueTurn: deps.enqueueContinueTurn,
|
|
1778
1911
|
compact: deps.compact,
|
|
1912
|
+
requestNewSession: deps.requestNewSession,
|
|
1779
1913
|
sendInteractiveMessage: deps.sendInteractiveMessage,
|
|
1780
1914
|
enqueueControlItem: commandTargetRuntime.enqueueControlItem,
|
|
1781
1915
|
showStatus: commandTargetRuntime.showStatus,
|
|
@@ -2042,6 +2176,28 @@ async function handleTelegramCommandRuntime<
|
|
|
2042
2176
|
assertExecutionCurrentFor(nextMessage),
|
|
2043
2177
|
);
|
|
2044
2178
|
},
|
|
2179
|
+
handleNew: async (nextMessage, commandCtx) => {
|
|
2180
|
+
if (deps.sendInteractiveMessage) {
|
|
2181
|
+
await openTelegramNewConfirmation(
|
|
2182
|
+
getTelegramCommandMessageTarget(nextMessage),
|
|
2183
|
+
{ sendInteractiveMessage: deps.sendInteractiveMessage },
|
|
2184
|
+
);
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
await handleTelegramNewCommand({
|
|
2188
|
+
isIdle: () => deps.isIdle(commandCtx),
|
|
2189
|
+
hasPendingMessages: () => deps.hasPendingMessages(commandCtx),
|
|
2190
|
+
hasActiveTelegramTurn: deps.hasActiveTelegramTurn,
|
|
2191
|
+
hasDispatchPending: deps.hasDispatchPending,
|
|
2192
|
+
hasQueuedTelegramItems: deps.hasQueuedTelegramItems,
|
|
2193
|
+
isCompactionInProgress: deps.isCompactionInProgress,
|
|
2194
|
+
requestNewSession: deps.requestNewSession
|
|
2195
|
+
? () => deps.requestNewSession!(nextMessage)
|
|
2196
|
+
: undefined,
|
|
2197
|
+
sendTextReply: sendReplyFor(nextMessage),
|
|
2198
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
2199
|
+
});
|
|
2200
|
+
},
|
|
2045
2201
|
handleCompact: async (nextMessage, commandCtx) => {
|
|
2046
2202
|
if (deps.sendInteractiveMessage) {
|
|
2047
2203
|
await openTelegramCompactConfirmation(
|
|
@@ -2178,3 +2334,292 @@ async function handleTelegramCommandRuntime<
|
|
|
2178
2334
|
commandArgs,
|
|
2179
2335
|
);
|
|
2180
2336
|
}
|
|
2337
|
+
|
|
2338
|
+
export const TELEGRAM_SESSION_ACTION_COMMAND_NAME = "telegram-session-action";
|
|
2339
|
+
export const TELEGRAM_SESSION_ACTION_COMMAND_DESCRIPTION =
|
|
2340
|
+
"(internal) replace the current Pi session after Telegram settlement";
|
|
2341
|
+
|
|
2342
|
+
export function delayTelegramSessionAction(delayMs: number): Promise<void> {
|
|
2343
|
+
return new Promise<void>((resolve) => setTimeout(resolve, delayMs));
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
export interface TelegramSessionActionRuntimeDeps {
|
|
2347
|
+
registerCommand: Pi.ExtensionAPI["registerCommand"];
|
|
2348
|
+
sendUserMessage: Pi.ExtensionAPI["sendUserMessage"];
|
|
2349
|
+
notifyResult: (
|
|
2350
|
+
target: { chatId: number; threadId?: number; messageId: number },
|
|
2351
|
+
result: "success" | "cancelled" | "failure",
|
|
2352
|
+
) => Promise<void>;
|
|
2353
|
+
prepareReplacement?: (
|
|
2354
|
+
ctx: Pi.ExtensionCommandContext,
|
|
2355
|
+
updateId: number,
|
|
2356
|
+
target: { chatId: number; threadId?: number; messageId: number },
|
|
2357
|
+
) => Promise<void>;
|
|
2358
|
+
recordRuntimeEvent?: (category: string, error: unknown) => void;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
export interface TelegramSessionReplacementSettlementDeps {
|
|
2362
|
+
getIntent: () => Promise<TelegramSessionReplacementIntent | undefined>;
|
|
2363
|
+
hasSuccessorContinuity: (intent: TelegramSessionReplacementIntent) => boolean;
|
|
2364
|
+
editSuccess: (intent: TelegramSessionReplacementIntent) =>
|
|
2365
|
+
Promise<{ ok: boolean; retryable?: boolean; message?: string }>;
|
|
2366
|
+
clearIntent: (intent: TelegramSessionReplacementIntent) => Promise<boolean>;
|
|
2367
|
+
profileName: string | undefined;
|
|
2368
|
+
cwd: string;
|
|
2369
|
+
sessionId: string;
|
|
2370
|
+
now?: () => number;
|
|
2371
|
+
sleep?: (delayMs: number) => Promise<void>;
|
|
2372
|
+
isCurrent?: () => boolean;
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
export async function settleTelegramSessionReplacement(
|
|
2376
|
+
deps: TelegramSessionReplacementSettlementDeps,
|
|
2377
|
+
): Promise<"none" | "settled" | "expired" | "failed" | "stale"> {
|
|
2378
|
+
const now = deps.now ?? Date.now;
|
|
2379
|
+
const sleep = deps.sleep ?? ((delayMs) =>
|
|
2380
|
+
new Promise<void>((resolve) => setTimeout(resolve, delayMs)));
|
|
2381
|
+
while (deps.isCurrent?.() !== false) {
|
|
2382
|
+
const intent = await deps.getIntent();
|
|
2383
|
+
if (!intent || intent.sourceSessionId === deps.sessionId) return "none";
|
|
2384
|
+
if (intent.profileName !== deps.profileName || intent.cwd !== deps.cwd) return "stale";
|
|
2385
|
+
if (now() >= intent.expiresAtMs) return "expired";
|
|
2386
|
+
if (!deps.hasSuccessorContinuity(intent)) {
|
|
2387
|
+
await sleep(100);
|
|
2388
|
+
continue;
|
|
2389
|
+
}
|
|
2390
|
+
if (!await deps.clearIntent(intent)) return "failed";
|
|
2391
|
+
do {
|
|
2392
|
+
const delivered = await deps.editSuccess(intent);
|
|
2393
|
+
if (delivered.ok) return "settled";
|
|
2394
|
+
if (!delivered.retryable) return "failed";
|
|
2395
|
+
await sleep(100);
|
|
2396
|
+
} while (deps.isCurrent?.() !== false && now() < intent.expiresAtMs);
|
|
2397
|
+
return "failed";
|
|
2398
|
+
}
|
|
2399
|
+
return "stale";
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
export function createTelegramSessionReplacementSettlementRuntime<TContext>(deps: {
|
|
2403
|
+
resolve: (ctx: TContext) => TelegramSessionReplacementSettlementDeps | undefined;
|
|
2404
|
+
onResult?: (result: "none" | "settled" | "expired" | "failed" | "stale") => void;
|
|
2405
|
+
onError?: (error: unknown) => void;
|
|
2406
|
+
}): { onSessionStart: (ctx: TContext) => void } {
|
|
2407
|
+
let generation = 0;
|
|
2408
|
+
return {
|
|
2409
|
+
onSessionStart(ctx) {
|
|
2410
|
+
const currentGeneration = ++generation;
|
|
2411
|
+
const resolved = deps.resolve(ctx);
|
|
2412
|
+
if (!resolved) return;
|
|
2413
|
+
void settleTelegramSessionReplacement({
|
|
2414
|
+
...resolved,
|
|
2415
|
+
isCurrent: () => currentGeneration === generation &&
|
|
2416
|
+
resolved.isCurrent?.() !== false,
|
|
2417
|
+
}).then(deps.onResult, deps.onError);
|
|
2418
|
+
},
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
export interface TelegramSessionActionAssemblyDeps {
|
|
2423
|
+
registerCommand: Pi.ExtensionAPI["registerCommand"];
|
|
2424
|
+
sendUserMessage: Pi.ExtensionAPI["sendUserMessage"];
|
|
2425
|
+
store: {
|
|
2426
|
+
load: () => Promise<void>;
|
|
2427
|
+
refresh?: () => Promise<void>;
|
|
2428
|
+
getWorkspaceBindingByTarget: (
|
|
2429
|
+
target: { chatId: number; threadId?: number },
|
|
2430
|
+
sessionId?: string,
|
|
2431
|
+
) => {
|
|
2432
|
+
cwd: string; sessionId?: string; slot?: string; threadName?: string;
|
|
2433
|
+
manualThreadName?: string; target: { chatId: number; threadId: number };
|
|
2434
|
+
} | undefined;
|
|
2435
|
+
getSessionReplacementIntent: () => TelegramSessionReplacementIntent | undefined;
|
|
2436
|
+
commitSessionReplacementIntent: (
|
|
2437
|
+
intent: TelegramSessionReplacementIntent,
|
|
2438
|
+
isCurrent: () => boolean,
|
|
2439
|
+
) => Promise<boolean>;
|
|
2440
|
+
removeSessionReplacementIntent: (
|
|
2441
|
+
intent: TelegramSessionReplacementIntent,
|
|
2442
|
+
isCurrent: () => boolean,
|
|
2443
|
+
) => Promise<boolean>;
|
|
2444
|
+
};
|
|
2445
|
+
getProfileName: () => string | undefined;
|
|
2446
|
+
ownsPersistence: () => boolean;
|
|
2447
|
+
sendResult: (
|
|
2448
|
+
target: { chatId: number; threadId?: number },
|
|
2449
|
+
html: string,
|
|
2450
|
+
) => Promise<{ ok: boolean; retryable?: boolean }>;
|
|
2451
|
+
handoffTtlMs: number;
|
|
2452
|
+
now?: () => number;
|
|
2453
|
+
recordRuntimeEvent?: (category: string, error: unknown) => void;
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
export function createTelegramSessionActionAssembly(
|
|
2457
|
+
deps: TelegramSessionActionAssemblyDeps,
|
|
2458
|
+
): {
|
|
2459
|
+
action: TelegramSessionActionRuntime;
|
|
2460
|
+
settlement: { onSessionStart: (ctx: Pi.ExtensionContext) => void };
|
|
2461
|
+
} {
|
|
2462
|
+
const now = deps.now ?? Date.now;
|
|
2463
|
+
const report = (error: unknown): void => deps.recordRuntimeEvent?.("new-session", error);
|
|
2464
|
+
const sendTerminalResult = async (
|
|
2465
|
+
target: { chatId: number; threadId?: number },
|
|
2466
|
+
result: "success" | "cancelled" | "failure",
|
|
2467
|
+
): Promise<void> => {
|
|
2468
|
+
const text = result === "success" ? "<b>🆕 New session started.</b>"
|
|
2469
|
+
: result === "cancelled" ? "<b>🚫 New session cancelled.</b>"
|
|
2470
|
+
: "<b>⚠️ New session failed.</b>";
|
|
2471
|
+
const deadline = now() + 10_000;
|
|
2472
|
+
do {
|
|
2473
|
+
const delivery = await deps.sendResult(target, text);
|
|
2474
|
+
if (delivery.ok) return;
|
|
2475
|
+
if (!delivery.retryable) break;
|
|
2476
|
+
await delayTelegramSessionAction(100);
|
|
2477
|
+
} while (now() < deadline);
|
|
2478
|
+
report(new Error("Telegram new-session result delivery failed."));
|
|
2479
|
+
};
|
|
2480
|
+
const action = createTelegramSessionActionRuntime({
|
|
2481
|
+
registerCommand: deps.registerCommand,
|
|
2482
|
+
sendUserMessage: deps.sendUserMessage,
|
|
2483
|
+
notifyResult(target, result) { return sendTerminalResult(target, result); },
|
|
2484
|
+
async prepareReplacement(ctx, updateId, target) {
|
|
2485
|
+
await deps.store.load();
|
|
2486
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
2487
|
+
const binding = typeof target.threadId === "number"
|
|
2488
|
+
? deps.store.getWorkspaceBindingByTarget(target)
|
|
2489
|
+
: undefined;
|
|
2490
|
+
if (typeof target.threadId === "number" &&
|
|
2491
|
+
(!binding || binding.cwd !== ctx.cwd || binding.sessionId !== sessionId)) {
|
|
2492
|
+
throw new Error("Telegram session replacement binding is unavailable.");
|
|
2493
|
+
}
|
|
2494
|
+
const createdAtMs = now();
|
|
2495
|
+
if (!await deps.store.commitSessionReplacementIntent({
|
|
2496
|
+
continuity: binding ? "workspace-thread" : "classic-chat",
|
|
2497
|
+
cwd: binding?.cwd ?? ctx.cwd,
|
|
2498
|
+
profileName: deps.getProfileName() ?? "default",
|
|
2499
|
+
sourceSessionId: sessionId,
|
|
2500
|
+
sourceUpdateId: updateId,
|
|
2501
|
+
target: binding ? { ...binding.target } : { chatId: target.chatId },
|
|
2502
|
+
messageId: target.messageId,
|
|
2503
|
+
...(binding?.slot ? { slot: binding.slot } : {}),
|
|
2504
|
+
...(binding?.manualThreadName ?? binding?.threadName
|
|
2505
|
+
? { threadName: binding.manualThreadName ?? binding.threadName } : {}),
|
|
2506
|
+
createdAtMs,
|
|
2507
|
+
expiresAtMs: createdAtMs + deps.handoffTtlMs,
|
|
2508
|
+
}, deps.ownsPersistence)) {
|
|
2509
|
+
throw new Error("Telegram session replacement intent was not persisted.");
|
|
2510
|
+
}
|
|
2511
|
+
},
|
|
2512
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
2513
|
+
});
|
|
2514
|
+
const settlement = createTelegramSessionReplacementSettlementRuntime<Pi.ExtensionContext>({
|
|
2515
|
+
resolve(ctx) {
|
|
2516
|
+
const sessionId = ctx.sessionManager?.getSessionId?.();
|
|
2517
|
+
if (!sessionId) return undefined;
|
|
2518
|
+
return {
|
|
2519
|
+
async getIntent() { await deps.store.refresh?.(); return deps.store.getSessionReplacementIntent(); },
|
|
2520
|
+
hasSuccessorContinuity(intent) {
|
|
2521
|
+
return intent.continuity === "classic-chat" ||
|
|
2522
|
+
deps.store.getWorkspaceBindingByTarget(intent.target, sessionId)?.cwd === intent.cwd;
|
|
2523
|
+
},
|
|
2524
|
+
editSuccess(intent) { return deps.sendResult(intent.target, "<b>🆕 New session started.</b>"); },
|
|
2525
|
+
clearIntent(intent) { return deps.store.removeSessionReplacementIntent(intent, deps.ownsPersistence); },
|
|
2526
|
+
profileName: deps.getProfileName() ?? "default",
|
|
2527
|
+
cwd: ctx.cwd,
|
|
2528
|
+
sessionId,
|
|
2529
|
+
};
|
|
2530
|
+
},
|
|
2531
|
+
onResult(result) {
|
|
2532
|
+
if (result === "expired" || result === "failed") {
|
|
2533
|
+
report(new Error(`Telegram session replacement successor settlement ${result}.`));
|
|
2534
|
+
}
|
|
2535
|
+
},
|
|
2536
|
+
onError: report,
|
|
2537
|
+
});
|
|
2538
|
+
return { action, settlement };
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
export interface TelegramSessionActionRuntime {
|
|
2542
|
+
register: () => void;
|
|
2543
|
+
scheduleAfterUpdate: (
|
|
2544
|
+
updateId: number,
|
|
2545
|
+
target: { chatId: number; threadId?: number; messageId: number },
|
|
2546
|
+
) => boolean;
|
|
2547
|
+
onUpdateCompleted: (updateId: number) => void;
|
|
2548
|
+
hasPending: () => boolean;
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
export function createTelegramSessionActionRuntime(
|
|
2552
|
+
deps: TelegramSessionActionRuntimeDeps,
|
|
2553
|
+
): TelegramSessionActionRuntime {
|
|
2554
|
+
let pendingUpdateId: number | undefined;
|
|
2555
|
+
let pendingTarget: {
|
|
2556
|
+
chatId: number;
|
|
2557
|
+
threadId?: number;
|
|
2558
|
+
messageId: number;
|
|
2559
|
+
} | undefined;
|
|
2560
|
+
let commandPending = false;
|
|
2561
|
+
let commandUpdateId: number | undefined;
|
|
2562
|
+
let registered = false;
|
|
2563
|
+
|
|
2564
|
+
const reportFailure = (error: unknown): void => {
|
|
2565
|
+
try {
|
|
2566
|
+
deps.recordRuntimeEvent?.("new-session", error);
|
|
2567
|
+
} catch {
|
|
2568
|
+
// Diagnostics cannot make a completed durable update retryable.
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
|
|
2572
|
+
return {
|
|
2573
|
+
register() {
|
|
2574
|
+
if (registered) return;
|
|
2575
|
+
registered = true;
|
|
2576
|
+
deps.registerCommand(TELEGRAM_SESSION_ACTION_COMMAND_NAME, {
|
|
2577
|
+
description: TELEGRAM_SESSION_ACTION_COMMAND_DESCRIPTION,
|
|
2578
|
+
handler: async (_args, ctx) => {
|
|
2579
|
+
if (!commandPending) return;
|
|
2580
|
+
commandPending = false;
|
|
2581
|
+
const target = pendingTarget;
|
|
2582
|
+
const updateId = commandUpdateId;
|
|
2583
|
+
pendingTarget = undefined;
|
|
2584
|
+
commandUpdateId = undefined;
|
|
2585
|
+
if (!target || updateId === undefined) return;
|
|
2586
|
+
try {
|
|
2587
|
+
await deps.prepareReplacement?.(ctx, updateId, target);
|
|
2588
|
+
const result = await ctx.newSession();
|
|
2589
|
+
if (result.cancelled) await deps.notifyResult(target, "cancelled");
|
|
2590
|
+
} catch (error) {
|
|
2591
|
+
reportFailure(error);
|
|
2592
|
+
await deps.notifyResult(target, "failure");
|
|
2593
|
+
}
|
|
2594
|
+
},
|
|
2595
|
+
});
|
|
2596
|
+
},
|
|
2597
|
+
scheduleAfterUpdate(updateId, target) {
|
|
2598
|
+
if (pendingUpdateId !== undefined || commandPending) return false;
|
|
2599
|
+
pendingUpdateId = updateId;
|
|
2600
|
+
pendingTarget = { ...target };
|
|
2601
|
+
return true;
|
|
2602
|
+
},
|
|
2603
|
+
onUpdateCompleted(updateId) {
|
|
2604
|
+
if (pendingUpdateId !== updateId) return;
|
|
2605
|
+
pendingUpdateId = undefined;
|
|
2606
|
+
commandUpdateId = updateId;
|
|
2607
|
+
commandPending = true;
|
|
2608
|
+
void Promise.resolve()
|
|
2609
|
+
.then(() =>
|
|
2610
|
+
deps.sendUserMessage(`/${TELEGRAM_SESSION_ACTION_COMMAND_NAME}`, {
|
|
2611
|
+
expandPromptTemplates: true,
|
|
2612
|
+
}),
|
|
2613
|
+
)
|
|
2614
|
+
.catch((error) => {
|
|
2615
|
+
commandPending = false;
|
|
2616
|
+
commandUpdateId = undefined;
|
|
2617
|
+
pendingTarget = undefined;
|
|
2618
|
+
reportFailure(error);
|
|
2619
|
+
});
|
|
2620
|
+
},
|
|
2621
|
+
hasPending() {
|
|
2622
|
+
return pendingUpdateId !== undefined || commandPending;
|
|
2623
|
+
},
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
@@ -878,6 +878,21 @@ export async function sendTelegramView(
|
|
|
878
878
|
return runDeliveryOperation((runtime) => runtime.sendView(view, options));
|
|
879
879
|
}
|
|
880
880
|
|
|
881
|
+
/** @internal Edit an exact Telegram message through the currently bound runtime generation. */
|
|
882
|
+
export async function editTelegramTargetView(
|
|
883
|
+
target: TelegramDeliveryTarget,
|
|
884
|
+
messageId: number,
|
|
885
|
+
view: TelegramDeliveryView,
|
|
886
|
+
): Promise<TelegramDeliveryResult<TelegramDeliveryHandle>> {
|
|
887
|
+
const invalid = validateView<TelegramDeliveryHandle>(view);
|
|
888
|
+
if (invalid) return invalid;
|
|
889
|
+
return runDeliveryOperation((runtime) => runtime.editView({
|
|
890
|
+
target: { ...target },
|
|
891
|
+
messageIds: [messageId],
|
|
892
|
+
generation: runtime.generation,
|
|
893
|
+
}, view));
|
|
894
|
+
}
|
|
895
|
+
|
|
881
896
|
export async function editTelegramView(
|
|
882
897
|
handle: TelegramDeliveryHandle,
|
|
883
898
|
view: TelegramDeliveryView,
|