@llblab/pi-kit 0.6.0 → 0.7.1
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 +4 -4
- package/node_modules/@llblab/pi-actors/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-actors/CHANGELOG.md +6 -0
- package/node_modules/@llblab/pi-actors/README.md +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +3 -0
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +14 -1
- package/node_modules/@llblab/pi-actors/dist/lib/command-templates.js +45 -3
- package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +16 -3
- package/node_modules/@llblab/pi-actors/dist/lib/observability.js +92 -7
- package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +0 -1
- package/node_modules/@llblab/pi-actors/dist/lib/pi.js +15 -24
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.d.ts +17 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.js +44 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.d.ts +4 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.js +102 -4
- package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +58 -39
- package/node_modules/@llblab/pi-actors/dist/lib/runtime.js +14 -6
- package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/async-runs.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +18 -23
- package/node_modules/@llblab/pi-actors/lib/async-runs.ts +18 -1
- package/node_modules/@llblab/pi-actors/lib/command-templates.ts +41 -3
- package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +1 -1
- package/node_modules/@llblab/pi-actors/lib/observability.ts +119 -5
- package/node_modules/@llblab/pi-actors/lib/pi.ts +15 -28
- package/node_modules/@llblab/pi-actors/lib/run-delivery-lineage.ts +68 -0
- package/node_modules/@llblab/pi-actors/lib/run-delivery.ts +120 -4
- package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +69 -44
- package/node_modules/@llblab/pi-actors/lib/runtime.ts +17 -6
- package/node_modules/@llblab/pi-actors/package.json +1 -1
- package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +33 -12
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +125 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +87 -45
- package/node_modules/@llblab/pi-state-flow/README.md +203 -107
- package/node_modules/@llblab/pi-state-flow/docs/README.md +4 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +188 -0
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +42 -0
- package/node_modules/@llblab/pi-state-flow/index.ts +164 -2
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +138 -0
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +273 -0
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +48 -0
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +18 -5
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +268 -0
- package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +117 -0
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +562 -0
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +24 -12
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +606 -70
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +666 -0
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +95 -0
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/maintenance.ts +141 -0
- package/node_modules/@llblab/pi-state-flow/lib/memory.ts +52 -0
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +88 -0
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +296 -0
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +23 -7
- package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +79 -0
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +264 -0
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +6 -0
- package/node_modules/@llblab/pi-state-flow/lib/skills.ts +99 -7
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +305 -48
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +74 -7
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +125 -6
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +196 -0
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +233 -0
- package/node_modules/@llblab/pi-state-flow/lib/terminal.ts +70 -24
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +254 -29
- package/node_modules/@llblab/pi-state-flow/package.json +8 -2
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +128 -0
- package/node_modules/@llblab/pi-telegram/AGENTS.md +14 -9
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +23 -5
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +18 -0
- package/node_modules/@llblab/pi-telegram/README.md +13 -9
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +227 -23
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +70 -19
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +6 -6
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +13 -6
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +3 -1
- package/node_modules/@llblab/pi-telegram/index.ts +4 -1418
- package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +6 -3
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +46 -1
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +32 -19
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +600 -135
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +962 -55
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +355 -26
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +718 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +237 -11
- package/node_modules/@llblab/pi-telegram/lib/config.ts +242 -26
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1851 -0
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +20 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +2184 -126
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +44 -2
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +152 -13
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +97 -10
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +29 -0
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +85 -17
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +17 -0
- package/node_modules/@llblab/pi-telegram/lib/prompts.ts +6 -2
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +118 -26
- package/node_modules/@llblab/pi-telegram/lib/rendering.ts +4 -1
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +21 -2
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +344 -112
- package/node_modules/@llblab/pi-telegram/lib/setup.ts +44 -4
- package/node_modules/@llblab/pi-telegram/lib/status.ts +51 -4
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +308 -39
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +353 -22
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +664 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +226 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +118 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1686 -129
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +7 -0
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1319 -97
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +1643 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +968 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +84 -0
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/screenshot.png +0 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-bus.mjs +83 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +101 -0
- package/node_modules/@llblab/{skills → pi-telegram/skills}/show-me/SKILL.md +28 -6
- package/node_modules/@llblab/pi-telegram/skills/show-me/references/telegram-surfaces.md +43 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/delivery-and-threads.md +1 -1
- package/node_modules/@llblab/skills/package.json +2 -3
- package/package.json +6 -5
- /package/node_modules/@llblab/pi-telegram/lib/{logs.ts → logging.ts} +0 -0
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
pairTelegramUserIfNeeded,
|
|
9
|
+
type TelegramConfigStore,
|
|
9
10
|
TELEGRAM_DEFAULT_PROFILE_NAME,
|
|
10
11
|
} from "./config.ts";
|
|
11
12
|
import type { ExtensionAPI, ExtensionCommandContext } from "./pi.ts";
|
|
@@ -170,6 +171,7 @@ export const TELEGRAM_COMMAND_EMOJI = {
|
|
|
170
171
|
continue: "▶️",
|
|
171
172
|
abort: "⏹️",
|
|
172
173
|
stop: "🟥",
|
|
174
|
+
name: "🏷️",
|
|
173
175
|
} as const;
|
|
174
176
|
|
|
175
177
|
export type TelegramCommandEmojiName = keyof typeof TELEGRAM_COMMAND_EMOJI;
|
|
@@ -186,6 +188,10 @@ export function formatTelegramCommandEmojiPrefix(
|
|
|
186
188
|
return `${getTelegramCommandEmoji(command)} `;
|
|
187
189
|
}
|
|
188
190
|
|
|
191
|
+
export function formatTelegramPiCommandHtml(command: string): string {
|
|
192
|
+
return `<code>${escapeHtml(command)}</code>`;
|
|
193
|
+
}
|
|
194
|
+
|
|
189
195
|
export function formatTelegramInformationHeading(
|
|
190
196
|
emoji: string,
|
|
191
197
|
text: string,
|
|
@@ -193,6 +199,37 @@ export function formatTelegramInformationHeading(
|
|
|
193
199
|
return `<b>${escapeHtml(emoji)} ${escapeHtml(text)}</b>`;
|
|
194
200
|
}
|
|
195
201
|
|
|
202
|
+
export function formatTelegramInvalidInstanceName(
|
|
203
|
+
validationError: string,
|
|
204
|
+
): string {
|
|
205
|
+
const details = validationError.replace(
|
|
206
|
+
/^Invalid Telegram (?:instance name|Thread display name):\s*/,
|
|
207
|
+
"",
|
|
208
|
+
);
|
|
209
|
+
const items = details
|
|
210
|
+
.split(/;\s+|(?<=\.)\s+(?=[A-Z])/)
|
|
211
|
+
.map((item) => item.trim())
|
|
212
|
+
.filter(Boolean)
|
|
213
|
+
.map((item) => /[.!?]$/.test(item) ? item : `${item}.`)
|
|
214
|
+
.map((item) => item[0]!.toUpperCase() + item.slice(1));
|
|
215
|
+
return [
|
|
216
|
+
"<b>⚠️ Invalid Thread Display Name:</b>\n",
|
|
217
|
+
...items.map((item) => `• ${escapeHtml(item)}`),
|
|
218
|
+
].join("\n");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function formatTelegramThreadDisplayNameSavedHeading(
|
|
222
|
+
name: string,
|
|
223
|
+
): string {
|
|
224
|
+
return `<b>✅ Thread display name saved as <i>${escapeHtml(name)}</i>.</b>`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function formatTelegramAutomaticThreadDisplayNameRestoredHeading(
|
|
228
|
+
name: string,
|
|
229
|
+
): string {
|
|
230
|
+
return `<b>✅ Automatic Thread display name restored as <i>${escapeHtml(name)}</i>.</b>`;
|
|
231
|
+
}
|
|
232
|
+
|
|
196
233
|
export const TELEGRAM_COMPACTION_STARTED_TEXT =
|
|
197
234
|
formatTelegramInformationHeading(
|
|
198
235
|
getTelegramCommandEmoji("compact"),
|
|
@@ -221,6 +258,13 @@ export const TELEGRAM_BUILTIN_BOT_COMMANDS: readonly TelegramBotCommandDefinitio
|
|
|
221
258
|
"Open menu / Pair bridge",
|
|
222
259
|
),
|
|
223
260
|
},
|
|
261
|
+
{
|
|
262
|
+
command: "name",
|
|
263
|
+
description: formatTelegramBotCommandDescription(
|
|
264
|
+
"name",
|
|
265
|
+
"Rename this thread",
|
|
266
|
+
),
|
|
267
|
+
},
|
|
224
268
|
{
|
|
225
269
|
command: "compact",
|
|
226
270
|
description: formatTelegramBotCommandDescription(
|
|
@@ -319,6 +363,7 @@ export function createTelegramBotCommandRegistrar(
|
|
|
319
363
|
export interface TelegramBridgeCommandStartPollingOptions {
|
|
320
364
|
force?: boolean;
|
|
321
365
|
forceFreshLeaderThread?: boolean;
|
|
366
|
+
requestedThreadName?: string;
|
|
322
367
|
}
|
|
323
368
|
|
|
324
369
|
export interface TelegramBridgeCommandStartPollingResult {
|
|
@@ -338,6 +383,7 @@ export interface TelegramBridgeCommandRegistrationDeps {
|
|
|
338
383
|
getStatusLines: (options?: TelegramBridgeStatusLineOptions) => string[];
|
|
339
384
|
reloadConfig: () => Promise<void>;
|
|
340
385
|
hasBotToken: () => boolean;
|
|
386
|
+
getBotTokenDiagnostic?: () => string | undefined;
|
|
341
387
|
startPolling: (
|
|
342
388
|
ctx: ExtensionCommandContext,
|
|
343
389
|
options?: TelegramBridgeCommandStartPollingOptions,
|
|
@@ -358,15 +404,76 @@ export interface TelegramBridgeCommandRegistrationDeps {
|
|
|
358
404
|
ctx: ExtensionCommandContext,
|
|
359
405
|
profileName: string,
|
|
360
406
|
) => Promise<boolean>;
|
|
407
|
+
validateThreadName?: (threadName: string) => string | undefined;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export type TelegramThreadDisplayNameRenamePort = (
|
|
411
|
+
target: { chatId: number; threadId?: number },
|
|
412
|
+
threadName: string,
|
|
413
|
+
) => Promise<{ ok: boolean; threadName?: string; message?: string }>;
|
|
414
|
+
|
|
415
|
+
export type TelegramThreadDisplayNameResetPort = (
|
|
416
|
+
target: { chatId: number; threadId?: number },
|
|
417
|
+
) => Promise<{
|
|
418
|
+
ok: boolean;
|
|
419
|
+
threadName?: string;
|
|
420
|
+
message?: string;
|
|
421
|
+
}>;
|
|
422
|
+
|
|
423
|
+
export function createTelegramThreadDisplayNameResetBinding(): {
|
|
424
|
+
bind: (reset: TelegramThreadDisplayNameResetPort) => void;
|
|
425
|
+
reset: TelegramThreadDisplayNameResetPort;
|
|
426
|
+
} {
|
|
427
|
+
let current: TelegramThreadDisplayNameResetPort | undefined;
|
|
428
|
+
return {
|
|
429
|
+
bind(reset) { current = reset; },
|
|
430
|
+
async reset(target) {
|
|
431
|
+
return current
|
|
432
|
+
? current(target)
|
|
433
|
+
: { ok: false, message: "Thread display name reset is unavailable." };
|
|
434
|
+
},
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function createTelegramThreadDisplayNameRenameBinding(): {
|
|
439
|
+
bind: (rename: TelegramThreadDisplayNameRenamePort) => void;
|
|
440
|
+
rename: TelegramThreadDisplayNameRenamePort;
|
|
441
|
+
} {
|
|
442
|
+
let current: TelegramThreadDisplayNameRenamePort | undefined;
|
|
443
|
+
return {
|
|
444
|
+
bind(rename) {
|
|
445
|
+
current = rename;
|
|
446
|
+
},
|
|
447
|
+
async rename(target, threadName) {
|
|
448
|
+
if (!current) {
|
|
449
|
+
return {
|
|
450
|
+
ok: false,
|
|
451
|
+
message: "Thread display naming is unavailable.",
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
return current(target, threadName);
|
|
455
|
+
},
|
|
456
|
+
};
|
|
361
457
|
}
|
|
362
458
|
|
|
363
459
|
function parseTelegramProfileArg(args: string): string | undefined {
|
|
364
460
|
const word = args.trim().split(/\s+/)[0];
|
|
365
461
|
if (!word || word.length === 0) return undefined;
|
|
366
|
-
if (word.startsWith("-")) return undefined;
|
|
462
|
+
if (word.startsWith("-") || /^as=/i.test(word)) return undefined;
|
|
367
463
|
return word === TELEGRAM_DEFAULT_PROFILE_NAME ? undefined : word;
|
|
368
464
|
}
|
|
369
465
|
|
|
466
|
+
export function parseTelegramRequestedThreadName(
|
|
467
|
+
args: string,
|
|
468
|
+
): string | undefined {
|
|
469
|
+
const token = args
|
|
470
|
+
.trim()
|
|
471
|
+
.split(/\s+/)
|
|
472
|
+
.find((word) => /^as=/i.test(word));
|
|
473
|
+
const value = token?.slice(3).trim();
|
|
474
|
+
return value || undefined;
|
|
475
|
+
}
|
|
476
|
+
|
|
370
477
|
function formatTelegramTakeoverTitle(ctx: ExtensionCommandContext): string {
|
|
371
478
|
return ctx.ui.theme.fg("accent", "pi-telegram");
|
|
372
479
|
}
|
|
@@ -403,9 +510,28 @@ export function registerTelegramBridgeCommands(
|
|
|
403
510
|
},
|
|
404
511
|
});
|
|
405
512
|
pi.registerCommand("telegram-connect", {
|
|
406
|
-
description:
|
|
513
|
+
description:
|
|
514
|
+
"Start the Telegram bridge. Use /telegram-connect <profile> and optional as=Name for a fresh Workspace Thread.",
|
|
407
515
|
handler: async (args, ctx) => {
|
|
408
516
|
const profileName = parseTelegramProfileArg(args);
|
|
517
|
+
const requestedNameTokens = args
|
|
518
|
+
.trim()
|
|
519
|
+
.split(/\s+/)
|
|
520
|
+
.filter((word) => /^as=/i.test(word));
|
|
521
|
+
const requestedThreadName = parseTelegramRequestedThreadName(args);
|
|
522
|
+
const requestedNameError =
|
|
523
|
+
requestedNameTokens.length > 1
|
|
524
|
+
? "Specify at most one as=Name Workspace Thread name."
|
|
525
|
+
: requestedNameTokens.length === 1 && !requestedThreadName
|
|
526
|
+
? "Usage: /telegram-connect [profile] as=Flightprice"
|
|
527
|
+
: requestedThreadName
|
|
528
|
+
? deps.validateThreadName?.(requestedThreadName)
|
|
529
|
+
: undefined;
|
|
530
|
+
if (requestedNameError) {
|
|
531
|
+
ctx.ui.notify(requestedNameError, "warning");
|
|
532
|
+
deps.updateStatus(ctx);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
409
535
|
if (profileName && deps.activateProfileConfig) {
|
|
410
536
|
const ok = await deps.activateProfileConfig(ctx, profileName);
|
|
411
537
|
if (!ok) {
|
|
@@ -418,6 +544,8 @@ export function registerTelegramBridgeCommands(
|
|
|
418
544
|
await (deps.activateDefaultProfileConfig?.(ctx) ?? deps.reloadConfig());
|
|
419
545
|
}
|
|
420
546
|
if (!deps.hasBotToken()) {
|
|
547
|
+
const botTokenDiagnostic = deps.getBotTokenDiagnostic?.();
|
|
548
|
+
if (botTokenDiagnostic) ctx.ui.notify(botTokenDiagnostic, "error");
|
|
421
549
|
const profileNames = deps.getProfileNames?.() ?? [];
|
|
422
550
|
if (!profileName && profileNames.length > 0) {
|
|
423
551
|
ctx.ui.notify(
|
|
@@ -466,6 +594,7 @@ export function registerTelegramBridgeCommands(
|
|
|
466
594
|
};
|
|
467
595
|
let result = await startWithRecovery({
|
|
468
596
|
forceFreshLeaderThread: true,
|
|
597
|
+
...(requestedThreadName ? { requestedThreadName } : {}),
|
|
469
598
|
});
|
|
470
599
|
if (result && !result.ok && result.canTakeover) {
|
|
471
600
|
const confirmed = await ctx.ui.confirm(
|
|
@@ -480,6 +609,7 @@ export function registerTelegramBridgeCommands(
|
|
|
480
609
|
result = await startWithRecovery({
|
|
481
610
|
force: true,
|
|
482
611
|
forceFreshLeaderThread: true,
|
|
612
|
+
...(requestedThreadName ? { requestedThreadName } : {}),
|
|
483
613
|
});
|
|
484
614
|
}
|
|
485
615
|
if (result?.message) {
|
|
@@ -527,6 +657,7 @@ export function registerTelegramBridgeCommands(
|
|
|
527
657
|
|
|
528
658
|
export const TELEGRAM_RESERVED_COMMAND_NAMES = [
|
|
529
659
|
"stop",
|
|
660
|
+
"name",
|
|
530
661
|
"abort",
|
|
531
662
|
"next",
|
|
532
663
|
"continue",
|
|
@@ -559,6 +690,7 @@ export function isTelegramReservedCommandName(
|
|
|
559
690
|
export type TelegramCommandAction =
|
|
560
691
|
| { kind: "ignore"; executionMode: "ignored" }
|
|
561
692
|
| { kind: "stop"; executionMode: "immediate" }
|
|
693
|
+
| { kind: "name"; executionMode: "immediate" }
|
|
562
694
|
| { kind: "abort"; executionMode: "immediate" }
|
|
563
695
|
| { kind: "next"; executionMode: "immediate" }
|
|
564
696
|
| { kind: "continue"; executionMode: "immediate" }
|
|
@@ -578,6 +710,7 @@ export type TelegramCommandExecutionMode = "ignored" | "immediate";
|
|
|
578
710
|
|
|
579
711
|
export interface TelegramCommandActionDeps<TMessage, TContext> {
|
|
580
712
|
handleStop: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
713
|
+
handleName: (message: TMessage, ctx: TContext, name: string) => Promise<void>;
|
|
581
714
|
handleAbort: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
582
715
|
handleNext: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
583
716
|
handleContinue: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
@@ -971,10 +1104,15 @@ export function createTelegramCommandTargetRuntime<
|
|
|
971
1104
|
export interface TelegramCommandOrPromptRuntimeDeps<TMessage, TContext> {
|
|
972
1105
|
extractRawText: (messages: TMessage[]) => string;
|
|
973
1106
|
shouldIgnoreMessages?: (messages: TMessage[]) => boolean;
|
|
1107
|
+
consumeThreadNameInput?: (
|
|
1108
|
+
messages: TMessage[],
|
|
1109
|
+
ctx: TContext,
|
|
1110
|
+
) => Promise<boolean>;
|
|
974
1111
|
handleCommand: (
|
|
975
1112
|
commandName: string | undefined,
|
|
976
1113
|
message: TMessage,
|
|
977
1114
|
ctx: TContext,
|
|
1115
|
+
commandArgs?: string,
|
|
978
1116
|
) => Promise<boolean>;
|
|
979
1117
|
executeExtensionCommand?: (
|
|
980
1118
|
command: ParsedTelegramCommand,
|
|
@@ -1039,11 +1177,17 @@ export interface TelegramCommandRuntimeDeps<
|
|
|
1039
1177
|
openThinkingMenu: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
1040
1178
|
openQueueMenu: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
1041
1179
|
openSettingsMenu?: (message: TMessage, ctx: TContext) => Promise<void>;
|
|
1180
|
+
validateThreadName?: (threadName: string) => string | undefined;
|
|
1181
|
+
renameCurrentThread?: TelegramThreadDisplayNameRenamePort;
|
|
1182
|
+
resetCurrentThreadName?: TelegramThreadDisplayNameResetPort;
|
|
1183
|
+
openThreadNameDialog?: (
|
|
1184
|
+
message: TMessage,
|
|
1185
|
+
ctx: TContext,
|
|
1186
|
+
) => Promise<void>;
|
|
1042
1187
|
getAllowedUserId: () => number | undefined;
|
|
1043
|
-
|
|
1188
|
+
persistAllowedUserId: TelegramConfigStore["persistAllowedUserId"];
|
|
1044
1189
|
registerBotCommands: () => Promise<void>;
|
|
1045
1190
|
getPromptTemplateCommands?: () => readonly TelegramPromptTemplateMenuCommand[];
|
|
1046
|
-
persistConfig: () => Promise<void>;
|
|
1047
1191
|
sendTextReply: (
|
|
1048
1192
|
message: TMessage,
|
|
1049
1193
|
text: string,
|
|
@@ -1060,6 +1204,7 @@ export const TELEGRAM_APP_MENU_INTRO_HTML = [
|
|
|
1060
1204
|
"<b>Pi Telegram</b>",
|
|
1061
1205
|
"",
|
|
1062
1206
|
`${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
|
|
1207
|
+
`${formatTelegramCommandEmojiPrefix("name")}/name Name — Rename this thread`,
|
|
1063
1208
|
`${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
|
|
1064
1209
|
`${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
|
|
1065
1210
|
`${formatTelegramCommandEmojiPrefix("continue")}/continue — Queue continue prompt`,
|
|
@@ -1100,6 +1245,7 @@ function buildTelegramAppMenuIntroHtml(): string {
|
|
|
1100
1245
|
"<b>Pi Telegram</b>",
|
|
1101
1246
|
"",
|
|
1102
1247
|
`${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
|
|
1248
|
+
`${formatTelegramCommandEmojiPrefix("name")}/name Name — Rename this thread`,
|
|
1103
1249
|
`${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
|
|
1104
1250
|
...extensionLines,
|
|
1105
1251
|
`${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
|
|
@@ -1168,6 +1314,7 @@ export function parseTelegramCommand(
|
|
|
1168
1314
|
|
|
1169
1315
|
export const TELEGRAM_COMMAND_ACTIONS = {
|
|
1170
1316
|
stop: { kind: "stop", executionMode: "immediate" },
|
|
1317
|
+
name: { kind: "name", executionMode: "immediate" },
|
|
1171
1318
|
abort: { kind: "abort", executionMode: "immediate" },
|
|
1172
1319
|
next: { kind: "next", executionMode: "immediate" },
|
|
1173
1320
|
continue: { kind: "continue", executionMode: "immediate" },
|
|
@@ -1294,7 +1441,7 @@ export async function handleTelegramNextCommand(deps: {
|
|
|
1294
1441
|
deps.updateStatus();
|
|
1295
1442
|
const notice = formatTelegramInformationHeading(
|
|
1296
1443
|
"⏩",
|
|
1297
|
-
"
|
|
1444
|
+
"Dispatching next queued turn.",
|
|
1298
1445
|
);
|
|
1299
1446
|
if (activeTurnReply) {
|
|
1300
1447
|
await activeTurnReply(notice, { parseMode: "HTML" });
|
|
@@ -1525,6 +1672,7 @@ export async function executeTelegramCommandAction<TMessage, TContext>(
|
|
|
1525
1672
|
message: TMessage,
|
|
1526
1673
|
ctx: TContext,
|
|
1527
1674
|
deps: TelegramCommandActionDeps<TMessage, TContext>,
|
|
1675
|
+
commandArgs = "",
|
|
1528
1676
|
): Promise<boolean> {
|
|
1529
1677
|
switch (action.kind) {
|
|
1530
1678
|
case "ignore":
|
|
@@ -1532,6 +1680,9 @@ export async function executeTelegramCommandAction<TMessage, TContext>(
|
|
|
1532
1680
|
case "stop":
|
|
1533
1681
|
await deps.handleStop(message, ctx);
|
|
1534
1682
|
return true;
|
|
1683
|
+
case "name":
|
|
1684
|
+
await deps.handleName(message, ctx, commandArgs);
|
|
1685
|
+
return true;
|
|
1535
1686
|
case "abort":
|
|
1536
1687
|
await deps.handleAbort(message, ctx);
|
|
1537
1688
|
return true;
|
|
@@ -1595,6 +1746,7 @@ export function createTelegramCommandHandlerTargetRuntime<
|
|
|
1595
1746
|
commandName: string | undefined,
|
|
1596
1747
|
message: TMessage,
|
|
1597
1748
|
ctx: TContext,
|
|
1749
|
+
commandArgs?: string,
|
|
1598
1750
|
) => Promise<boolean> {
|
|
1599
1751
|
const commandTargetRuntime = createTelegramCommandTargetQueueRuntime<
|
|
1600
1752
|
TMessage,
|
|
@@ -1643,11 +1795,14 @@ export function createTelegramCommandHandlerTargetRuntime<
|
|
|
1643
1795
|
openSettingsMenu: commandTargetRuntime.openSettingsMenu,
|
|
1644
1796
|
handleForumBootstrap: deps.handleForumBootstrap,
|
|
1645
1797
|
getAllowedUserId: deps.getAllowedUserId,
|
|
1646
|
-
|
|
1798
|
+
persistAllowedUserId: deps.persistAllowedUserId,
|
|
1647
1799
|
registerBotCommands: createTelegramBotCommandRegistrar({
|
|
1648
1800
|
setMyCommands: deps.setMyCommands,
|
|
1649
1801
|
}),
|
|
1650
|
-
|
|
1802
|
+
validateThreadName: deps.validateThreadName,
|
|
1803
|
+
renameCurrentThread: deps.renameCurrentThread,
|
|
1804
|
+
resetCurrentThreadName: deps.resetCurrentThreadName,
|
|
1805
|
+
openThreadNameDialog: deps.openThreadNameDialog,
|
|
1651
1806
|
sendTextReply: commandTargetRuntime.sendTextReply,
|
|
1652
1807
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
1653
1808
|
});
|
|
@@ -1661,8 +1816,9 @@ export function createTelegramCommandHandler<
|
|
|
1661
1816
|
commandName: string | undefined,
|
|
1662
1817
|
message: TMessage,
|
|
1663
1818
|
ctx: TContext,
|
|
1819
|
+
commandArgs?: string,
|
|
1664
1820
|
): Promise<boolean> => {
|
|
1665
|
-
return handleTelegramCommandRuntime(commandName, message, ctx, deps);
|
|
1821
|
+
return handleTelegramCommandRuntime(commandName, message, ctx, deps, commandArgs);
|
|
1666
1822
|
};
|
|
1667
1823
|
}
|
|
1668
1824
|
|
|
@@ -1678,11 +1834,16 @@ export function createTelegramCommandOrPromptRuntime<TMessage, TContext>(
|
|
|
1678
1834
|
if (!firstMessage) return;
|
|
1679
1835
|
if (deps.shouldIgnoreMessages?.(messages)) return;
|
|
1680
1836
|
deps.assertExecutionCurrent?.(firstMessage);
|
|
1837
|
+
if (await deps.consumeThreadNameInput?.(messages, ctx)) {
|
|
1838
|
+
deps.assertExecutionCurrent?.(firstMessage);
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1681
1841
|
const command = parseTelegramCommand(deps.extractRawText(messages));
|
|
1682
1842
|
const handled = await deps.handleCommand(
|
|
1683
1843
|
command?.name,
|
|
1684
1844
|
firstMessage,
|
|
1685
1845
|
ctx,
|
|
1846
|
+
command?.args,
|
|
1686
1847
|
);
|
|
1687
1848
|
deps.assertExecutionCurrent?.(firstMessage);
|
|
1688
1849
|
if (handled) return;
|
|
@@ -1755,6 +1916,7 @@ async function handleTelegramCommandRuntime<
|
|
|
1755
1916
|
message: TMessage,
|
|
1756
1917
|
ctx: TContext,
|
|
1757
1918
|
deps: TelegramCommandRuntimeDeps<TMessage, TContext>,
|
|
1919
|
+
commandArgs = "",
|
|
1758
1920
|
): Promise<boolean> {
|
|
1759
1921
|
const assertExecutionCurrentFor = (nextMessage: TMessage) => (): void =>
|
|
1760
1922
|
deps.assertExecutionCurrent?.(nextMessage);
|
|
@@ -1784,6 +1946,69 @@ async function handleTelegramCommandRuntime<
|
|
|
1784
1946
|
sendTextReply: sendReplyFor(nextMessage),
|
|
1785
1947
|
});
|
|
1786
1948
|
},
|
|
1949
|
+
handleName: async (nextMessage, _commandCtx, requestedName) => {
|
|
1950
|
+
const threadName = requestedName.trim();
|
|
1951
|
+
if (!threadName) {
|
|
1952
|
+
if (deps.openThreadNameDialog) {
|
|
1953
|
+
await deps.openThreadNameDialog(nextMessage, _commandCtx);
|
|
1954
|
+
} else {
|
|
1955
|
+
await sendReplyFor(nextMessage)(
|
|
1956
|
+
formatTelegramInformationHeading("🏷️", "Usage: /name Navigator"),
|
|
1957
|
+
{ parseMode: "HTML" },
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
if (/^[A-Z]$/.test(threadName) && deps.resetCurrentThreadName) {
|
|
1963
|
+
const result = await deps.resetCurrentThreadName(
|
|
1964
|
+
getTelegramCommandMessageTarget(nextMessage),
|
|
1965
|
+
);
|
|
1966
|
+
await sendReplyFor(nextMessage)(
|
|
1967
|
+
result.ok && !result.message
|
|
1968
|
+
? formatTelegramAutomaticThreadDisplayNameRestoredHeading(
|
|
1969
|
+
result.threadName ?? threadName,
|
|
1970
|
+
)
|
|
1971
|
+
: formatTelegramInformationHeading(
|
|
1972
|
+
result.ok ? "✅" : "⚠️",
|
|
1973
|
+
result.message ?? "Thread display name reset failed.",
|
|
1974
|
+
),
|
|
1975
|
+
{ parseMode: "HTML" },
|
|
1976
|
+
);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
const validationError = deps.validateThreadName?.(threadName);
|
|
1980
|
+
if (validationError) {
|
|
1981
|
+
await sendReplyFor(nextMessage)(
|
|
1982
|
+
formatTelegramInvalidInstanceName(validationError),
|
|
1983
|
+
{ parseMode: "HTML" },
|
|
1984
|
+
);
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
if (!deps.renameCurrentThread) {
|
|
1988
|
+
await sendReplyFor(nextMessage)(
|
|
1989
|
+
formatTelegramInformationHeading("🚫", "Thread display naming is unavailable."),
|
|
1990
|
+
{ parseMode: "HTML" },
|
|
1991
|
+
);
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
deps.assertExecutionCurrent?.(nextMessage);
|
|
1995
|
+
const result = await deps.renameCurrentThread(
|
|
1996
|
+
getTelegramCommandMessageTarget(nextMessage),
|
|
1997
|
+
threadName,
|
|
1998
|
+
);
|
|
1999
|
+
deps.assertExecutionCurrent?.(nextMessage);
|
|
2000
|
+
await sendReplyFor(nextMessage)(
|
|
2001
|
+
result.ok && !result.message
|
|
2002
|
+
? formatTelegramThreadDisplayNameSavedHeading(
|
|
2003
|
+
result.threadName ?? threadName,
|
|
2004
|
+
)
|
|
2005
|
+
: formatTelegramInformationHeading(
|
|
2006
|
+
result.ok ? "✅" : "⚠️",
|
|
2007
|
+
result.message ?? "Thread display name update failed.",
|
|
2008
|
+
),
|
|
2009
|
+
{ parseMode: "HTML" },
|
|
2010
|
+
);
|
|
2011
|
+
},
|
|
1787
2012
|
handleAbort: async (nextMessage, commandCtx) => {
|
|
1788
2013
|
await handleTelegramAbortCommand({
|
|
1789
2014
|
hasAbortHandler: deps.hasAbortHandler,
|
|
@@ -1916,14 +2141,14 @@ async function handleTelegramCommandRuntime<
|
|
|
1916
2141
|
nextMessage.from?.id !== undefined &&
|
|
1917
2142
|
canPairTelegramUserFromCommandMessage(nextMessage)
|
|
1918
2143
|
) {
|
|
1919
|
-
await pairTelegramUserIfNeeded(nextMessage.from.id, {
|
|
2144
|
+
const allowed = await pairTelegramUserIfNeeded(nextMessage.from.id, {
|
|
1920
2145
|
allowedUserId: deps.getAllowedUserId(),
|
|
1921
2146
|
ctx: undefined,
|
|
1922
|
-
|
|
1923
|
-
persistConfig: deps.persistConfig,
|
|
2147
|
+
persistAllowedUserId: deps.persistAllowedUserId,
|
|
1924
2148
|
updateStatus: updateStatusFor(commandCtx),
|
|
1925
2149
|
assertExecutionCurrent: assertExecutionCurrentFor(nextMessage),
|
|
1926
2150
|
});
|
|
2151
|
+
if (!allowed) return;
|
|
1927
2152
|
}
|
|
1928
2153
|
const isContextActive = () =>
|
|
1929
2154
|
deps.isContextActive?.(commandCtx) !== false;
|
|
@@ -1959,5 +2184,6 @@ async function handleTelegramCommandRuntime<
|
|
|
1959
2184
|
);
|
|
1960
2185
|
},
|
|
1961
2186
|
},
|
|
2187
|
+
commandArgs,
|
|
1962
2188
|
);
|
|
1963
2189
|
}
|