@llblab/pi-kit 0.27.6 → 0.28.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/BACKLOG.md +2 -5
- package/CHANGELOG.md +5 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-telegram/AGENTS.md +10 -8
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -9
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
- package/node_modules/@llblab/pi-telegram/LICENSE +21 -0
- package/node_modules/@llblab/pi-telegram/README.md +15 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +5 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +5 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +6 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +4 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +54 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +206 -139
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +21 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +181 -25
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +4 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +28 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +52 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +78 -246
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +1 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +23 -26
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +0 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +15 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +0 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/config.js +12 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +2 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +255 -61
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -19
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +177 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +1115 -249
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +100 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +380 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +30 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +129 -72
- package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +28 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/media.js +26 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +0 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +8 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +0 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +18 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +4 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +12 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +1 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +1 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +4 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +0 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +50 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +155 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +0 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +3 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +3 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +69 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +29 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +104 -33
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +73 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2206 -307
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +44 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +149 -27
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +5 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +11 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +34 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +18 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +32 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +191 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +291 -59
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +1647 -369
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +184 -28
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +1032 -114
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.d.ts +12 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +21 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +25 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +95 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.d.ts +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +103 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +43 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +148 -52
- package/node_modules/@llblab/pi-telegram/dist/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/activity.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +152 -34
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/delivery.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +125 -19
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -1
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +22 -8
- package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +5 -9
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +6 -0
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +243 -224
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +178 -31
- package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +30 -8
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +110 -334
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +29 -29
- package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +15 -15
- package/node_modules/@llblab/pi-telegram/lib/config.ts +12 -17
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +2 -8
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +257 -72
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +0 -22
- package/node_modules/@llblab/pi-telegram/lib/inbound.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +1182 -326
- package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +402 -23
- package/node_modules/@llblab/pi-telegram/lib/logging.ts +151 -101
- package/node_modules/@llblab/pi-telegram/lib/media.ts +51 -9
- package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +8 -8
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +18 -18
- package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +11 -0
- package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/menu.ts +5 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +1 -10
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +5 -24
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +177 -17
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +3 -3
- package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +67 -9
- package/node_modules/@llblab/pi-telegram/lib/recovery.ts +104 -44
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +1955 -375
- package/node_modules/@llblab/pi-telegram/lib/sections.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/status.ts +156 -36
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +48 -33
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +24 -21
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +267 -9
- package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +3 -1
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1697 -488
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1072 -151
- package/node_modules/@llblab/pi-telegram/lib/wire.ts +28 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +97 -47
- package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +147 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +188 -89
- package/node_modules/@llblab/pi-telegram/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/scripts/audit-exports.mjs +100 -0
- package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +80 -43
- package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/package.json +2 -2
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Owns bounded JSONL runtime evidence files, previous-log preservation, and profile-aware log paths without becoming routing state
|
|
5
5
|
*/
|
|
6
6
|
import { copyFileSync, existsSync, mkdirSync, statSync, writeFileSync, appendFileSync, } from "node:fs";
|
|
7
|
-
import { dirname } from "node:path";
|
|
8
|
-
import { resolveAgentDir, resolveTelegramProfileTempFilePath, } from "./paths.js";
|
|
7
|
+
import { basename, dirname, join } from "node:path";
|
|
8
|
+
import { resolveAgentDir, resolveTelegramProfileTempFilePath, resolveTelegramRuntimeLogPath, resolveTelegramPreviousSharedRuntimeLogPath, } from "./paths.js";
|
|
9
9
|
import { withTelegramFileTransaction } from "./locks.js";
|
|
10
10
|
import * as Status from "./status.js";
|
|
11
11
|
const DEFAULT_MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
@@ -27,15 +27,19 @@ function safeJsonLine(value) {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
const shared = options.sharedProfiles ? { ...options.sharedProfiles } : undefined;
|
|
31
|
+
const pathSource = options.path, previousSource = options.previousPath;
|
|
32
|
+
const canReset = options.canReset, commitReset = options.commitReset;
|
|
33
|
+
const resolvePath = () => typeof pathSource === "function"
|
|
34
|
+
? pathSource()
|
|
35
|
+
: (pathSource ?? getTelegramRuntimeLogPath());
|
|
33
36
|
const resolvePreviousPath = () => {
|
|
34
|
-
if (typeof
|
|
35
|
-
return
|
|
36
|
-
if (
|
|
37
|
-
return
|
|
38
|
-
return resolvePath()
|
|
37
|
+
if (typeof previousSource === "function")
|
|
38
|
+
return previousSource();
|
|
39
|
+
if (previousSource)
|
|
40
|
+
return previousSource;
|
|
41
|
+
return shared ? join(dirname(resolvePath()), "logs", `${basename(resolvePath(), ".jsonl")}._prev.jsonl`)
|
|
42
|
+
: resolvePath().replace(/\.jsonl$/u, "._prev.jsonl");
|
|
39
43
|
};
|
|
40
44
|
const maxBytes = options.maxBytes ?? DEFAULT_MAX_LOG_BYTES;
|
|
41
45
|
const getNowMs = options.getNowMs ?? Date.now;
|
|
@@ -43,6 +47,15 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
43
47
|
let pending = Promise.resolve();
|
|
44
48
|
let appendScheduled = false;
|
|
45
49
|
let queuedAppends = [];
|
|
50
|
+
const getProfile = () => shared?.getProfileName() ?? "default";
|
|
51
|
+
const transactionPath = (path) => shared ? join(dirname(path), "runtime", `${basename(path)}.transaction`) : `${path}.transaction`;
|
|
52
|
+
const captureRotation = (path, previousPath, profile) => {
|
|
53
|
+
if (!shared)
|
|
54
|
+
return undefined;
|
|
55
|
+
const authority = shared.captureAuthority();
|
|
56
|
+
return () => authority?.() === true && resolvePath() === path && resolvePreviousPath() === previousPath && getProfile() === profile;
|
|
57
|
+
};
|
|
58
|
+
const scopeStorageKey = (path, profile) => shared ? JSON.stringify([path, profile]) : path;
|
|
46
59
|
const ensureParent = (path) => {
|
|
47
60
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
48
61
|
};
|
|
@@ -52,42 +65,64 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
52
65
|
mkdirSync(dirname(previousPath), { recursive: true, mode: 0o700 });
|
|
53
66
|
copyFileSync(path, previousPath);
|
|
54
67
|
};
|
|
55
|
-
const writeResetLocked = (path, previousPath, reason, scope) => {
|
|
56
|
-
|
|
57
|
-
preserveCurrentLog(path, previousPath);
|
|
58
|
-
writeFileSync(path, safeJsonLine({
|
|
68
|
+
const writeResetLocked = (path, previousPath, reason, scope, profile, isCurrent) => {
|
|
69
|
+
const line = safeJsonLine({
|
|
59
70
|
at: getNowMs(),
|
|
60
71
|
kind: "reset",
|
|
61
72
|
reason,
|
|
62
73
|
scope,
|
|
63
74
|
previousPath,
|
|
64
|
-
|
|
75
|
+
...(shared ? { profile } : {}),
|
|
76
|
+
}) + "\n";
|
|
77
|
+
if (shared && !isCurrent?.())
|
|
78
|
+
return false;
|
|
79
|
+
ensureParent(path);
|
|
80
|
+
preserveCurrentLog(path, previousPath);
|
|
81
|
+
if (shared && !isCurrent?.())
|
|
82
|
+
return false;
|
|
83
|
+
writeFileSync(path, line, { mode: 0o600 });
|
|
84
|
+
return true;
|
|
65
85
|
};
|
|
66
|
-
const writeReset = (reason, scope) => {
|
|
67
|
-
|
|
86
|
+
const writeReset = (reason, scope, path, profile) => {
|
|
87
|
+
const previousPath = resolvePreviousPath();
|
|
88
|
+
const isCurrent = captureRotation(path, previousPath, profile);
|
|
89
|
+
if (canReset && !canReset())
|
|
68
90
|
return false;
|
|
69
|
-
const path = resolvePath();
|
|
70
91
|
let didReset = false;
|
|
71
|
-
withTelegramFileTransaction(
|
|
92
|
+
withTelegramFileTransaction(transactionPath(path), () => {
|
|
72
93
|
const commit = () => {
|
|
73
|
-
|
|
74
|
-
|
|
94
|
+
if (shared && !isCurrent?.())
|
|
95
|
+
return;
|
|
96
|
+
if (shared) {
|
|
97
|
+
const line = safeJsonLine({ at: getNowMs(), kind: "reset", reason, scope, profile }) + "\n";
|
|
98
|
+
if (!isCurrent?.())
|
|
99
|
+
return;
|
|
100
|
+
ensureParent(path);
|
|
101
|
+
if (existsSync(path) && statSync(path).size + Buffer.byteLength(line) > maxBytes &&
|
|
102
|
+
!writeResetLocked(path, previousPath, "max-bytes", { maxBytes }, profile, isCurrent))
|
|
103
|
+
return;
|
|
104
|
+
if (!isCurrent?.())
|
|
105
|
+
return;
|
|
106
|
+
appendFileSync(path, line, { mode: 0o600 });
|
|
107
|
+
didReset = true;
|
|
108
|
+
}
|
|
109
|
+
else
|
|
110
|
+
didReset = writeResetLocked(path, previousPath, reason, scope);
|
|
75
111
|
};
|
|
76
|
-
if (
|
|
77
|
-
|
|
112
|
+
if (commitReset) {
|
|
113
|
+
commitReset(commit);
|
|
78
114
|
}
|
|
79
115
|
else {
|
|
80
116
|
commit();
|
|
81
117
|
}
|
|
82
118
|
});
|
|
83
|
-
return didReset;
|
|
119
|
+
return didReset && (!shared || isCurrent?.() === true);
|
|
84
120
|
};
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
121
|
+
const appendEvent = (event) => {
|
|
122
|
+
const path = resolvePath(), previousPath = resolvePreviousPath(), profile = getProfile();
|
|
123
|
+
const rotationCurrent = captureRotation(path, previousPath, profile);
|
|
124
|
+
const line = safeJsonLine({ kind: "event", ...event, ...(shared ? { profile } : {}) }) + "\n";
|
|
125
|
+
queuedAppends.push({ path, previousPath, line, ...(shared ? { profile, rotationCurrent } : {}) });
|
|
91
126
|
if (appendScheduled)
|
|
92
127
|
return;
|
|
93
128
|
appendScheduled = true;
|
|
@@ -101,14 +136,14 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
101
136
|
const key = `${entry.path}\u0000${entry.previousPath}`;
|
|
102
137
|
const group = groups.get(key);
|
|
103
138
|
if (group)
|
|
104
|
-
group.
|
|
139
|
+
group.entries.push(entry);
|
|
105
140
|
else
|
|
106
|
-
groups.set(key, { ...entry,
|
|
141
|
+
groups.set(key, { ...entry, entries: [entry] });
|
|
107
142
|
}
|
|
108
143
|
for (const group of groups.values()) {
|
|
109
144
|
try {
|
|
110
145
|
ensureParent(group.path);
|
|
111
|
-
withTelegramFileTransaction(
|
|
146
|
+
withTelegramFileTransaction(transactionPath(group.path), () => {
|
|
112
147
|
let currentSize = existsSync(group.path)
|
|
113
148
|
? statSync(group.path).size
|
|
114
149
|
: 0;
|
|
@@ -122,28 +157,32 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
122
157
|
chunk = "";
|
|
123
158
|
chunkBytes = 0;
|
|
124
159
|
};
|
|
125
|
-
const rotate = () => {
|
|
126
|
-
if (
|
|
160
|
+
const rotate = (entry) => {
|
|
161
|
+
if (canReset && !canReset())
|
|
162
|
+
return false;
|
|
163
|
+
if (shared && !entry.rotationCurrent?.())
|
|
127
164
|
return false;
|
|
128
165
|
let rotated = false;
|
|
129
166
|
const commit = () => {
|
|
130
|
-
|
|
131
|
-
|
|
167
|
+
if (shared && !entry.rotationCurrent?.())
|
|
168
|
+
return;
|
|
169
|
+
rotated = writeResetLocked(group.path, group.previousPath, "max-bytes", { maxBytes }, entry.profile, entry.rotationCurrent);
|
|
132
170
|
};
|
|
133
|
-
if (
|
|
134
|
-
|
|
171
|
+
if (commitReset)
|
|
172
|
+
commitReset(commit);
|
|
135
173
|
else
|
|
136
174
|
commit();
|
|
137
175
|
if (rotated)
|
|
138
176
|
currentSize = statSync(group.path).size;
|
|
139
177
|
return rotated;
|
|
140
178
|
};
|
|
141
|
-
for (const
|
|
179
|
+
for (const entry of group.entries) {
|
|
180
|
+
const line = entry.line;
|
|
142
181
|
const lineBytes = Buffer.byteLength(line);
|
|
143
182
|
if (currentSize + chunkBytes > 0 &&
|
|
144
183
|
currentSize + chunkBytes + lineBytes > maxBytes) {
|
|
145
184
|
flushChunk();
|
|
146
|
-
rotate();
|
|
185
|
+
rotate(entry);
|
|
147
186
|
}
|
|
148
187
|
chunk += line;
|
|
149
188
|
chunkBytes += lineBytes;
|
|
@@ -161,10 +200,10 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
161
200
|
return {
|
|
162
201
|
getPath: resolvePath,
|
|
163
202
|
reset(reason, scope) {
|
|
164
|
-
const path = resolvePath();
|
|
203
|
+
const path = resolvePath(), profile = getProfile();
|
|
165
204
|
try {
|
|
166
|
-
if (writeReset(reason, scope)) {
|
|
167
|
-
scopeKeys.set(path, scope ? safeJsonLine(scope) : undefined);
|
|
205
|
+
if (writeReset(reason, scope, path, profile)) {
|
|
206
|
+
scopeKeys.set(scopeStorageKey(path, profile), scope ? safeJsonLine(scope) : undefined);
|
|
168
207
|
}
|
|
169
208
|
}
|
|
170
209
|
catch {
|
|
@@ -172,12 +211,12 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
172
211
|
}
|
|
173
212
|
},
|
|
174
213
|
resetIfScopeChanged(nextScopeKey, reason, scope) {
|
|
175
|
-
const path = resolvePath();
|
|
176
|
-
if (scopeKeys.get(
|
|
214
|
+
const path = resolvePath(), profile = getProfile(), key = scopeStorageKey(path, profile);
|
|
215
|
+
if (scopeKeys.get(key) === nextScopeKey)
|
|
177
216
|
return;
|
|
178
217
|
try {
|
|
179
|
-
if (writeReset(reason, scope))
|
|
180
|
-
scopeKeys.set(
|
|
218
|
+
if (writeReset(reason, scope, path, profile))
|
|
219
|
+
scopeKeys.set(key, nextScopeKey);
|
|
181
220
|
}
|
|
182
221
|
catch {
|
|
183
222
|
// Diagnostics must never break Telegram runtime behavior.
|
|
@@ -185,7 +224,7 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
185
224
|
},
|
|
186
225
|
record(event) {
|
|
187
226
|
try {
|
|
188
|
-
|
|
227
|
+
appendEvent(event);
|
|
189
228
|
}
|
|
190
229
|
catch {
|
|
191
230
|
// Diagnostics must never break Telegram runtime behavior.
|
|
@@ -193,19 +232,22 @@ export function createTelegramRuntimeJsonlLog(options = {}) {
|
|
|
193
232
|
},
|
|
194
233
|
};
|
|
195
234
|
}
|
|
196
|
-
export function createTelegramRuntimeDiagnosticsRuntime() {
|
|
235
|
+
export function createTelegramRuntimeDiagnosticsRuntime(options = {}) {
|
|
236
|
+
const sharedFile = options.sharedFile === true;
|
|
197
237
|
let getBotToken = () => undefined;
|
|
198
238
|
let getProfileName = () => undefined;
|
|
199
239
|
let canReset = () => false;
|
|
200
240
|
let commitReset = (_commit) => false;
|
|
241
|
+
let captureAuthority = () => undefined;
|
|
201
242
|
let statusPorts;
|
|
202
|
-
let requestSnapshotPersist = () => { };
|
|
203
243
|
const events = Status.createTelegramRuntimeEventRecorder({
|
|
204
244
|
getBotToken: () => getBotToken(),
|
|
205
245
|
});
|
|
206
246
|
const jsonl = createTelegramRuntimeJsonlLog({
|
|
207
|
-
path: () => getTelegramRuntimeLogPath(undefined, getProfileName()),
|
|
208
|
-
previousPath: () =>
|
|
247
|
+
path: () => sharedFile ? resolveTelegramRuntimeLogPath() : getTelegramRuntimeLogPath(undefined, getProfileName()),
|
|
248
|
+
previousPath: () => sharedFile ? resolveTelegramPreviousSharedRuntimeLogPath()
|
|
249
|
+
: getTelegramPreviousRuntimeLogPath(undefined, getProfileName()),
|
|
250
|
+
...(sharedFile ? { sharedProfiles: { getProfileName: () => getProfileName(), captureAuthority: () => captureAuthority() } } : {}),
|
|
209
251
|
canReset: () => canReset(),
|
|
210
252
|
commitReset: (commit) => commitReset(commit),
|
|
211
253
|
});
|
|
@@ -216,11 +258,34 @@ export function createTelegramRuntimeDiagnosticsRuntime() {
|
|
|
216
258
|
jsonl.record(latestEvent);
|
|
217
259
|
requestSnapshotPersist();
|
|
218
260
|
};
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
261
|
+
const captureSnapshotScope = () => {
|
|
262
|
+
const ports = statusPorts;
|
|
263
|
+
if (!ports)
|
|
264
|
+
return undefined;
|
|
265
|
+
if (!ports.session)
|
|
266
|
+
return () => statusPorts === ports;
|
|
267
|
+
const session = ports.session, ctx = session.get(), generation = session.getGeneration();
|
|
268
|
+
if (ctx === undefined || !session.isCurrent(ctx, generation))
|
|
269
|
+
return undefined;
|
|
270
|
+
return () => statusPorts === ports && session.isCurrent(ctx, generation);
|
|
223
271
|
};
|
|
272
|
+
const requestSnapshotPersist = Status.createTelegramRuntimeDiagnosticsSnapshotScheduler({
|
|
273
|
+
...options.snapshotTimer,
|
|
274
|
+
captureScope: captureSnapshotScope,
|
|
275
|
+
async persistSnapshot(isCurrent) {
|
|
276
|
+
const ports = statusPorts;
|
|
277
|
+
// Projection may acquire journal guards or recover a snapshot: fence before reading it.
|
|
278
|
+
if (!ports || !isCurrent())
|
|
279
|
+
return;
|
|
280
|
+
const snapshot = Status.createTelegramStatusSnapshot(ports.getStatusState());
|
|
281
|
+
if (!isCurrent())
|
|
282
|
+
return;
|
|
283
|
+
await ports.persistSnapshot(snapshot);
|
|
284
|
+
},
|
|
285
|
+
recordError(error) {
|
|
286
|
+
events.record("telegram", error, { phase: "runtime-diagnostics-snapshot-persist" });
|
|
287
|
+
},
|
|
288
|
+
});
|
|
224
289
|
const updateRuntimeLogScope = function (reason) {
|
|
225
290
|
if (!statusPorts)
|
|
226
291
|
return;
|
|
@@ -238,21 +303,17 @@ export function createTelegramRuntimeDiagnosticsRuntime() {
|
|
|
238
303
|
getProfileName = ports.getProfileName;
|
|
239
304
|
canReset = ports.canReset;
|
|
240
305
|
commitReset = ports.commitReset;
|
|
306
|
+
captureAuthority = ports.captureAuthority ?? (() => undefined);
|
|
241
307
|
},
|
|
242
308
|
bindStatus(ports) {
|
|
243
309
|
statusPorts = ports;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
persistSnapshot: persistCurrentSnapshot,
|
|
247
|
-
recordError(error) {
|
|
248
|
-
events.record("telegram", error, {
|
|
249
|
-
phase: "runtime-diagnostics-snapshot-persist",
|
|
250
|
-
});
|
|
251
|
-
},
|
|
252
|
-
});
|
|
310
|
+
if (ports.session)
|
|
311
|
+
void requestSnapshotPersist.suspend();
|
|
253
312
|
},
|
|
313
|
+
onSessionStart: requestSnapshotPersist.resume,
|
|
314
|
+
onSessionShutdown: requestSnapshotPersist.suspend,
|
|
254
315
|
updateStatus(ctx, error) {
|
|
255
|
-
if (!statusPorts)
|
|
316
|
+
if (!statusPorts || statusPorts.session?.isCurrent(ctx) === false)
|
|
256
317
|
return;
|
|
257
318
|
statusPorts.updateStatus(ctx, error);
|
|
258
319
|
updateRuntimeLogScope("status-scope-change");
|
|
@@ -260,11 +321,7 @@ export function createTelegramRuntimeDiagnosticsRuntime() {
|
|
|
260
321
|
getStatusLines(options) {
|
|
261
322
|
if (!statusPorts)
|
|
262
323
|
return [];
|
|
263
|
-
|
|
264
|
-
recordRuntimeEvent("telegram", error, {
|
|
265
|
-
phase: "status-snapshot-persist",
|
|
266
|
-
});
|
|
267
|
-
});
|
|
324
|
+
requestSnapshotPersist();
|
|
268
325
|
return Status.buildTelegramBridgeStatusLines(statusPorts.getStatusState(), options);
|
|
269
326
|
},
|
|
270
327
|
scheduleSnapshotPersist() {
|
|
@@ -61,9 +61,16 @@ export interface TelegramReplyToMessage {
|
|
|
61
61
|
}
|
|
62
62
|
export interface TelegramSticker {
|
|
63
63
|
file_id: string;
|
|
64
|
+
is_video?: boolean;
|
|
65
|
+
is_animated?: boolean;
|
|
64
66
|
}
|
|
65
67
|
export interface TelegramMediaMessage {
|
|
66
68
|
message_id: number;
|
|
69
|
+
chat?: {
|
|
70
|
+
id: number;
|
|
71
|
+
type?: string;
|
|
72
|
+
username?: string;
|
|
73
|
+
};
|
|
67
74
|
from?: TelegramMessageUser;
|
|
68
75
|
forward_origin?: TelegramMessageForwardOrigin;
|
|
69
76
|
forward_from?: TelegramMessageUser;
|
|
@@ -125,9 +132,29 @@ export interface TelegramMediaGroupControllerOptions {
|
|
|
125
132
|
clearTimer?: (timer: ReturnType<typeof setTimeout>) => void;
|
|
126
133
|
}
|
|
127
134
|
export type TelegramAttachmentKind = "photo" | "document" | "video" | "audio" | "voice" | "animation" | "sticker";
|
|
135
|
+
/** Where an inbound attachment came from; the download layer turns it into a scoped file name. */
|
|
136
|
+
export interface TelegramAttachmentSource {
|
|
137
|
+
kind: string;
|
|
138
|
+
messageId: number;
|
|
139
|
+
index?: number;
|
|
140
|
+
/** Sender-chosen file name, kept as a trailing readable segment. */
|
|
141
|
+
userFileName?: string;
|
|
142
|
+
chat?: {
|
|
143
|
+
id: number;
|
|
144
|
+
type?: string;
|
|
145
|
+
username?: string;
|
|
146
|
+
};
|
|
147
|
+
/** Explicit source scope, e.g. a Guest Mode peer whose message IDs are not the bot chat's. */
|
|
148
|
+
scope?: string;
|
|
149
|
+
}
|
|
128
150
|
export interface TelegramFileInfo {
|
|
129
151
|
file_id: string;
|
|
130
152
|
fileName: string;
|
|
153
|
+
/** Present only when the sender chose the name. */
|
|
154
|
+
userFileName?: string;
|
|
155
|
+
/** Position inside a rich message. */
|
|
156
|
+
index?: number;
|
|
157
|
+
source?: TelegramAttachmentSource;
|
|
131
158
|
mimeType?: string;
|
|
132
159
|
kind: TelegramAttachmentKind;
|
|
133
160
|
isImage: boolean;
|
|
@@ -147,20 +174,16 @@ export interface DownloadedTelegramMessageFile {
|
|
|
147
174
|
kind?: TelegramAttachmentKind;
|
|
148
175
|
}
|
|
149
176
|
export interface DownloadTelegramMessageFilesDeps {
|
|
150
|
-
downloadFile: (fileId: string, fileName: string) => Promise<string>;
|
|
177
|
+
downloadFile: (fileId: string, fileName: string, source?: TelegramAttachmentSource) => Promise<string>;
|
|
151
178
|
}
|
|
152
|
-
export declare function guessExtensionFromMime(mimeType: string | undefined, fallback: string): string;
|
|
153
179
|
export declare function guessMediaType(path: string): string | undefined;
|
|
154
180
|
export declare function extractTelegramMessageText(message: TelegramMediaMessage): string;
|
|
155
181
|
export declare function extractTelegramForwardContextText(message: TelegramMediaMessage, allowedUserId?: number): string;
|
|
156
|
-
export declare function extractTelegramReplyContextText(message: TelegramMediaMessage): string;
|
|
157
182
|
export declare function buildTelegramReplyContextBlock(message: TelegramMediaMessage, replyFiles?: Pick<DownloadedTelegramFile, "path">[], replyOutputs?: readonly string[]): string;
|
|
158
|
-
export declare function appendTelegramReplyContext(text: string, replyContext: string): string;
|
|
159
183
|
export declare function extractTelegramMessagePromptText(message: TelegramMediaMessage): string;
|
|
160
184
|
export declare function extractTelegramMessagesText(messages: TelegramMediaMessage[]): string;
|
|
161
185
|
export declare function extractTelegramMessagesPromptText(messages: TelegramMediaMessage[]): string;
|
|
162
186
|
export declare function extractFirstTelegramMessageText(messages: TelegramMediaMessage[]): string;
|
|
163
|
-
export declare function hasTelegramMessagePromptContent(message: TelegramMediaMessage): boolean;
|
|
164
187
|
export declare function hasTelegramMessagesPromptContent(messages: TelegramMediaMessage[]): boolean;
|
|
165
188
|
export declare function collectTelegramMessageIds(messages: TelegramMediaMessage[]): number[];
|
|
166
189
|
export declare function getTelegramMediaGroupKey(message: TelegramMediaGroupMessage): string | undefined;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { basename, dirname } from "node:path";
|
|
7
7
|
const TELEGRAM_MEDIA_GROUP_DEBOUNCE_MS = 1200;
|
|
8
8
|
const TELEGRAM_REPLY_CONTEXT_MAX_LENGTH = 1000;
|
|
9
|
-
|
|
9
|
+
function guessExtensionFromMime(mimeType, fallback) {
|
|
10
10
|
if (!mimeType)
|
|
11
11
|
return fallback;
|
|
12
12
|
const normalized = mimeType.toLowerCase();
|
|
@@ -162,7 +162,7 @@ export function extractTelegramForwardContextText(message, allowedUserId) {
|
|
|
162
162
|
return "";
|
|
163
163
|
return `from: ${origin}`;
|
|
164
164
|
}
|
|
165
|
-
|
|
165
|
+
function extractTelegramReplyContextText(message) {
|
|
166
166
|
const quoted = (extractTelegramRichMessageText(message.reply_to_message?.rich_message) ||
|
|
167
167
|
message.reply_to_message?.text ||
|
|
168
168
|
message.reply_to_message?.caption ||
|
|
@@ -195,7 +195,7 @@ export function buildTelegramReplyContextBlock(message, replyFiles = [], replyOu
|
|
|
195
195
|
}
|
|
196
196
|
return "";
|
|
197
197
|
}
|
|
198
|
-
|
|
198
|
+
function appendTelegramReplyContext(text, replyContext) {
|
|
199
199
|
if (!replyContext)
|
|
200
200
|
return text;
|
|
201
201
|
return text ? `${text}\n\n${replyContext}` : `_\n\n${replyContext}`;
|
|
@@ -216,7 +216,7 @@ export function extractTelegramMessagesPromptText(messages) {
|
|
|
216
216
|
export function extractFirstTelegramMessageText(messages) {
|
|
217
217
|
return messages.map(extractTelegramMessageText).find(Boolean) ?? "";
|
|
218
218
|
}
|
|
219
|
-
|
|
219
|
+
function hasTelegramMessagePromptContent(message) {
|
|
220
220
|
return (!!extractTelegramMessageText(message) ||
|
|
221
221
|
(Array.isArray(message.photo) && message.photo.length > 0) ||
|
|
222
222
|
!!message.document ||
|
|
@@ -422,7 +422,7 @@ export async function downloadTelegramMessageFiles(messages, deps) {
|
|
|
422
422
|
const downloaded = [];
|
|
423
423
|
for (const file of collectTelegramFileInfos(messages)) {
|
|
424
424
|
downloaded.push({
|
|
425
|
-
path: await deps.downloadFile(file.file_id, file.fileName),
|
|
425
|
+
path: await deps.downloadFile(file.file_id, file.fileName, file.source),
|
|
426
426
|
fileName: file.fileName,
|
|
427
427
|
isImage: file.isImage,
|
|
428
428
|
mimeType: file.mimeType,
|
|
@@ -463,6 +463,7 @@ function collectTelegramRichBlockFileInfos(blocks, messageId) {
|
|
|
463
463
|
files.push({
|
|
464
464
|
file_id: photo.file_id,
|
|
465
465
|
fileName: `photo-${messageId}-${mediaIndex}.jpg`,
|
|
466
|
+
index: mediaIndex,
|
|
466
467
|
mimeType: "image/jpeg",
|
|
467
468
|
kind: "photo",
|
|
468
469
|
isImage: true,
|
|
@@ -489,6 +490,8 @@ function collectTelegramRichBlockFileInfos(blocks, messageId) {
|
|
|
489
490
|
const fallbackExtension = kind === "voice" ? ".ogg" : kind === "audio" ? ".mp3" : ".mp4";
|
|
490
491
|
files.push({
|
|
491
492
|
file_id: fileId,
|
|
493
|
+
index: mediaIndex,
|
|
494
|
+
...(typeof fileName === "string" ? { userFileName: fileName } : {}),
|
|
492
495
|
fileName: typeof fileName === "string"
|
|
493
496
|
? fileName
|
|
494
497
|
: `${kind}-${messageId}-${mediaIndex}${guessExtensionFromMime(typeof mimeType === "string" ? mimeType : undefined, fallbackExtension)}`,
|
|
@@ -512,6 +515,7 @@ function collectTelegramRichBlockFileInfos(blocks, messageId) {
|
|
|
512
515
|
export function collectTelegramFileInfos(messages) {
|
|
513
516
|
const files = [];
|
|
514
517
|
for (const message of messages) {
|
|
518
|
+
const firstOfMessage = files.length;
|
|
515
519
|
files.push(...collectTelegramRichBlockFileInfos(message.rich_message?.blocks, message.message_id));
|
|
516
520
|
if (Array.isArray(message.photo) && message.photo.length > 0) {
|
|
517
521
|
const photo = [...message.photo]
|
|
@@ -533,6 +537,7 @@ export function collectTelegramFileInfos(messages) {
|
|
|
533
537
|
files.push({
|
|
534
538
|
file_id: message.document.file_id,
|
|
535
539
|
fileName,
|
|
540
|
+
...(message.document.file_name ? { userFileName: message.document.file_name } : {}),
|
|
536
541
|
mimeType: message.document.mime_type,
|
|
537
542
|
kind: "document",
|
|
538
543
|
isImage: isImageMimeType(message.document.mime_type),
|
|
@@ -544,6 +549,7 @@ export function collectTelegramFileInfos(messages) {
|
|
|
544
549
|
files.push({
|
|
545
550
|
file_id: message.video.file_id,
|
|
546
551
|
fileName,
|
|
552
|
+
...(message.video.file_name ? { userFileName: message.video.file_name } : {}),
|
|
547
553
|
mimeType: message.video.mime_type,
|
|
548
554
|
kind: "video",
|
|
549
555
|
isImage: false,
|
|
@@ -556,6 +562,7 @@ export function collectTelegramFileInfos(messages) {
|
|
|
556
562
|
files.push({
|
|
557
563
|
file_id: message.audio.file_id,
|
|
558
564
|
fileName,
|
|
565
|
+
...(message.audio.file_name ? { userFileName: message.audio.file_name } : {}),
|
|
559
566
|
mimeType: message.audio.mime_type,
|
|
560
567
|
kind: "audio",
|
|
561
568
|
isImage: false,
|
|
@@ -577,20 +584,31 @@ export function collectTelegramFileInfos(messages) {
|
|
|
577
584
|
files.push({
|
|
578
585
|
file_id: message.animation.file_id,
|
|
579
586
|
fileName,
|
|
587
|
+
...(message.animation.file_name ? { userFileName: message.animation.file_name } : {}),
|
|
580
588
|
mimeType: message.animation.mime_type,
|
|
581
589
|
kind: "animation",
|
|
582
590
|
isImage: false,
|
|
583
591
|
});
|
|
584
592
|
}
|
|
585
593
|
if (message.sticker) {
|
|
594
|
+
// Sticker format is defined by Bot API flags, not a documented MIME field.
|
|
595
|
+
const video = message.sticker.is_video === true;
|
|
596
|
+
const animated = message.sticker.is_animated === true;
|
|
597
|
+
const extension = video ? ".webm" : animated ? ".tgs" : ".webp";
|
|
586
598
|
files.push({
|
|
587
599
|
file_id: message.sticker.file_id,
|
|
588
|
-
fileName: `sticker-${message.message_id}
|
|
589
|
-
mimeType: "image/webp",
|
|
600
|
+
fileName: `sticker-${message.message_id}${extension}`,
|
|
601
|
+
mimeType: video ? "video/webm" : animated ? "application/x-tgsticker" : "image/webp",
|
|
590
602
|
kind: "sticker",
|
|
591
|
-
isImage:
|
|
603
|
+
isImage: !video && !animated,
|
|
592
604
|
});
|
|
593
605
|
}
|
|
606
|
+
for (const file of files.slice(firstOfMessage)) {
|
|
607
|
+
file.source = { kind: file.kind, messageId: message.message_id,
|
|
608
|
+
...(file.index !== undefined ? { index: file.index } : {}),
|
|
609
|
+
...(file.userFileName ? { userFileName: file.userFileName } : {}),
|
|
610
|
+
...(message.chat ? { chat: message.chat } : {}) };
|
|
611
|
+
}
|
|
594
612
|
}
|
|
595
613
|
const seenFileIds = new Set();
|
|
596
614
|
return files.filter((file) => {
|
|
@@ -177,7 +177,6 @@ export interface TelegramModelMenuRuntime<TModel extends MenuModel = MenuModel>
|
|
|
177
177
|
export declare const TELEGRAM_MODEL_PAGE_SIZE = 6;
|
|
178
178
|
export declare const MODEL_MENU_TITLE = "<b>\uD83E\uDD16 Choose a model:</b>";
|
|
179
179
|
export declare const MODEL_PAGE_MENU_TITLE = "<b>Choose a page:</b>";
|
|
180
|
-
export declare const MODEL_DETAIL_MENU_TITLE = "<b>\uD83E\uDD16 Model:</b>";
|
|
181
180
|
export declare function formatScopedModelButtonText<TModel extends MenuModel = MenuModel>(entry: ScopedTelegramModel<TModel>, currentModel: TModel | undefined): string;
|
|
182
181
|
export declare function formatStatusButtonLabel(label: string, value: string): string;
|
|
183
182
|
export declare function getModelMenuItems<TModel extends MenuModel = MenuModel>(state: TelegramModelMenuState<TModel>): ScopedTelegramModel<TModel>[];
|
|
@@ -195,21 +194,11 @@ export declare function buildTelegramModelMenuStateRuntime<TContext extends Tele
|
|
|
195
194
|
export declare function applyTelegramModelScopeSelection(state: TelegramModelMenuState, value: string | undefined): TelegramMenuMutationResult;
|
|
196
195
|
export declare function applyTelegramModelPageSelection(state: TelegramModelMenuState, value: string | undefined): TelegramMenuMutationResult;
|
|
197
196
|
export declare function getTelegramModelSelection<TModel extends MenuModel = MenuModel>(state: TelegramModelMenuState<TModel>, value: string | undefined): TelegramMenuSelectionResult<TModel>;
|
|
198
|
-
export declare function applyTelegramModelDetailSelection(state: TelegramModelMenuState, value: string | undefined): TelegramMenuMutationResult;
|
|
199
|
-
export declare function getTelegramSelectedDetailModel<TModel extends MenuModel = MenuModel>(state: TelegramModelMenuState<TModel>): TelegramMenuSelectionResult<TModel>;
|
|
200
|
-
export declare function isTelegramModelScoped(state: TelegramModelMenuState, model: MenuModel): boolean;
|
|
201
|
-
export declare function focusTelegramModelListPage(state: TelegramModelMenuState, model: MenuModel, pageSize?: number): void;
|
|
202
|
-
export declare function setTelegramModelScope(state: TelegramModelMenuState, model: MenuModel, enabled: boolean): {
|
|
203
|
-
patterns: string[];
|
|
204
|
-
enabled: boolean;
|
|
205
|
-
};
|
|
206
197
|
export declare function buildTelegramModelCallbackPlan<TModel extends MenuModel = MenuModel>(params: BuildTelegramModelCallbackPlanParams<TModel>): TelegramModelCallbackPlan<TModel>;
|
|
207
198
|
export declare function openTelegramModelMenu<TModel extends MenuModel = MenuModel>(deps: TelegramModelMenuOpenDeps<TModel>): Promise<void>;
|
|
208
199
|
export declare function handleTelegramModelMenuCallbackAction<TModel extends MenuModel = MenuModel>(callbackQueryId: string, params: BuildTelegramModelCallbackPlanParams<TModel>, deps: TelegramModelMenuCallbackDeps<TModel>): Promise<boolean>;
|
|
209
200
|
export declare function getTelegramModelMenuPage(state: TelegramModelMenuState, pageSize: number): TelegramModelMenuPage;
|
|
210
201
|
export declare function buildModelMenuReplyMarkup(state: TelegramModelMenuState, currentModel: MenuModel | undefined, pageSize: number): TelegramReplyMarkup;
|
|
211
|
-
export declare function buildModelDetailMenuReplyMarkup(state: TelegramModelMenuState, currentModel: MenuModel | undefined): TelegramReplyMarkup;
|
|
212
|
-
export declare function buildModelDetailMenuText(state: TelegramModelMenuState): string;
|
|
213
202
|
export declare function buildModelPageMenuReplyMarkup(state: TelegramModelMenuState, pageSize: number): TelegramReplyMarkup;
|
|
214
203
|
export declare function buildTelegramModelPageMenuRenderPayload(state: TelegramModelMenuState): TelegramMenuRenderPayload;
|
|
215
204
|
export declare function buildTelegramModelMenuRenderPayload(state: TelegramModelMenuState, activeModel: MenuModel | undefined): TelegramMenuRenderPayload;
|
|
@@ -11,7 +11,7 @@ export const TELEGRAM_MODEL_PAGE_SIZE = 6;
|
|
|
11
11
|
const TELEGRAM_MODEL_PAGE_PICKER_ROW_SIZE = 4;
|
|
12
12
|
export const MODEL_MENU_TITLE = "<b>🤖 Choose a model:</b>";
|
|
13
13
|
export const MODEL_PAGE_MENU_TITLE = "<b>Choose a page:</b>";
|
|
14
|
-
|
|
14
|
+
const MODEL_DETAIL_MENU_TITLE = "<b>🤖 Model:</b>";
|
|
15
15
|
function truncateTelegramButtonLabel(label, maxLength = 56) {
|
|
16
16
|
return label.length <= maxLength
|
|
17
17
|
? label
|
|
@@ -249,7 +249,7 @@ export function getTelegramModelSelection(state, value) {
|
|
|
249
249
|
return { kind: "missing" };
|
|
250
250
|
return { kind: "selected", selection };
|
|
251
251
|
}
|
|
252
|
-
|
|
252
|
+
function applyTelegramModelDetailSelection(state, value) {
|
|
253
253
|
const index = Number(value);
|
|
254
254
|
if (!Number.isFinite(index))
|
|
255
255
|
return "invalid";
|
|
@@ -261,7 +261,7 @@ export function applyTelegramModelDetailSelection(state, value) {
|
|
|
261
261
|
state.mode = "model-detail";
|
|
262
262
|
return "changed";
|
|
263
263
|
}
|
|
264
|
-
|
|
264
|
+
function getTelegramSelectedDetailModel(state) {
|
|
265
265
|
const indexedSelection = getTelegramModelSelection(state, state.selectedModelIndex?.toString());
|
|
266
266
|
if (indexedSelection.kind === "selected") {
|
|
267
267
|
if (!state.selectedModelKey)
|
|
@@ -279,11 +279,11 @@ export function getTelegramSelectedDetailModel(state) {
|
|
|
279
279
|
}
|
|
280
280
|
return indexedSelection;
|
|
281
281
|
}
|
|
282
|
-
|
|
282
|
+
function isTelegramModelScoped(state, model) {
|
|
283
283
|
const key = getCanonicalModelId(model);
|
|
284
284
|
return state.scopedModels.some((entry) => getCanonicalModelId(entry.model) === key);
|
|
285
285
|
}
|
|
286
|
-
|
|
286
|
+
function focusTelegramModelListPage(state, model, pageSize = TELEGRAM_MODEL_PAGE_SIZE) {
|
|
287
287
|
const key = getCanonicalModelId(model).toLowerCase();
|
|
288
288
|
const index = getModelMenuItems(state).findIndex((entry) => getCanonicalModelId(entry.model).toLowerCase() === key);
|
|
289
289
|
state.page = index < 0 ? 0 : Math.floor(index / pageSize);
|
|
@@ -293,7 +293,7 @@ function formatScopedModelPattern(entry) {
|
|
|
293
293
|
const key = getCanonicalModelId(entry.model);
|
|
294
294
|
return entry.thinkingLevel ? `${key}:${entry.thinkingLevel}` : key;
|
|
295
295
|
}
|
|
296
|
-
|
|
296
|
+
function setTelegramModelScope(state, model, enabled) {
|
|
297
297
|
const key = getCanonicalModelId(model);
|
|
298
298
|
const lowerKey = key.toLowerCase();
|
|
299
299
|
const scopedModelPatterns = state.scopedModelPatterns ?? [];
|
|
@@ -583,7 +583,7 @@ export function buildModelMenuReplyMarkup(state, currentModel, pageSize) {
|
|
|
583
583
|
]));
|
|
584
584
|
return { inline_keyboard: rows };
|
|
585
585
|
}
|
|
586
|
-
|
|
586
|
+
function buildModelDetailMenuReplyMarkup(state, currentModel) {
|
|
587
587
|
const selection = getTelegramSelectedDetailModel(state);
|
|
588
588
|
if (selection.kind !== "selected") {
|
|
589
589
|
return {
|
|
@@ -617,7 +617,7 @@ export function buildModelDetailMenuReplyMarkup(state, currentModel) {
|
|
|
617
617
|
],
|
|
618
618
|
};
|
|
619
619
|
}
|
|
620
|
-
|
|
620
|
+
function buildModelDetailMenuText(state) {
|
|
621
621
|
const selection = getTelegramSelectedDetailModel(state);
|
|
622
622
|
if (selection.kind !== "selected")
|
|
623
623
|
return MODEL_DETAIL_MENU_TITLE;
|