@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/lib/logging.ts
CHANGED
|
@@ -5,21 +5,22 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
appendFileSync,
|
|
8
9
|
copyFileSync,
|
|
9
10
|
existsSync,
|
|
10
11
|
mkdirSync,
|
|
11
12
|
statSync,
|
|
12
13
|
writeFileSync,
|
|
13
|
-
appendFileSync,
|
|
14
14
|
} from "node:fs";
|
|
15
15
|
import { basename, dirname, join } from "node:path";
|
|
16
|
+
|
|
17
|
+
import { withTelegramFileTransaction } from "./locks.ts";
|
|
16
18
|
import {
|
|
17
19
|
resolveAgentDir,
|
|
20
|
+
resolveTelegramPreviousSharedRuntimeLogPath,
|
|
18
21
|
resolveTelegramProfileTempFilePath,
|
|
19
22
|
resolveTelegramRuntimeLogPath,
|
|
20
|
-
resolveTelegramPreviousSharedRuntimeLogPath,
|
|
21
23
|
} from "./paths.ts";
|
|
22
|
-
import { withTelegramFileTransaction } from "./locks.ts";
|
|
23
24
|
import * as Status from "./status.ts";
|
|
24
25
|
|
|
25
26
|
type TelegramLogPathInput = string | (() => string);
|
|
@@ -95,9 +96,13 @@ function safeJsonLine(value: unknown): string {
|
|
|
95
96
|
export function createTelegramRuntimeJsonlLog(
|
|
96
97
|
options: TelegramRuntimeJsonlLogOptions = {},
|
|
97
98
|
): TelegramRuntimeJsonlLog {
|
|
98
|
-
const shared = options.sharedProfiles
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const shared = options.sharedProfiles
|
|
100
|
+
? { ...options.sharedProfiles }
|
|
101
|
+
: undefined;
|
|
102
|
+
const pathSource = options.path,
|
|
103
|
+
previousSource = options.previousPath;
|
|
104
|
+
const canReset = options.canReset,
|
|
105
|
+
commitReset = options.commitReset;
|
|
101
106
|
const resolvePath = () =>
|
|
102
107
|
typeof pathSource === "function"
|
|
103
108
|
? pathSource()
|
|
@@ -105,7 +110,12 @@ export function createTelegramRuntimeJsonlLog(
|
|
|
105
110
|
const resolvePreviousPath = () => {
|
|
106
111
|
if (typeof previousSource === "function") return previousSource();
|
|
107
112
|
if (previousSource) return previousSource;
|
|
108
|
-
return shared
|
|
113
|
+
return shared
|
|
114
|
+
? join(
|
|
115
|
+
dirname(resolvePath()),
|
|
116
|
+
"logs",
|
|
117
|
+
`${basename(resolvePath(), ".jsonl")}._prev.jsonl`,
|
|
118
|
+
)
|
|
109
119
|
: resolvePath().replace(/\.jsonl$/u, "._prev.jsonl");
|
|
110
120
|
};
|
|
111
121
|
const maxBytes = options.maxBytes ?? DEFAULT_MAX_LOG_BYTES;
|
|
@@ -121,13 +131,25 @@ export function createTelegramRuntimeJsonlLog(
|
|
|
121
131
|
rotationCurrent?: () => boolean;
|
|
122
132
|
}[] = [];
|
|
123
133
|
const getProfile = () => shared?.getProfileName() ?? "default";
|
|
124
|
-
const transactionPath = (path: string) =>
|
|
125
|
-
|
|
134
|
+
const transactionPath = (path: string) =>
|
|
135
|
+
shared
|
|
136
|
+
? join(dirname(path), "runtime", `${basename(path)}.transaction`)
|
|
137
|
+
: `${path}.transaction`;
|
|
138
|
+
const captureRotation = (
|
|
139
|
+
path: string,
|
|
140
|
+
previousPath: string,
|
|
141
|
+
profile: string,
|
|
142
|
+
): (() => boolean) | undefined => {
|
|
126
143
|
if (!shared) return undefined;
|
|
127
144
|
const authority = shared.captureAuthority();
|
|
128
|
-
return () =>
|
|
145
|
+
return () =>
|
|
146
|
+
authority?.() === true &&
|
|
147
|
+
resolvePath() === path &&
|
|
148
|
+
resolvePreviousPath() === previousPath &&
|
|
149
|
+
getProfile() === profile;
|
|
129
150
|
};
|
|
130
|
-
const scopeStorageKey = (path: string, profile: string) =>
|
|
151
|
+
const scopeStorageKey = (path: string, profile: string) =>
|
|
152
|
+
shared ? JSON.stringify([path, profile]) : path;
|
|
131
153
|
|
|
132
154
|
const ensureParent = (path: string) => {
|
|
133
155
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
@@ -147,14 +169,15 @@ export function createTelegramRuntimeJsonlLog(
|
|
|
147
169
|
profile?: string,
|
|
148
170
|
isCurrent?: () => boolean,
|
|
149
171
|
): boolean => {
|
|
150
|
-
const line =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
172
|
+
const line =
|
|
173
|
+
safeJsonLine({
|
|
174
|
+
at: getNowMs(),
|
|
175
|
+
kind: "reset",
|
|
176
|
+
reason,
|
|
177
|
+
scope,
|
|
178
|
+
previousPath,
|
|
179
|
+
...(shared ? { profile } : {}),
|
|
180
|
+
}) + "\n";
|
|
158
181
|
if (shared && !isCurrent?.()) return false;
|
|
159
182
|
ensureParent(path);
|
|
160
183
|
preserveCurrentLog(path, previousPath);
|
|
@@ -177,11 +200,29 @@ export function createTelegramRuntimeJsonlLog(
|
|
|
177
200
|
const commit = () => {
|
|
178
201
|
if (shared && !isCurrent?.()) return;
|
|
179
202
|
if (shared) {
|
|
180
|
-
const line =
|
|
203
|
+
const line =
|
|
204
|
+
safeJsonLine({
|
|
205
|
+
at: getNowMs(),
|
|
206
|
+
kind: "reset",
|
|
207
|
+
reason,
|
|
208
|
+
scope,
|
|
209
|
+
profile,
|
|
210
|
+
}) + "\n";
|
|
181
211
|
if (!isCurrent?.()) return;
|
|
182
212
|
ensureParent(path);
|
|
183
|
-
if (
|
|
184
|
-
|
|
213
|
+
if (
|
|
214
|
+
existsSync(path) &&
|
|
215
|
+
statSync(path).size + Buffer.byteLength(line) > maxBytes &&
|
|
216
|
+
!writeResetLocked(
|
|
217
|
+
path,
|
|
218
|
+
previousPath,
|
|
219
|
+
"max-bytes",
|
|
220
|
+
{ maxBytes },
|
|
221
|
+
profile,
|
|
222
|
+
isCurrent,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
return;
|
|
185
226
|
if (!isCurrent?.()) return;
|
|
186
227
|
appendFileSync(path, line, { mode: 0o600 });
|
|
187
228
|
didReset = true;
|
|
@@ -197,10 +238,22 @@ export function createTelegramRuntimeJsonlLog(
|
|
|
197
238
|
};
|
|
198
239
|
|
|
199
240
|
const appendEvent = (event: TelegramRuntimeJsonlEvent) => {
|
|
200
|
-
const path = resolvePath(),
|
|
241
|
+
const path = resolvePath(),
|
|
242
|
+
previousPath = resolvePreviousPath(),
|
|
243
|
+
profile = getProfile();
|
|
201
244
|
const rotationCurrent = captureRotation(path, previousPath, profile);
|
|
202
|
-
const line =
|
|
203
|
-
|
|
245
|
+
const line =
|
|
246
|
+
safeJsonLine({
|
|
247
|
+
kind: "event",
|
|
248
|
+
...event,
|
|
249
|
+
...(shared ? { profile } : {}),
|
|
250
|
+
}) + "\n";
|
|
251
|
+
queuedAppends.push({
|
|
252
|
+
path,
|
|
253
|
+
previousPath,
|
|
254
|
+
line,
|
|
255
|
+
...(shared ? { profile, rotationCurrent } : {}),
|
|
256
|
+
});
|
|
204
257
|
if (appendScheduled) return;
|
|
205
258
|
appendScheduled = true;
|
|
206
259
|
pending = pending
|
|
@@ -234,7 +287,9 @@ export function createTelegramRuntimeJsonlLog(
|
|
|
234
287
|
chunk = "";
|
|
235
288
|
chunkBytes = 0;
|
|
236
289
|
};
|
|
237
|
-
const rotate = (
|
|
290
|
+
const rotate = (
|
|
291
|
+
entry: (typeof queuedAppends)[number],
|
|
292
|
+
): boolean => {
|
|
238
293
|
if (canReset && !canReset()) return false;
|
|
239
294
|
if (shared && !entry.rotationCurrent?.()) return false;
|
|
240
295
|
let rotated = false;
|
|
@@ -280,20 +335,27 @@ export function createTelegramRuntimeJsonlLog(
|
|
|
280
335
|
return {
|
|
281
336
|
getPath: resolvePath,
|
|
282
337
|
reset(reason, scope) {
|
|
283
|
-
const path = resolvePath(),
|
|
338
|
+
const path = resolvePath(),
|
|
339
|
+
profile = getProfile();
|
|
284
340
|
try {
|
|
285
341
|
if (writeReset(reason, scope, path, profile)) {
|
|
286
|
-
scopeKeys.set(
|
|
342
|
+
scopeKeys.set(
|
|
343
|
+
scopeStorageKey(path, profile),
|
|
344
|
+
scope ? safeJsonLine(scope) : undefined,
|
|
345
|
+
);
|
|
287
346
|
}
|
|
288
347
|
} catch {
|
|
289
348
|
// Diagnostics must never break Telegram runtime behavior.
|
|
290
349
|
}
|
|
291
350
|
},
|
|
292
351
|
resetIfScopeChanged(nextScopeKey, reason, scope) {
|
|
293
|
-
const path = resolvePath(),
|
|
352
|
+
const path = resolvePath(),
|
|
353
|
+
profile = getProfile(),
|
|
354
|
+
key = scopeStorageKey(path, profile);
|
|
294
355
|
if (scopeKeys.get(key) === nextScopeKey) return;
|
|
295
356
|
try {
|
|
296
|
-
if (writeReset(reason, scope, path, profile))
|
|
357
|
+
if (writeReset(reason, scope, path, profile))
|
|
358
|
+
scopeKeys.set(key, nextScopeKey);
|
|
297
359
|
} catch {
|
|
298
360
|
// Diagnostics must never break Telegram runtime behavior.
|
|
299
361
|
}
|
|
@@ -312,7 +374,9 @@ interface TelegramRuntimeDiagnosticsStatusPorts<TContext> {
|
|
|
312
374
|
instanceId: string;
|
|
313
375
|
updateStatus(ctx: TContext, error?: string): void;
|
|
314
376
|
getStatusState(): Status.TelegramBridgeStatusLineState;
|
|
315
|
-
persistSnapshot(
|
|
377
|
+
persistSnapshot(
|
|
378
|
+
snapshot: ReturnType<typeof Status.createTelegramStatusSnapshot>,
|
|
379
|
+
): Promise<void>;
|
|
316
380
|
session?: {
|
|
317
381
|
get(): TContext | undefined;
|
|
318
382
|
getGeneration(): number;
|
|
@@ -342,12 +406,17 @@ interface TelegramRuntimeDiagnosticsRuntime<TContext> {
|
|
|
342
406
|
scheduleSnapshotPersist(): void;
|
|
343
407
|
}
|
|
344
408
|
|
|
345
|
-
export function createTelegramRuntimeDiagnosticsRuntime<
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
409
|
+
export function createTelegramRuntimeDiagnosticsRuntime<TContext>(
|
|
410
|
+
options: {
|
|
411
|
+
sharedFile?: boolean;
|
|
412
|
+
snapshotTimer?: Pick<
|
|
413
|
+
Parameters<
|
|
414
|
+
typeof Status.createTelegramRuntimeDiagnosticsSnapshotScheduler
|
|
415
|
+
>[0],
|
|
416
|
+
"setTimer" | "clearTimer"
|
|
417
|
+
>;
|
|
418
|
+
} = {},
|
|
419
|
+
): TelegramRuntimeDiagnosticsRuntime<TContext> {
|
|
351
420
|
const sharedFile = options.sharedFile === true;
|
|
352
421
|
let getBotToken = (): string | undefined => undefined;
|
|
353
422
|
let getProfileName = (): string | undefined => undefined;
|
|
@@ -359,10 +428,22 @@ export function createTelegramRuntimeDiagnosticsRuntime<
|
|
|
359
428
|
getBotToken: () => getBotToken(),
|
|
360
429
|
});
|
|
361
430
|
const jsonl = createTelegramRuntimeJsonlLog({
|
|
362
|
-
path: () =>
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
431
|
+
path: () =>
|
|
432
|
+
sharedFile
|
|
433
|
+
? resolveTelegramRuntimeLogPath()
|
|
434
|
+
: getTelegramRuntimeLogPath(undefined, getProfileName()),
|
|
435
|
+
previousPath: () =>
|
|
436
|
+
sharedFile
|
|
437
|
+
? resolveTelegramPreviousSharedRuntimeLogPath()
|
|
438
|
+
: getTelegramPreviousRuntimeLogPath(undefined, getProfileName()),
|
|
439
|
+
...(sharedFile
|
|
440
|
+
? {
|
|
441
|
+
sharedProfiles: {
|
|
442
|
+
getProfileName: () => getProfileName(),
|
|
443
|
+
captureAuthority: () => captureAuthority(),
|
|
444
|
+
},
|
|
445
|
+
}
|
|
446
|
+
: {}),
|
|
366
447
|
canReset: () => canReset(),
|
|
367
448
|
commitReset: (commit) => commitReset(commit),
|
|
368
449
|
});
|
|
@@ -380,25 +461,33 @@ export function createTelegramRuntimeDiagnosticsRuntime<
|
|
|
380
461
|
const ports = statusPorts;
|
|
381
462
|
if (!ports) return undefined;
|
|
382
463
|
if (!ports.session) return () => statusPorts === ports;
|
|
383
|
-
const session = ports.session,
|
|
384
|
-
|
|
464
|
+
const session = ports.session,
|
|
465
|
+
ctx = session.get(),
|
|
466
|
+
generation = session.getGeneration();
|
|
467
|
+
if (ctx === undefined || !session.isCurrent(ctx, generation))
|
|
468
|
+
return undefined;
|
|
385
469
|
return () => statusPorts === ports && session.isCurrent(ctx, generation);
|
|
386
470
|
};
|
|
387
|
-
const requestSnapshotPersist =
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
471
|
+
const requestSnapshotPersist =
|
|
472
|
+
Status.createTelegramRuntimeDiagnosticsSnapshotScheduler({
|
|
473
|
+
...options.snapshotTimer,
|
|
474
|
+
captureScope: captureSnapshotScope,
|
|
475
|
+
async persistSnapshot(isCurrent) {
|
|
476
|
+
const ports = statusPorts;
|
|
477
|
+
// Projection may acquire journal guards or recover a snapshot: fence before reading it.
|
|
478
|
+
if (!ports || !isCurrent()) return;
|
|
479
|
+
const snapshot = Status.createTelegramStatusSnapshot(
|
|
480
|
+
ports.getStatusState(),
|
|
481
|
+
);
|
|
482
|
+
if (!isCurrent()) return;
|
|
483
|
+
await ports.persistSnapshot(snapshot);
|
|
484
|
+
},
|
|
485
|
+
recordError(error) {
|
|
486
|
+
events.record("telegram", error, {
|
|
487
|
+
phase: "runtime-diagnostics-snapshot-persist",
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
});
|
|
402
491
|
const updateRuntimeLogScope = function (reason: string): void {
|
|
403
492
|
if (!statusPorts) return;
|
|
404
493
|
const scope = Status.createTelegramRuntimeLogScope({
|
package/lib/media.ts
CHANGED
|
@@ -428,15 +428,6 @@ function appendTelegramReplyContext(
|
|
|
428
428
|
return text ? `${text}\n\n${replyContext}` : `_\n\n${replyContext}`;
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
-
export function extractTelegramMessagePromptText(
|
|
432
|
-
message: TelegramMediaMessage,
|
|
433
|
-
): string {
|
|
434
|
-
return appendTelegramReplyContext(
|
|
435
|
-
extractTelegramMessageText(message),
|
|
436
|
-
buildTelegramReplyContextBlock(message),
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
431
|
export function extractTelegramMessagesText(
|
|
441
432
|
messages: TelegramMediaMessage[],
|
|
442
433
|
): string {
|
|
@@ -601,35 +592,37 @@ export function queueTelegramMediaGroupMessage<
|
|
|
601
592
|
return true;
|
|
602
593
|
}
|
|
603
594
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
595
|
+
/** Timer and dispatch lifecycle shared by delayed media-group and text-group coalescing. */
|
|
596
|
+
export interface TelegramPendingGroupState<TMessage, TContext> {
|
|
597
|
+
messages: TMessage[];
|
|
598
|
+
context?: TContext;
|
|
599
|
+
flushTimer?: ReturnType<typeof setTimeout>;
|
|
600
|
+
dispatching?: boolean;
|
|
601
|
+
suspended?: boolean;
|
|
602
|
+
reschedule?: () => void;
|
|
603
|
+
dispatchNow?: () => Promise<void>;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export interface TelegramPendingGroupLifecycle<TContext> {
|
|
607
|
+
flushMessage: (messageId: number) => Promise<boolean>;
|
|
608
|
+
suspend: () => void;
|
|
609
|
+
resume: (context: TContext) => void;
|
|
610
|
+
clear: () => void;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
export function createTelegramPendingGroupLifecycle<
|
|
614
|
+
TMessage extends { message_id: number },
|
|
615
|
+
TContext,
|
|
607
616
|
>(
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const debounceMs = options.debounceMs ?? TELEGRAM_MEDIA_GROUP_DEBOUNCE_MS;
|
|
612
|
-
const setTimer =
|
|
613
|
-
options.setTimer ??
|
|
614
|
-
((callback: () => void, ms: number): ReturnType<typeof setTimeout> =>
|
|
615
|
-
setTimeout(callback, ms));
|
|
616
|
-
const clearTimer = options.clearTimer ?? clearTimeout;
|
|
617
|
+
groups: Map<string, TelegramPendingGroupState<TMessage, TContext>>,
|
|
618
|
+
clearTimer: (timer: ReturnType<typeof setTimeout>) => void,
|
|
619
|
+
): TelegramPendingGroupLifecycle<TContext> {
|
|
617
620
|
return {
|
|
618
|
-
queueMessage: ({ message, context, dispatchMessages }) =>
|
|
619
|
-
queueTelegramMediaGroupMessage({
|
|
620
|
-
message,
|
|
621
|
-
context,
|
|
622
|
-
groups,
|
|
623
|
-
debounceMs,
|
|
624
|
-
setTimer,
|
|
625
|
-
clearTimer,
|
|
626
|
-
dispatchMessages,
|
|
627
|
-
}),
|
|
628
|
-
removeMessages: (messageIds) =>
|
|
629
|
-
removePendingTelegramMediaGroupMessages(groups, messageIds, clearTimer),
|
|
630
621
|
async flushMessage(messageId) {
|
|
631
622
|
for (const state of groups.values()) {
|
|
632
|
-
if (
|
|
623
|
+
if (
|
|
624
|
+
!state.messages.some((message) => message.message_id === messageId)
|
|
625
|
+
) {
|
|
633
626
|
continue;
|
|
634
627
|
}
|
|
635
628
|
if (state.flushTimer) clearTimer(state.flushTimer);
|
|
@@ -668,6 +661,36 @@ export function createTelegramMediaGroupController<
|
|
|
668
661
|
};
|
|
669
662
|
}
|
|
670
663
|
|
|
664
|
+
export function createTelegramMediaGroupController<
|
|
665
|
+
TMessage extends TelegramMediaGroupMessage,
|
|
666
|
+
TContext = unknown,
|
|
667
|
+
>(
|
|
668
|
+
options: TelegramMediaGroupControllerOptions = {},
|
|
669
|
+
): TelegramMediaGroupController<TMessage, TContext> {
|
|
670
|
+
const groups = new Map<string, TelegramMediaGroupState<TMessage, TContext>>();
|
|
671
|
+
const debounceMs = options.debounceMs ?? TELEGRAM_MEDIA_GROUP_DEBOUNCE_MS;
|
|
672
|
+
const setTimer =
|
|
673
|
+
options.setTimer ??
|
|
674
|
+
((callback: () => void, ms: number): ReturnType<typeof setTimeout> =>
|
|
675
|
+
setTimeout(callback, ms));
|
|
676
|
+
const clearTimer = options.clearTimer ?? clearTimeout;
|
|
677
|
+
return {
|
|
678
|
+
queueMessage: ({ message, context, dispatchMessages }) =>
|
|
679
|
+
queueTelegramMediaGroupMessage({
|
|
680
|
+
message,
|
|
681
|
+
context,
|
|
682
|
+
groups,
|
|
683
|
+
debounceMs,
|
|
684
|
+
setTimer,
|
|
685
|
+
clearTimer,
|
|
686
|
+
dispatchMessages,
|
|
687
|
+
}),
|
|
688
|
+
removeMessages: (messageIds) =>
|
|
689
|
+
removePendingTelegramMediaGroupMessages(groups, messageIds, clearTimer),
|
|
690
|
+
...createTelegramPendingGroupLifecycle(groups, clearTimer),
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
671
694
|
export function createTelegramMediaGroupDispatchRuntime<
|
|
672
695
|
TMessage extends TelegramMediaGroupMessage,
|
|
673
696
|
TContext,
|
|
@@ -693,7 +716,8 @@ export function createTelegramMediaGroupDispatchRuntime<
|
|
|
693
716
|
};
|
|
694
717
|
}
|
|
695
718
|
|
|
696
|
-
|
|
719
|
+
/** Pure `[title]` bullet section appended after a blank line; empty items leave text unchanged. */
|
|
720
|
+
export function appendTelegramListSection(
|
|
697
721
|
text: string,
|
|
698
722
|
title: string,
|
|
699
723
|
items: string[],
|
|
@@ -703,7 +727,7 @@ function appendTelegramListSection(
|
|
|
703
727
|
return `${prefix}[${title}]\n${items.map((item) => `- ${item}`).join("\n")}`;
|
|
704
728
|
}
|
|
705
729
|
|
|
706
|
-
function appendTelegramAttachmentSection(
|
|
730
|
+
export function appendTelegramAttachmentSection(
|
|
707
731
|
text: string,
|
|
708
732
|
files: Pick<DownloadedTelegramFile, "path">[],
|
|
709
733
|
): string {
|
|
@@ -875,7 +899,9 @@ export function collectTelegramFileInfos(
|
|
|
875
899
|
files.push({
|
|
876
900
|
file_id: message.document.file_id,
|
|
877
901
|
fileName,
|
|
878
|
-
...(message.document.file_name
|
|
902
|
+
...(message.document.file_name
|
|
903
|
+
? { userFileName: message.document.file_name }
|
|
904
|
+
: {}),
|
|
879
905
|
mimeType: message.document.mime_type,
|
|
880
906
|
kind: "document",
|
|
881
907
|
isImage: isImageMimeType(message.document.mime_type),
|
|
@@ -891,7 +917,9 @@ export function collectTelegramFileInfos(
|
|
|
891
917
|
files.push({
|
|
892
918
|
file_id: message.video.file_id,
|
|
893
919
|
fileName,
|
|
894
|
-
...(message.video.file_name
|
|
920
|
+
...(message.video.file_name
|
|
921
|
+
? { userFileName: message.video.file_name }
|
|
922
|
+
: {}),
|
|
895
923
|
mimeType: message.video.mime_type,
|
|
896
924
|
kind: "video",
|
|
897
925
|
isImage: false,
|
|
@@ -908,7 +936,9 @@ export function collectTelegramFileInfos(
|
|
|
908
936
|
files.push({
|
|
909
937
|
file_id: message.audio.file_id,
|
|
910
938
|
fileName,
|
|
911
|
-
...(message.audio.file_name
|
|
939
|
+
...(message.audio.file_name
|
|
940
|
+
? { userFileName: message.audio.file_name }
|
|
941
|
+
: {}),
|
|
912
942
|
mimeType: message.audio.mime_type,
|
|
913
943
|
kind: "audio",
|
|
914
944
|
isImage: false,
|
|
@@ -938,7 +968,9 @@ export function collectTelegramFileInfos(
|
|
|
938
968
|
files.push({
|
|
939
969
|
file_id: message.animation.file_id,
|
|
940
970
|
fileName,
|
|
941
|
-
...(message.animation.file_name
|
|
971
|
+
...(message.animation.file_name
|
|
972
|
+
? { userFileName: message.animation.file_name }
|
|
973
|
+
: {}),
|
|
942
974
|
mimeType: message.animation.mime_type,
|
|
943
975
|
kind: "animation",
|
|
944
976
|
isImage: false,
|
|
@@ -952,16 +984,23 @@ export function collectTelegramFileInfos(
|
|
|
952
984
|
files.push({
|
|
953
985
|
file_id: message.sticker.file_id,
|
|
954
986
|
fileName: `sticker-${message.message_id}${extension}`,
|
|
955
|
-
mimeType: video
|
|
987
|
+
mimeType: video
|
|
988
|
+
? "video/webm"
|
|
989
|
+
: animated
|
|
990
|
+
? "application/x-tgsticker"
|
|
991
|
+
: "image/webp",
|
|
956
992
|
kind: "sticker",
|
|
957
993
|
isImage: !video && !animated,
|
|
958
994
|
});
|
|
959
995
|
}
|
|
960
996
|
for (const file of files.slice(firstOfMessage)) {
|
|
961
|
-
file.source = {
|
|
997
|
+
file.source = {
|
|
998
|
+
kind: file.kind,
|
|
999
|
+
messageId: message.message_id,
|
|
962
1000
|
...(file.index !== undefined ? { index: file.index } : {}),
|
|
963
1001
|
...(file.userFileName ? { userFileName: file.userFileName } : {}),
|
|
964
|
-
...(message.chat ? { chat: message.chat } : {})
|
|
1002
|
+
...(message.chat ? { chat: message.chat } : {}),
|
|
1003
|
+
};
|
|
965
1004
|
}
|
|
966
1005
|
}
|
|
967
1006
|
const seenFileIds = new Set<string>();
|