@llblab/pi-telegram 0.52.2 → 0.53.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 +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +11 -0
- package/README.md +7 -11
- package/api/commands.ts +3 -0
- package/dist/api/commands.d.ts +1 -1
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +15 -42
- package/dist/lib/activity.d.ts +9 -0
- package/dist/lib/activity.js +39 -10
- package/dist/lib/agent-messages.js +7 -3
- package/dist/lib/bindings.d.ts +43 -5
- package/dist/lib/bindings.js +163 -20
- package/dist/lib/bus-api.d.ts +12 -0
- package/dist/lib/bus-api.js +182 -79
- package/dist/lib/bus-follower.d.ts +111 -31
- package/dist/lib/bus-follower.js +1324 -359
- package/dist/lib/bus-leader.d.ts +41 -91
- package/dist/lib/bus-leader.js +969 -637
- package/dist/lib/bus-transport.js +16 -9
- package/dist/lib/bus.d.ts +215 -18
- package/dist/lib/bus.js +1048 -327
- package/dist/lib/channel-posts.d.ts +5 -0
- package/dist/lib/channel-posts.js +434 -261
- package/dist/lib/command-templates.d.ts +26 -5
- package/dist/lib/command-templates.js +92 -176
- package/dist/lib/commands.d.ts +168 -82
- package/dist/lib/commands.js +1409 -291
- package/dist/lib/config.d.ts +1 -8
- package/dist/lib/config.js +34 -76
- package/dist/lib/delivery.d.ts +2 -2
- package/dist/lib/delivery.js +16 -16
- package/dist/lib/extension.js +639 -171
- package/dist/lib/generative-app-worker.mjs +19 -7
- package/dist/lib/generative-apps.d.ts +0 -5
- package/dist/lib/generative-apps.js +61 -49
- package/dist/lib/inbound.d.ts +5 -0
- package/dist/lib/inbound.js +35 -59
- package/dist/lib/journal.d.ts +33 -3
- package/dist/lib/journal.js +2063 -782
- package/dist/lib/lifecycle.d.ts +2 -6
- package/dist/lib/lifecycle.js +75 -58
- package/dist/lib/locks.d.ts +29 -3
- package/dist/lib/locks.js +289 -108
- package/dist/lib/logging.js +50 -14
- package/dist/lib/media.d.ts +22 -1
- package/dist/lib/media.js +46 -28
- package/dist/lib/menu-model.d.ts +19 -5
- package/dist/lib/menu-model.js +53 -11
- package/dist/lib/menu-queue.d.ts +5 -4
- package/dist/lib/menu-queue.js +45 -63
- package/dist/lib/menu-settings.d.ts +5 -4
- package/dist/lib/menu-settings.js +114 -78
- package/dist/lib/menu-status.d.ts +1 -1
- package/dist/lib/menu-status.js +8 -25
- package/dist/lib/menu-thinking.d.ts +6 -1
- package/dist/lib/menu-thinking.js +14 -21
- package/dist/lib/menu.d.ts +8 -61
- package/dist/lib/menu.js +92 -164
- package/dist/lib/model.d.ts +1 -1
- package/dist/lib/model.js +2 -16
- package/dist/lib/outbound-attachments.d.ts +6 -27
- package/dist/lib/outbound-attachments.js +45 -38
- package/dist/lib/outbound-buttons.js +22 -16
- package/dist/lib/outbound-markup.d.ts +0 -2
- package/dist/lib/outbound-markup.js +51 -64
- package/dist/lib/outbound-voice.js +6 -3
- package/dist/lib/outbound.d.ts +6 -6
- package/dist/lib/outbound.js +61 -51
- package/dist/lib/ownership.js +6 -2
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +37 -21
- package/dist/lib/pi.d.ts +2 -0
- package/dist/lib/pi.js +8 -1
- package/dist/lib/polling.d.ts +21 -37
- package/dist/lib/polling.js +59 -50
- package/dist/lib/preview.js +39 -19
- package/dist/lib/process-identity.d.ts +24 -0
- package/dist/lib/process-identity.js +133 -0
- package/dist/lib/prompts.js +2 -7
- package/dist/lib/queue.d.ts +14 -16
- package/dist/lib/queue.js +249 -126
- package/dist/lib/recovery.d.ts +2 -83
- package/dist/lib/recovery.js +20 -274
- package/dist/lib/replies.d.ts +16 -24
- package/dist/lib/replies.js +99 -116
- package/dist/lib/routing.d.ts +120 -7
- package/dist/lib/routing.js +4667 -1152
- package/dist/lib/runtime.d.ts +1 -11
- package/dist/lib/sections.d.ts +4 -29
- package/dist/lib/sections.js +7 -20
- package/dist/lib/skills.js +4 -3
- package/dist/lib/status.js +86 -57
- package/dist/lib/sync.d.ts +1 -3
- package/dist/lib/sync.js +67 -43
- package/dist/lib/target.d.ts +4 -0
- package/dist/lib/target.js +18 -0
- package/dist/lib/telegram-api.d.ts +18 -13
- package/dist/lib/telegram-api.js +148 -53
- package/dist/lib/text-groups.js +4 -39
- package/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/dist/lib/thread-cleanup-manager.js +369 -186
- package/dist/lib/thread-display.d.ts +1 -2
- package/dist/lib/thread-display.js +58 -38
- package/dist/lib/thread-naming.d.ts +28 -8
- package/dist/lib/thread-naming.js +175 -66
- package/dist/lib/thread-reconciler.d.ts +42 -16
- package/dist/lib/thread-reconciler.js +90 -50
- package/dist/lib/threads.d.ts +82 -20
- package/dist/lib/threads.js +2247 -855
- package/dist/lib/time-injection.js +1 -2
- package/dist/lib/turns.js +1 -19
- package/dist/lib/updates.d.ts +169 -80
- package/dist/lib/updates.js +2418 -641
- package/dist/lib/voice.d.ts +7 -22
- package/dist/lib/voice.js +7 -22
- package/dist/lib/wire.js +1 -1
- package/dist/lib/workspace-admission.d.ts +3 -1
- package/dist/lib/workspace-admission.js +425 -211
- package/dist/lib/workspace-identity.js +8 -13
- package/dist/lib/workspace-retirement.d.ts +1 -1
- package/dist/lib/workspace-retirement.js +328 -140
- package/dist/lib/workspace-slots.js +8 -4
- package/dist/package.json +1 -1
- package/docs/architecture.md +31 -29
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +15 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +29 -1
- package/docs/ui-style.md +25 -8
- package/lib/activity-verbosity.ts +59 -82
- package/lib/activity.ts +146 -102
- package/lib/agent-messages.ts +12 -5
- package/lib/bindings.ts +389 -82
- package/lib/bus-api.ts +263 -102
- package/lib/bus-follower.ts +2440 -601
- package/lib/bus-leader.ts +2209 -1290
- package/lib/bus-transport.ts +46 -12
- package/lib/bus.ts +1673 -444
- package/lib/channel-posts.ts +848 -327
- package/lib/command-templates.ts +152 -265
- package/lib/commands.ts +2398 -555
- package/lib/config.ts +177 -170
- package/lib/delivery.ts +41 -29
- package/lib/extension.ts +1206 -433
- package/lib/generative-app-worker.mjs +19 -7
- package/lib/generative-apps.ts +324 -211
- package/lib/inbound.ts +95 -97
- package/lib/journal.ts +5311 -1763
- package/lib/keyboard.ts +1 -3
- package/lib/lifecycle.ts +128 -78
- package/lib/locks.ts +677 -203
- package/lib/logging.ts +147 -58
- package/lib/media.ts +82 -43
- package/lib/menu-model.ts +85 -18
- package/lib/menu-queue.ts +88 -129
- package/lib/menu-settings.ts +260 -155
- package/lib/menu-status.ts +20 -60
- package/lib/menu-thinking.ts +41 -56
- package/lib/menu.ts +112 -285
- package/lib/model.ts +8 -22
- package/lib/outbound-attachments.ts +156 -125
- package/lib/outbound-buttons.ts +76 -49
- package/lib/outbound-markup.ts +96 -94
- package/lib/outbound-voice.ts +33 -27
- package/lib/outbound.ts +125 -125
- package/lib/ownership.ts +19 -10
- package/lib/paths.ts +148 -49
- package/lib/pi.ts +35 -15
- package/lib/polling.ts +144 -147
- package/lib/preview.ts +93 -40
- package/lib/process-identity.ts +164 -0
- package/lib/prompts.ts +4 -11
- package/lib/queue.ts +511 -300
- package/lib/recovery.ts +50 -454
- package/lib/rendering.ts +1 -3
- package/lib/replies.ts +248 -209
- package/lib/routing.ts +8509 -2186
- package/lib/runtime.ts +1 -12
- package/lib/sections.ts +24 -89
- package/lib/setup.ts +1 -3
- package/lib/skills.ts +9 -4
- package/lib/status.ts +215 -110
- package/lib/sync.ts +448 -361
- package/lib/target.ts +22 -3
- package/lib/telegram-api.ts +302 -127
- package/lib/text-groups.ts +5 -39
- package/lib/thread-cleanup-manager.ts +833 -331
- package/lib/thread-display.ts +213 -97
- package/lib/thread-naming.ts +232 -87
- package/lib/thread-reconciler.ts +155 -72
- package/lib/threads.ts +5318 -1845
- package/lib/time-injection.ts +1 -4
- package/lib/turns.ts +15 -28
- package/lib/updates.ts +5184 -1612
- package/lib/voice.ts +7 -35
- package/lib/wire.ts +1 -1
- package/lib/workspace-admission.ts +996 -416
- package/lib/workspace-identity.ts +14 -17
- package/lib/workspace-retirement.ts +878 -376
- package/lib/workspace-slots.ts +22 -13
- package/package.json +1 -1
- package/scripts/check-downgrade.mjs +0 -472
- package/scripts/measure-workspace.mjs +0 -108
package/dist/lib/runtime.d.ts
CHANGED
|
@@ -142,18 +142,8 @@ export interface TelegramPromptDispatchLifecycleDeps<TContext> extends TelegramR
|
|
|
142
142
|
}) => boolean | void;
|
|
143
143
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
144
144
|
}
|
|
145
|
-
export interface TelegramPromptDispatchRuntimeDeps<TContext> extends
|
|
145
|
+
export interface TelegramPromptDispatchRuntimeDeps<TContext> extends TelegramTypingLoopStarterDeps<TContext> {
|
|
146
146
|
lifecycle: TelegramPromptDispatchLifecycleDeps<TContext>["lifecycle"];
|
|
147
|
-
typing: TelegramRuntimeTypingPort;
|
|
148
|
-
getDefaultChatId: () => number | undefined;
|
|
149
|
-
sendTypingAction: (chatId: number, options?: {
|
|
150
|
-
message_thread_id?: number;
|
|
151
|
-
}) => Promise<unknown>;
|
|
152
|
-
updateStatus: (ctx: TContext, error?: string) => void;
|
|
153
|
-
isContextActive?: (ctx: TContext) => boolean;
|
|
154
|
-
isTransportAvailable?: () => boolean;
|
|
155
|
-
getTransportAuthority?: () => string | number | undefined;
|
|
156
|
-
intervalMs?: number;
|
|
157
147
|
}
|
|
158
148
|
export interface TelegramPromptDispatchRuntime<TContext> {
|
|
159
149
|
startTypingLoop: (ctx: TContext, chatId?: number, options?: {
|
package/dist/lib/sections.d.ts
CHANGED
|
@@ -50,20 +50,9 @@ export interface TelegramSectionContext {
|
|
|
50
50
|
/** Delete the message that triggered this callback (dialog cleanup) */
|
|
51
51
|
deleteMessage(): Promise<void>;
|
|
52
52
|
}
|
|
53
|
-
export interface TelegramSectionCallbackContext {
|
|
54
|
-
sectionId: string;
|
|
55
|
-
chatId: number;
|
|
56
|
-
messageId?: number;
|
|
53
|
+
export interface TelegramSectionCallbackContext extends TelegramSectionContext {
|
|
57
54
|
action: string;
|
|
58
55
|
payload: string;
|
|
59
|
-
answerCallback(text?: string): Promise<void>;
|
|
60
|
-
edit(view: TelegramSectionView): Promise<void>;
|
|
61
|
-
open(view: TelegramSectionView): Promise<void>;
|
|
62
|
-
openRich(message: TelegramInputRichMessage): Promise<void>;
|
|
63
|
-
enqueuePrompt(prompt: string): Promise<void>;
|
|
64
|
-
callbackData(action: string, payload?: string): string;
|
|
65
|
-
/** Delete the message that triggered this callback (dialog cleanup) */
|
|
66
|
-
deleteMessage(): Promise<void>;
|
|
67
56
|
}
|
|
68
57
|
/** @internal */
|
|
69
58
|
export interface RegisteredTelegramSection {
|
|
@@ -144,20 +133,6 @@ export declare function parseTelegramSectionCallback(data: string): {
|
|
|
144
133
|
action: string;
|
|
145
134
|
payload: string;
|
|
146
135
|
} | undefined;
|
|
147
|
-
|
|
148
|
-
export
|
|
149
|
-
|
|
150
|
-
target?: TelegramSectionTarget;
|
|
151
|
-
editInteractiveMessage: (chatId: number, messageId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: TelegramInlineKeyboardMarkup) => Promise<void>;
|
|
152
|
-
sendInteractiveMessage: (chatId: number, text: string, mode: "markdown" | "html" | "plain", replyMarkup: TelegramInlineKeyboardMarkup, options?: {
|
|
153
|
-
target?: TelegramSectionTarget;
|
|
154
|
-
}) => Promise<number | undefined>;
|
|
155
|
-
sendRichMessage: (chatId: number, message: TelegramInputRichMessage, options?: {
|
|
156
|
-
target?: TelegramSectionTarget;
|
|
157
|
-
}) => Promise<number | undefined>;
|
|
158
|
-
enqueuePrompt: (prompt: string) => Promise<void>;
|
|
159
|
-
deleteMessage: (chatId: number, messageId: number) => Promise<void>;
|
|
160
|
-
}
|
|
161
|
-
export declare function handleTelegramSectionOpen(registry: TelegramSectionRegistry, token: TelegramSectionToken, chatId: number, messageId: number, callbackQueryId: string, deps: TelegramSectionCallbackHandlerDeps): Promise<boolean>;
|
|
162
|
-
export declare function handleTelegramSectionCallback(registry: TelegramSectionRegistry, token: TelegramSectionToken, action: string, payload: string, chatId: number, messageId: number, callbackQueryId: string, deps: TelegramSectionCallbackHandlerDeps): Promise<boolean>;
|
|
163
|
-
export declare function handleTelegramSectionSettingsOpen(registry: TelegramSectionRegistry, token: TelegramSectionToken, chatId: number, messageId: number, callbackQueryId: string, deps: TelegramSectionCallbackHandlerDeps): Promise<boolean>;
|
|
136
|
+
export declare function handleTelegramSectionOpen(registry: TelegramSectionRegistry, token: TelegramSectionToken, chatId: number, messageId: number, callbackQueryId: string, deps: TelegramSectionRuntimeDeps): Promise<boolean>;
|
|
137
|
+
export declare function handleTelegramSectionCallback(registry: TelegramSectionRegistry, token: TelegramSectionToken, action: string, payload: string, chatId: number, messageId: number, callbackQueryId: string, deps: TelegramSectionRuntimeDeps): Promise<boolean>;
|
|
138
|
+
export declare function handleTelegramSectionSettingsOpen(registry: TelegramSectionRegistry, token: TelegramSectionToken, chatId: number, messageId: number, callbackQueryId: string, deps: TelegramSectionRuntimeDeps): Promise<boolean>;
|
package/dist/lib/sections.js
CHANGED
|
@@ -19,7 +19,9 @@ function buildTelegramSectionContext(sectionId, token, chatId, messageId, callba
|
|
|
19
19
|
open: (view) => deps
|
|
20
20
|
.sendInteractiveMessage(chatId, view.text, view.parseMode ?? "html", view.replyMarkup ?? { inline_keyboard: [] }, deps.target ? { target: deps.target } : undefined)
|
|
21
21
|
.then(() => { }),
|
|
22
|
-
openRich: (message) => deps
|
|
22
|
+
openRich: (message) => deps
|
|
23
|
+
.sendRichMessage(chatId, message, deps.target ? { target: deps.target } : undefined)
|
|
24
|
+
.then(() => { }),
|
|
23
25
|
enqueuePrompt: deps.enqueuePrompt,
|
|
24
26
|
callbackData: (action, payload) => buildTelegramSectionCallbackData(token, action, payload),
|
|
25
27
|
deleteMessage: () => messageId !== undefined
|
|
@@ -29,24 +31,9 @@ function buildTelegramSectionContext(sectionId, token, chatId, messageId, callba
|
|
|
29
31
|
}
|
|
30
32
|
function buildTelegramSectionCallbackContext(sectionId, token, chatId, messageId, action, payload, callbackQueryId, deps, backCallback = "menu:back", backLabel = "⬆️ Back") {
|
|
31
33
|
return {
|
|
32
|
-
sectionId,
|
|
33
|
-
chatId,
|
|
34
|
-
messageId,
|
|
34
|
+
...buildTelegramSectionContext(sectionId, token, chatId, messageId, callbackQueryId, deps, backCallback, backLabel),
|
|
35
35
|
action,
|
|
36
36
|
payload,
|
|
37
|
-
answerCallback: (text) => deps.answerCallbackQuery(callbackQueryId, text),
|
|
38
|
-
edit: (view) => messageId !== undefined
|
|
39
|
-
? deps.editInteractiveMessage(chatId, messageId, view.text, view.parseMode ?? "html", prependBackRow(view.replyMarkup, backCallback, backLabel))
|
|
40
|
-
: Promise.resolve(),
|
|
41
|
-
open: (view) => deps
|
|
42
|
-
.sendInteractiveMessage(chatId, view.text, view.parseMode ?? "html", view.replyMarkup ?? { inline_keyboard: [] }, deps.target ? { target: deps.target } : undefined)
|
|
43
|
-
.then(() => { }),
|
|
44
|
-
openRich: (message) => deps.sendRichMessage(chatId, message, deps.target ? { target: deps.target } : undefined).then(() => { }),
|
|
45
|
-
enqueuePrompt: deps.enqueuePrompt,
|
|
46
|
-
callbackData: (action, payload) => buildTelegramSectionCallbackData(token, action, payload),
|
|
47
|
-
deleteMessage: () => messageId !== undefined
|
|
48
|
-
? deps.deleteMessage(chatId, messageId)
|
|
49
|
-
: Promise.resolve(),
|
|
50
37
|
};
|
|
51
38
|
}
|
|
52
39
|
// --- GlobalThis Bridge ---
|
|
@@ -242,7 +229,7 @@ export function parseTelegramSectionCallback(data) {
|
|
|
242
229
|
export async function handleTelegramSectionOpen(registry, token, chatId, messageId, callbackQueryId, deps) {
|
|
243
230
|
const section = registry.getByToken(token);
|
|
244
231
|
if (!section) {
|
|
245
|
-
await deps.answerCallbackQuery(callbackQueryId, "This section is no longer available
|
|
232
|
+
await deps.answerCallbackQuery(callbackQueryId, "This section is no longer available");
|
|
246
233
|
return true;
|
|
247
234
|
}
|
|
248
235
|
try {
|
|
@@ -267,7 +254,7 @@ export async function handleTelegramSectionOpen(registry, token, chatId, message
|
|
|
267
254
|
export async function handleTelegramSectionCallback(registry, token, action, payload, chatId, messageId, callbackQueryId, deps) {
|
|
268
255
|
const section = registry.getByToken(token);
|
|
269
256
|
if (!section) {
|
|
270
|
-
await deps.answerCallbackQuery(callbackQueryId, "This section is no longer available
|
|
257
|
+
await deps.answerCallbackQuery(callbackQueryId, "This section is no longer available");
|
|
271
258
|
return true;
|
|
272
259
|
}
|
|
273
260
|
// Try main handleCallback first, then settings handleCallback as fallback.
|
|
@@ -303,7 +290,7 @@ export async function handleTelegramSectionCallback(registry, token, action, pay
|
|
|
303
290
|
export async function handleTelegramSectionSettingsOpen(registry, token, chatId, messageId, callbackQueryId, deps) {
|
|
304
291
|
const section = registry.getByToken(token);
|
|
305
292
|
if (!section || !section.registration.settings) {
|
|
306
|
-
await deps.answerCallbackQuery(callbackQueryId, "This section is no longer available
|
|
293
|
+
await deps.answerCallbackQuery(callbackQueryId, "This section is no longer available");
|
|
307
294
|
return true;
|
|
308
295
|
}
|
|
309
296
|
try {
|
package/dist/lib/skills.js
CHANGED
|
@@ -13,7 +13,8 @@ export function getTelegramExtensionPackageRoot(modulePath) {
|
|
|
13
13
|
while (true) {
|
|
14
14
|
if (existsSync(join(current, "package.json"))) {
|
|
15
15
|
const parent = dirname(current);
|
|
16
|
-
if (basename(current) === "dist" &&
|
|
16
|
+
if (basename(current) === "dist" &&
|
|
17
|
+
existsSync(join(parent, "package.json"))) {
|
|
17
18
|
return parent;
|
|
18
19
|
}
|
|
19
20
|
return current;
|
|
@@ -28,8 +29,8 @@ export function isRawTelegramExtensionCheckout(modulePath, options = {}) {
|
|
|
28
29
|
const packageRoot = resolve(getTelegramExtensionPackageRoot(modulePath));
|
|
29
30
|
const agentDir = resolve(options.agentDir ?? resolveAgentDir());
|
|
30
31
|
const cwd = resolve(options.cwd ?? process.cwd());
|
|
31
|
-
return dirname(packageRoot) === join(agentDir, "extensions") ||
|
|
32
|
-
dirname(packageRoot) === join(cwd, ".pi", "extensions");
|
|
32
|
+
return (dirname(packageRoot) === join(agentDir, "extensions") ||
|
|
33
|
+
dirname(packageRoot) === join(cwd, ".pi", "extensions"));
|
|
33
34
|
}
|
|
34
35
|
export const TELEGRAM_SKILLS_PATH = join(getTelegramExtensionPackageRoot(TELEGRAM_SKILLS_MODULE_PATH), "skills");
|
|
35
36
|
export function registerTelegramSkillDiscovery(pi, modulePath = TELEGRAM_SKILLS_MODULE_PATH, options = {}) {
|
package/dist/lib/status.js
CHANGED
|
@@ -7,21 +7,31 @@
|
|
|
7
7
|
import { isDeepStrictEqual } from "node:util";
|
|
8
8
|
/** UI copy is allowlisted; raw exception text belongs only in redacted diagnostics. */
|
|
9
9
|
export function formatTelegramConnectionFailure(error) {
|
|
10
|
-
const message = error instanceof Error
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const message = error instanceof Error
|
|
11
|
+
? error.message
|
|
12
|
+
: typeof error === "string"
|
|
13
|
+
? error
|
|
14
|
+
: "";
|
|
15
|
+
const status = error && typeof error === "object" && "status" in error
|
|
16
|
+
? error.status
|
|
17
|
+
: undefined;
|
|
18
|
+
const code = error && typeof error === "object" && "code" in error
|
|
19
|
+
? error.code
|
|
20
|
+
: undefined;
|
|
13
21
|
if (status === 401)
|
|
14
22
|
return "Telegram token rejected. Run /telegram-setup.";
|
|
15
23
|
if (status === 403)
|
|
16
24
|
return "Telegram access denied. Check /telegram-status --debug.";
|
|
17
|
-
if ((typeof code === "string" &&
|
|
25
|
+
if ((typeof code === "string" &&
|
|
26
|
+
["ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND", "ECONNRESET"].includes(code)) ||
|
|
18
27
|
/network unavailable|fetch failed/i.test(message)) {
|
|
19
28
|
return "Telegram network unavailable. Retry /telegram-connect.";
|
|
20
29
|
}
|
|
21
30
|
if (/Workspace slots?.*(?:unavailable|exhausted)|no free.*slot/i.test(message)) {
|
|
22
31
|
return "No Telegram slot available. Check /telegram-status --debug.";
|
|
23
32
|
}
|
|
24
|
-
if (code === "incompatible-protocol" ||
|
|
33
|
+
if (code === "incompatible-protocol" ||
|
|
34
|
+
/protocol.*(?:incompatible|mismatch)|incompatible.*protocol/i.test(message)) {
|
|
25
35
|
return "Telegram instances are incompatible. Update them together.";
|
|
26
36
|
}
|
|
27
37
|
if (/unsupported.*(?:journal|version)|(?:journal|version).*unsupported/i.test(message)) {
|
|
@@ -61,17 +71,11 @@ function truncateTelegramRuntimeEventText(text, maxLength) {
|
|
|
61
71
|
return text;
|
|
62
72
|
return `${text.slice(0, maxLength).trimEnd()}… [truncated ${text.length - maxLength} chars]`;
|
|
63
73
|
}
|
|
64
|
-
function
|
|
65
|
-
const redacted = botToken
|
|
66
|
-
? message.split(botToken).join("<redacted-token>")
|
|
67
|
-
: message;
|
|
68
|
-
return truncateTelegramRuntimeEventText(redacted, MAX_TELEGRAM_RUNTIME_EVENT_MESSAGE_LENGTH);
|
|
69
|
-
}
|
|
70
|
-
function redactTelegramRuntimeDetail(message, botToken) {
|
|
74
|
+
function redactTelegramRuntimeText(text, botToken, maxLength) {
|
|
71
75
|
const redacted = botToken
|
|
72
|
-
?
|
|
73
|
-
:
|
|
74
|
-
return truncateTelegramRuntimeEventText(redacted,
|
|
76
|
+
? text.split(botToken).join("<redacted-token>")
|
|
77
|
+
: text;
|
|
78
|
+
return truncateTelegramRuntimeEventText(redacted, maxLength);
|
|
75
79
|
}
|
|
76
80
|
function normalizeTelegramRuntimeEventDetails(details, botToken) {
|
|
77
81
|
if (!details)
|
|
@@ -81,7 +85,7 @@ function normalizeTelegramRuntimeEventDetails(details, botToken) {
|
|
|
81
85
|
if (value === undefined)
|
|
82
86
|
continue;
|
|
83
87
|
if (typeof value === "string") {
|
|
84
|
-
normalized[key] =
|
|
88
|
+
normalized[key] = redactTelegramRuntimeText(value, botToken, MAX_TELEGRAM_RUNTIME_EVENT_DETAIL_LENGTH);
|
|
85
89
|
continue;
|
|
86
90
|
}
|
|
87
91
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
@@ -92,7 +96,7 @@ function normalizeTelegramRuntimeEventDetails(details, botToken) {
|
|
|
92
96
|
normalized[key] = null;
|
|
93
97
|
continue;
|
|
94
98
|
}
|
|
95
|
-
normalized[key] =
|
|
99
|
+
normalized[key] = redactTelegramRuntimeText(String(value), botToken, MAX_TELEGRAM_RUNTIME_EVENT_DETAIL_LENGTH);
|
|
96
100
|
}
|
|
97
101
|
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
98
102
|
}
|
|
@@ -108,7 +112,7 @@ export function recordStructuredTelegramRuntimeEvent(events, input, options) {
|
|
|
108
112
|
events.push({
|
|
109
113
|
at: options.now ?? Date.now(),
|
|
110
114
|
category: input.category,
|
|
111
|
-
message:
|
|
115
|
+
message: redactTelegramRuntimeText(getTelegramRuntimeEventMessage(input), options.botToken, MAX_TELEGRAM_RUNTIME_EVENT_MESSAGE_LENGTH),
|
|
112
116
|
...(details ? { details } : {}),
|
|
113
117
|
});
|
|
114
118
|
while (events.length > options.maxEvents) {
|
|
@@ -248,7 +252,9 @@ export function createTelegramBridgeStatusRuntime(deps) {
|
|
|
248
252
|
const config = deps.getConfig();
|
|
249
253
|
const queuedItems = deps.getQueuedItems();
|
|
250
254
|
const hasPendingDispatch = deps.hasDispatchPending();
|
|
251
|
-
const waitingItems = hasPendingDispatch
|
|
255
|
+
const waitingItems = hasPendingDispatch
|
|
256
|
+
? queuedItems.slice(1)
|
|
257
|
+
: queuedItems;
|
|
252
258
|
const queuedItemCount = deps.getQueuedItemCount?.(waitingItems) ?? waitingItems.length;
|
|
253
259
|
const hasActiveTurn = deps.hasActiveTurn();
|
|
254
260
|
const hasPendingModelSwitch = deps.hasPendingModelSwitch();
|
|
@@ -305,9 +311,7 @@ export function createTelegramBridgeStatusRuntime(deps) {
|
|
|
305
311
|
instanceThreadName: deps.getInstanceThreadName?.(),
|
|
306
312
|
lockState: deps.getRuntimeLockState?.(),
|
|
307
313
|
pollingActive: deps.isPollingActive(),
|
|
308
|
-
...(deps.getPollingState
|
|
309
|
-
? { polling: deps.getPollingState() }
|
|
310
|
-
: {}),
|
|
314
|
+
...(deps.getPollingState ? { polling: deps.getPollingState() } : {}),
|
|
311
315
|
...(deps.getInboundWorkerState
|
|
312
316
|
? { inboundWorker: deps.getInboundWorkerState() }
|
|
313
317
|
: {}),
|
|
@@ -377,9 +381,21 @@ function isRuntimeProjection(value) {
|
|
|
377
381
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
378
382
|
return false;
|
|
379
383
|
const record = value;
|
|
380
|
-
return record.version === 1 &&
|
|
381
|
-
|
|
382
|
-
|
|
384
|
+
return (record.version === 1 &&
|
|
385
|
+
record.source === "snapshot" &&
|
|
386
|
+
Number.isSafeInteger(record.writtenAtMs) &&
|
|
387
|
+
record.writtenAtMs >= 0 &&
|
|
388
|
+
Object.keys(record).every((key) => [
|
|
389
|
+
"version",
|
|
390
|
+
"source",
|
|
391
|
+
"writtenAtMs",
|
|
392
|
+
"runtime",
|
|
393
|
+
"liveRoster",
|
|
394
|
+
"diagnostics",
|
|
395
|
+
].includes(key)) &&
|
|
396
|
+
["runtime", "liveRoster", "diagnostics"].every((key) => record[key] !== null &&
|
|
397
|
+
typeof record[key] === "object" &&
|
|
398
|
+
!Array.isArray(record[key])));
|
|
383
399
|
}
|
|
384
400
|
/** Observational runtime storage only; Workspace, admission, transport, logs and recovery remain with their owners. */
|
|
385
401
|
export function createTelegramRuntimeProjectionStore(options) {
|
|
@@ -392,7 +408,11 @@ export function createTelegramRuntimeProjectionStore(options) {
|
|
|
392
408
|
read() {
|
|
393
409
|
try {
|
|
394
410
|
const path = getPath(), profile = getProfile(), value = read({ path, profile });
|
|
395
|
-
return getPath() === path &&
|
|
411
|
+
return getPath() === path &&
|
|
412
|
+
getProfile() === profile &&
|
|
413
|
+
isRuntimeProjection(value)
|
|
414
|
+
? value
|
|
415
|
+
: undefined;
|
|
396
416
|
}
|
|
397
417
|
catch {
|
|
398
418
|
return undefined;
|
|
@@ -403,8 +423,13 @@ export function createTelegramRuntimeProjectionStore(options) {
|
|
|
403
423
|
if (!authority)
|
|
404
424
|
return false;
|
|
405
425
|
// Freeze wire content at submission, but do not duplicate the event log in persistent runtime state.
|
|
406
|
-
const semantic = JSON.parse(JSON.stringify({
|
|
407
|
-
|
|
426
|
+
const semantic = JSON.parse(JSON.stringify({
|
|
427
|
+
version: 1,
|
|
428
|
+
source: "snapshot",
|
|
429
|
+
runtime: snapshot.runtime,
|
|
430
|
+
liveRoster: snapshot.liveRoster,
|
|
431
|
+
diagnostics: snapshot.diagnostics,
|
|
432
|
+
}));
|
|
408
433
|
if (!isRuntimeProjection({ ...semantic, writtenAtMs: 0 }))
|
|
409
434
|
throw new Error("Telegram runtime projection is malformed.");
|
|
410
435
|
delete semantic.diagnostics.recentRuntimeEvents;
|
|
@@ -412,7 +437,7 @@ export function createTelegramRuntimeProjectionStore(options) {
|
|
|
412
437
|
const publication = publicationQueue.then(() => {
|
|
413
438
|
if (!isCurrent())
|
|
414
439
|
return false;
|
|
415
|
-
return publish({ path, profile }, current => {
|
|
440
|
+
return (publish({ path, profile }, (current) => {
|
|
416
441
|
if (isRuntimeProjection(current)) {
|
|
417
442
|
const { writtenAtMs: _writtenAtMs, ...existing } = current;
|
|
418
443
|
if (isDeepStrictEqual(existing, semantic))
|
|
@@ -422,7 +447,7 @@ export function createTelegramRuntimeProjectionStore(options) {
|
|
|
422
447
|
if (!Number.isSafeInteger(writtenAtMs) || writtenAtMs < 0)
|
|
423
448
|
throw new Error("Telegram runtime projection timestamp is invalid.");
|
|
424
449
|
return { value: { ...semantic, writtenAtMs }, changed: true };
|
|
425
|
-
}, isCurrent) === true;
|
|
450
|
+
}, isCurrent) === true);
|
|
426
451
|
});
|
|
427
452
|
publicationQueue = publication.then(() => undefined, () => undefined);
|
|
428
453
|
return publication;
|
|
@@ -432,7 +457,8 @@ export function createTelegramRuntimeProjectionStore(options) {
|
|
|
432
457
|
const TELEGRAM_DIAGNOSTICS_SNAPSHOT_COALESCE_MS = 100;
|
|
433
458
|
export function createTelegramRuntimeDiagnosticsSnapshotScheduler(deps) {
|
|
434
459
|
const setTimer = deps.setTimer ?? setTimeout;
|
|
435
|
-
const clearTimer = deps.clearTimer ??
|
|
460
|
+
const clearTimer = deps.clearTimer ??
|
|
461
|
+
((handle) => clearTimeout(handle));
|
|
436
462
|
let generation = 0;
|
|
437
463
|
let enabled = true;
|
|
438
464
|
let timer;
|
|
@@ -503,7 +529,9 @@ export function createTelegramRuntimeDiagnosticsSnapshotScheduler(deps) {
|
|
|
503
529
|
return;
|
|
504
530
|
let tracked;
|
|
505
531
|
tracked = Promise.resolve()
|
|
506
|
-
.then(() => publicationAuthority()
|
|
532
|
+
.then(() => publicationAuthority()
|
|
533
|
+
? deps.persistSnapshot(publicationAuthority)
|
|
534
|
+
: undefined)
|
|
507
535
|
.catch(recordError)
|
|
508
536
|
.finally(() => {
|
|
509
537
|
if (persistPromise !== tracked)
|
|
@@ -553,13 +581,7 @@ export function getTelegramStatusBarProcessingStatus(state) {
|
|
|
553
581
|
return undefined;
|
|
554
582
|
}
|
|
555
583
|
function getTelegramStatusBarLabel(state) {
|
|
556
|
-
|
|
557
|
-
if (!threadName)
|
|
558
|
-
return "telegram";
|
|
559
|
-
const genericLabels = new Set(["telegram", "leader", "follower"]);
|
|
560
|
-
if (genericLabels.has(threadName.toLowerCase()))
|
|
561
|
-
return "telegram";
|
|
562
|
-
return threadName;
|
|
584
|
+
return state.instanceThreadName?.trim() || "telegram";
|
|
563
585
|
}
|
|
564
586
|
export function buildTelegramStatusBarText(theme, state) {
|
|
565
587
|
const label = theme.fg("accent", getTelegramStatusBarLabel(state));
|
|
@@ -568,14 +590,15 @@ export function buildTelegramStatusBarText(theme, state) {
|
|
|
568
590
|
: "";
|
|
569
591
|
if (!state.hasBotToken)
|
|
570
592
|
return `${label} ${theme.fg("muted", "not configured")}${queued}`;
|
|
571
|
-
if (state.pollingStopReason === "persistent-conflict" &&
|
|
593
|
+
if (state.pollingStopReason === "persistent-conflict" &&
|
|
594
|
+
state.busRole !== "follower")
|
|
572
595
|
return `${label} ${theme.fg("error", "error")}`;
|
|
573
596
|
if (!state.paired)
|
|
574
597
|
return `${label} ${theme.fg("warning", "awaiting pairing")}${queued}`;
|
|
575
598
|
if (state.busLifecyclePhase === "electing")
|
|
576
599
|
return `${label} ${theme.fg("warning", "electing")}${queued}`;
|
|
577
600
|
if (!state.pollingActive && state.busRole !== "follower")
|
|
578
|
-
return `${
|
|
601
|
+
return `${label} ${theme.fg("dim", "disconnected")}${queued}`;
|
|
579
602
|
if (state.error) {
|
|
580
603
|
return `${label} ${theme.fg("error", "error")}${queued}`;
|
|
581
604
|
}
|
|
@@ -820,9 +843,7 @@ function buildTelegramPollingDiagnosticLines(polling) {
|
|
|
820
843
|
return [
|
|
821
844
|
`- phase: ${polling.phase}`,
|
|
822
845
|
...(polling.phaseStartedAtMs !== undefined
|
|
823
|
-
? [
|
|
824
|
-
`- phase started: ${new Date(polling.phaseStartedAtMs).toISOString()}`,
|
|
825
|
-
]
|
|
846
|
+
? [`- phase started: ${new Date(polling.phaseStartedAtMs).toISOString()}`]
|
|
826
847
|
: []),
|
|
827
848
|
...(polling.currentUpdateId !== undefined
|
|
828
849
|
? [`- current update id: ${polling.currentUpdateId}`]
|
|
@@ -889,7 +910,9 @@ function buildTelegramInboundWorkerDiagnosticLines(worker) {
|
|
|
889
910
|
? [`- blocked reason: ${worker.blockedReason}`]
|
|
890
911
|
: []),
|
|
891
912
|
...(worker.blockedInputCustody
|
|
892
|
-
? [
|
|
913
|
+
? [
|
|
914
|
+
`- blocked input custody: update=${worker.blockedInputCustody.updateId}, kind=${worker.blockedInputCustody.kind}`,
|
|
915
|
+
]
|
|
893
916
|
: []),
|
|
894
917
|
...(worker.lastCompletedUpdateId !== undefined
|
|
895
918
|
? [
|
|
@@ -903,10 +926,18 @@ function buildTelegramInboundWorkerDiagnosticLines(worker) {
|
|
|
903
926
|
: []),
|
|
904
927
|
];
|
|
905
928
|
}
|
|
929
|
+
function countTelegramQueueLanes(items) {
|
|
930
|
+
const counts = {
|
|
931
|
+
control: 0,
|
|
932
|
+
priority: 0,
|
|
933
|
+
default: 0,
|
|
934
|
+
};
|
|
935
|
+
for (const item of items)
|
|
936
|
+
counts[item.queueLane]++;
|
|
937
|
+
return counts;
|
|
938
|
+
}
|
|
906
939
|
function buildTelegramBridgeCompactStatusLines(state) {
|
|
907
|
-
const controlQueueCount = state.queuedItems
|
|
908
|
-
const priorityQueueCount = state.queuedItems.filter((item) => item.queueLane === "priority").length;
|
|
909
|
-
const defaultQueueCount = state.queuedItems.filter((item) => item.queueLane === "default").length;
|
|
940
|
+
const { control: controlQueueCount, priority: priorityQueueCount, default: defaultQueueCount, } = countTelegramQueueLanes(state.queuedItems);
|
|
910
941
|
const queueLine = `- queued turns: ${state.queuedItems.length}${state.queuedItems.length > 0
|
|
911
942
|
? ` (control=${controlQueueCount}, priority=${priorityQueueCount}, default=${defaultQueueCount})`
|
|
912
943
|
: ""}`;
|
|
@@ -966,9 +997,7 @@ export function buildTelegramBridgeStatusLines(state, options = {}) {
|
|
|
966
997
|
return buildTelegramBridgeCompactStatusLines(state);
|
|
967
998
|
}
|
|
968
999
|
function buildTelegramBridgeDiagnosticStatusLines(state) {
|
|
969
|
-
const controlQueueCount = state.queuedItems
|
|
970
|
-
const priorityQueueCount = state.queuedItems.filter((item) => item.queueLane === "priority").length;
|
|
971
|
-
const defaultQueueCount = state.queuedItems.filter((item) => item.queueLane === "default").length;
|
|
1000
|
+
const { control: controlQueueCount, priority: priorityQueueCount, default: defaultQueueCount, } = countTelegramQueueLanes(state.queuedItems);
|
|
972
1001
|
return [
|
|
973
1002
|
"connection:",
|
|
974
1003
|
`- bot: ${formatTelegramBridgeBotStatus(state)}`,
|
|
@@ -1024,12 +1053,6 @@ function buildTelegramBridgeDiagnosticStatusLines(state) {
|
|
|
1024
1053
|
...buildTelegramRuntimeEventLines(state.recentRuntimeEvents),
|
|
1025
1054
|
];
|
|
1026
1055
|
}
|
|
1027
|
-
function escapeHtml(text) {
|
|
1028
|
-
return text
|
|
1029
|
-
.replace(/&/g, "&")
|
|
1030
|
-
.replace(/</g, "<")
|
|
1031
|
-
.replace(/>/g, ">");
|
|
1032
|
-
}
|
|
1033
1056
|
function formatTokens(count) {
|
|
1034
1057
|
if (count < 1000)
|
|
1035
1058
|
return count.toString();
|
|
@@ -1150,3 +1173,9 @@ function buildStatusHtml(ctx, activeModel, bridgeStatus) {
|
|
|
1150
1173
|
}
|
|
1151
1174
|
return lines.join("\n");
|
|
1152
1175
|
}
|
|
1176
|
+
function escapeHtml(text) {
|
|
1177
|
+
return text
|
|
1178
|
+
.replace(/&/g, "&")
|
|
1179
|
+
.replace(/</g, "<")
|
|
1180
|
+
.replace(/>/g, ">");
|
|
1181
|
+
}
|
package/dist/lib/sync.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type TelegramTarget } from "./target.ts";
|
|
7
7
|
import * as ThreadReconciler from "./thread-reconciler.ts";
|
|
8
|
-
import { type TelegramWorkspaceAdmissionLedger } from "./workspace-admission.ts";
|
|
9
8
|
import { type TelegramOwnTopicProvisionResult, type TelegramTopicTargetStore, type TelegramWorkspaceDisplayBinding, type TelegramWorkspaceThreadBinding } from "./threads.ts";
|
|
9
|
+
import { type TelegramWorkspaceAdmissionLedger } from "./workspace-admission.ts";
|
|
10
10
|
export interface TelegramTopicLifecycleSyncUpdate<TMessage = unknown> {
|
|
11
11
|
kind: "created" | "closed" | "reopened";
|
|
12
12
|
target: TelegramTarget & {
|
|
@@ -144,7 +144,6 @@ export declare function recoverStaleTelegramTopicApiError<TSyncState extends Tel
|
|
|
144
144
|
export declare function ensureTelegramLeaderThreadBinding(deps: TelegramLeaderThreadSyncDeps): Promise<TelegramOwnTopicProvisionResult | undefined>;
|
|
145
145
|
export declare const TELEGRAM_SYNC_SLICES: readonly ["bot-identity", "bot-capabilities", "pairing", "allowed-user", "topic-capability", "topic-state", "target-bindings", "reservations", "transport-health"];
|
|
146
146
|
export type TelegramSyncSlice = (typeof TELEGRAM_SYNC_SLICES)[number];
|
|
147
|
-
export type TelegramSyncTrigger = "startup" | "reload" | "topic-lifecycle" | "stale-api-error" | "setup-change" | "pairing-change" | "follower-register" | "follower-prune" | "status-request" | "leader-health-tick" | "ordinary-message" | "ordinary-send";
|
|
148
147
|
export interface TelegramSyncSliceState {
|
|
149
148
|
status: "fresh" | "suspect" | "unknown";
|
|
150
149
|
updatedAtMs?: number;
|
|
@@ -174,7 +173,6 @@ export interface TelegramProvisioningActivityRuntime {
|
|
|
174
173
|
end(): void;
|
|
175
174
|
}
|
|
176
175
|
export declare function createTelegramProvisioningActivityRuntime(): TelegramProvisioningActivityRuntime;
|
|
177
|
-
export declare function shouldReconcileTelegramSync(trigger: TelegramSyncTrigger): boolean;
|
|
178
176
|
export declare function markTelegramSyncSliceSuspect(state: TelegramSyncState, slice: TelegramSyncSlice, input: {
|
|
179
177
|
reason: string;
|
|
180
178
|
nowMs: number;
|