@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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telegram Thread manual-name
|
|
3
|
-
* Zones:
|
|
4
|
-
* Owns one expiring exact-target
|
|
5
|
-
* Excludes
|
|
2
|
+
* Telegram Thread naming rules and manual-name interaction
|
|
3
|
+
* Zones: thread identity names, palettes, title formatting, runtime controls
|
|
4
|
+
* Owns name/title value policy and one expiring exact-target dialog per session scope.
|
|
5
|
+
* Excludes occupancy/slot allocation, display-mode projection, transport and API/store effects.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type
|
|
8
|
+
import { areTelegramTargetsEqual as sameTarget, type TelegramTarget } from "./target.ts";
|
|
9
9
|
|
|
10
10
|
export const TELEGRAM_THREAD_NAME_DIALOG_TTL_MS = 5 * 60_000;
|
|
11
11
|
|
|
@@ -23,10 +23,6 @@ function targetKey(target: TelegramTarget): string {
|
|
|
23
23
|
return `${target.chatId}:${target.threadId ?? "chat"}`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function sameTarget(left: TelegramTarget, right: TelegramTarget): boolean {
|
|
27
|
-
return left.chatId === right.chatId && left.threadId === right.threadId;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
function cloneCandidate(
|
|
31
27
|
candidate: TelegramThreadNameDialogCandidate,
|
|
32
28
|
): TelegramThreadNameDialogCandidate {
|
|
@@ -116,3 +112,265 @@ export function createTelegramThreadNameDialogRuntime(options?: {
|
|
|
116
112
|
},
|
|
117
113
|
};
|
|
118
114
|
}
|
|
115
|
+
|
|
116
|
+
export interface TelegramThreadTitleInput {
|
|
117
|
+
instanceId: string;
|
|
118
|
+
profileKey: string;
|
|
119
|
+
threadName?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface TelegramThreadNameInput {
|
|
123
|
+
seed: string;
|
|
124
|
+
cwd?: string;
|
|
125
|
+
role?: "leader" | "follower";
|
|
126
|
+
peers?: readonly string[];
|
|
127
|
+
slot?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function hashString(value: string): number {
|
|
131
|
+
let hash = 2166136261;
|
|
132
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
133
|
+
hash ^= value.charCodeAt(index);
|
|
134
|
+
hash = Math.imul(hash, 16777619);
|
|
135
|
+
}
|
|
136
|
+
return hash >>> 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getWorkspaceHint(cwd: string | undefined): string | undefined {
|
|
140
|
+
if (!cwd) return undefined;
|
|
141
|
+
const parts = cwd.split("/").filter(Boolean);
|
|
142
|
+
const last = parts.at(-1)?.trim();
|
|
143
|
+
if (!last) return undefined;
|
|
144
|
+
return (
|
|
145
|
+
last
|
|
146
|
+
.replace(/[^\p{L}\p{N}._-]+/gu, " ")
|
|
147
|
+
.trim()
|
|
148
|
+
.slice(0, 32) || undefined
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function createTelegramThreadName(
|
|
153
|
+
input: TelegramThreadNameInput,
|
|
154
|
+
): string {
|
|
155
|
+
const workspace = getWorkspaceHint(input.cwd);
|
|
156
|
+
const roleMark =
|
|
157
|
+
input.role === "leader"
|
|
158
|
+
? "Leader"
|
|
159
|
+
: input.role === "follower"
|
|
160
|
+
? "Follower"
|
|
161
|
+
: undefined;
|
|
162
|
+
const slot = input.slot ? `Thread ${input.slot}` : undefined;
|
|
163
|
+
const peerSalt = input.peers?.slice().sort().join("|") ?? "";
|
|
164
|
+
const fallback = `Instance ${hashString(
|
|
165
|
+
`${input.seed}|${input.cwd ?? ""}|${input.role ?? ""}|${peerSalt}|${input.slot ?? ""}`,
|
|
166
|
+
)
|
|
167
|
+
.toString(36)
|
|
168
|
+
.slice(0, 4)}`;
|
|
169
|
+
return (
|
|
170
|
+
[slot, workspace, roleMark].filter(Boolean).join(" ").slice(0, 96) ||
|
|
171
|
+
fallback
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function normalizeTelegramTopicTargetThreadName(
|
|
176
|
+
threadName: string,
|
|
177
|
+
): string {
|
|
178
|
+
return threadName.replace(/\s+/g, " ").trim().slice(0, 96);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function getGraphemeSegments(value: string): string[] {
|
|
182
|
+
const segmenter = (
|
|
183
|
+
Intl as unknown as {
|
|
184
|
+
Segmenter?: new (
|
|
185
|
+
locale?: string,
|
|
186
|
+
options?: { granularity: "grapheme" },
|
|
187
|
+
) => { segment(input: string): Iterable<{ segment: string }> };
|
|
188
|
+
}
|
|
189
|
+
).Segmenter;
|
|
190
|
+
if (!segmenter) return Array.from(value);
|
|
191
|
+
return Array.from(
|
|
192
|
+
new segmenter(undefined, { granularity: "grapheme" }).segment(value),
|
|
193
|
+
(part) => part.segment,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function getTelegramTopicIdentityName(threadName: string): string {
|
|
198
|
+
return getGraphemeSegments(normalizeTelegramTopicTargetThreadName(threadName))
|
|
199
|
+
.join("")
|
|
200
|
+
.trim();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const TELEGRAM_THREAD_NAME_PALETTE: Record<string, readonly string[]> = {
|
|
204
|
+
A: ["Atlas", "Aster", "Aurora", "Anchor", "Ashen"],
|
|
205
|
+
B: ["Beacon", "Briar", "Boreal", "Birch", "Bison"],
|
|
206
|
+
C: ["Cedar", "Comet", "Cipher", "Coral", "Cinder"],
|
|
207
|
+
D: ["Delta", "Dawn", "Drift", "Dune", "Dagger"],
|
|
208
|
+
E: ["Ember", "Echo", "Eagle", "Eden", "Elder"],
|
|
209
|
+
F: ["Falcon", "Fjord", "Flint", "Forest", "Fable"],
|
|
210
|
+
G: ["Grove", "Glade", "Glyph", "Garnet", "Gale"],
|
|
211
|
+
H: ["Harbor", "Hawk", "Hazel", "Helix", "Haven"],
|
|
212
|
+
I: ["Iris", "Ivory", "Iron", "Isle", "Idea"],
|
|
213
|
+
J: ["Jade", "Juno", "Jolt", "Jewel", "Jasper"],
|
|
214
|
+
K: ["Kite", "Karma", "Kernel", "Kodiak", "Kelp"],
|
|
215
|
+
L: ["Lumen", "Laurel", "Lynx", "Lotus", "Lagoon"],
|
|
216
|
+
M: ["Maple", "Meteor", "Meadow", "Marble", "Moss"],
|
|
217
|
+
N: ["Nimbus", "Nova", "Nectar", "North", "Noble"],
|
|
218
|
+
O: ["Orion", "Onyx", "Opal", "Orbit", "Olive"],
|
|
219
|
+
P: ["Pine", "Pulse", "Praxis", "Pebble", "Prism"],
|
|
220
|
+
Q: ["Quartz", "Quill", "Quasar", "Quest", "Quiver"],
|
|
221
|
+
R: ["River", "Raven", "Rune", "Reef", "Ridge"],
|
|
222
|
+
S: ["Spruce", "Solar", "Signal", "Stone", "Sable"],
|
|
223
|
+
T: ["Timber", "Talon", "Terra", "Torch", "Tide"],
|
|
224
|
+
U: ["Umber", "Unity", "Ursa", "Uplink", "Ulmus"],
|
|
225
|
+
V: ["Violet", "Vector", "Vista", "Vale", "Vortex"],
|
|
226
|
+
W: ["Willow", "Warden", "Wave", "Winter", "Wisp"],
|
|
227
|
+
X: ["Xenon", "Xylem", "Xavier", "Xylo", "Xerus"],
|
|
228
|
+
Y: ["Yarrow", "Yonder", "Yukon", "Yale", "Yogi"],
|
|
229
|
+
Z: ["Zenith", "Zephyr", "Zircon", "Zebra", "Zion"],
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export function chooseTelegramThreadName(input: {
|
|
233
|
+
slot: string | undefined;
|
|
234
|
+
entropy?: number | string;
|
|
235
|
+
getRandom?: () => number;
|
|
236
|
+
occupied?: readonly string[];
|
|
237
|
+
}): string | undefined {
|
|
238
|
+
if (!input.slot || !/^[A-Z]$/.test(input.slot)) return undefined;
|
|
239
|
+
const names = TELEGRAM_THREAD_NAME_PALETTE[input.slot];
|
|
240
|
+
if (!names || names.length === 0) return undefined;
|
|
241
|
+
const occupied = new Set(
|
|
242
|
+
(input.occupied ?? []).map((name) => getTelegramTopicIdentityName(name)),
|
|
243
|
+
);
|
|
244
|
+
const start = input.getRandom
|
|
245
|
+
? Math.max(
|
|
246
|
+
0,
|
|
247
|
+
Math.min(
|
|
248
|
+
names.length - 1,
|
|
249
|
+
Math.floor(input.getRandom() * names.length),
|
|
250
|
+
),
|
|
251
|
+
)
|
|
252
|
+
: getTelegramThreadNameEntropyIndex(input.entropy, names.length);
|
|
253
|
+
for (let offset = 0; offset < names.length; offset += 1) {
|
|
254
|
+
const name = names[(start + offset) % names.length];
|
|
255
|
+
if (!occupied.has(getTelegramTopicIdentityName(name))) return name;
|
|
256
|
+
}
|
|
257
|
+
for (const paletteSlot of "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
|
258
|
+
for (const name of TELEGRAM_THREAD_NAME_PALETTE[paletteSlot] ?? []) {
|
|
259
|
+
if (!occupied.has(getTelegramTopicIdentityName(name))) return name;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function getTelegramThreadNameLeadingSlot(
|
|
266
|
+
threadName: string | undefined,
|
|
267
|
+
): string | undefined {
|
|
268
|
+
if (!threadName) return undefined;
|
|
269
|
+
const first = getTelegramTopicIdentityName(threadName)[0];
|
|
270
|
+
return first && /^[A-Z]$/.test(first) ? first : undefined;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function getTelegramThreadNameEntropyIndex(
|
|
274
|
+
entropy: number | string | undefined,
|
|
275
|
+
length: number,
|
|
276
|
+
): number {
|
|
277
|
+
if (length <= 1) return 0;
|
|
278
|
+
if (typeof entropy === "number" && entropy < 1_000_000_000_000) return 0;
|
|
279
|
+
const value = entropy === undefined ? "0" : String(entropy);
|
|
280
|
+
let hash = 2166136261;
|
|
281
|
+
for (const char of value) {
|
|
282
|
+
hash ^= char.codePointAt(0) ?? 0;
|
|
283
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
284
|
+
}
|
|
285
|
+
return hash % length;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function getTelegramTopicThreadNameValidationError(
|
|
289
|
+
threadName: string,
|
|
290
|
+
_slot: string | undefined,
|
|
291
|
+
): string | undefined {
|
|
292
|
+
const identity = getTelegramTopicIdentityName(threadName);
|
|
293
|
+
const reasons: string[] = [];
|
|
294
|
+
if (!identity) reasons.push("it is empty after trimming");
|
|
295
|
+
if (/\s/.test(identity)) reasons.push("it contains spaces");
|
|
296
|
+
if (/[^A-Za-z]/.test(identity)) {
|
|
297
|
+
reasons.push("it contains characters outside Latin A-Z letters");
|
|
298
|
+
}
|
|
299
|
+
if (!/^[A-Z]/.test(identity)) {
|
|
300
|
+
reasons.push("it does not start with an uppercase Latin letter");
|
|
301
|
+
}
|
|
302
|
+
const genericLabels = new Set(["telegram", "leader", "follower"]);
|
|
303
|
+
if (genericLabels.has(identity.toLowerCase())) {
|
|
304
|
+
reasons.push("it is a generic role label");
|
|
305
|
+
}
|
|
306
|
+
if (/^[A-Z]$/.test(identity)) reasons.push("it is only a bare slot letter");
|
|
307
|
+
if (reasons.length === 0) return undefined;
|
|
308
|
+
return `Invalid Telegram instance name: ${reasons.join("; ")}. Use exactly one capitalized Latin word with no spaces, punctuation, emoji, non-Latin letters, or digits; it must not be a generic role label or only a bare slot letter.`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function getTelegramManualThreadDisplayNameValidationError(
|
|
312
|
+
threadName: string,
|
|
313
|
+
): string | undefined {
|
|
314
|
+
const trimmed = threadName.trim();
|
|
315
|
+
const normalized = trimmed.replace(/\s+/g, " ");
|
|
316
|
+
const reasons: string[] = [];
|
|
317
|
+
if (!trimmed) reasons.push("it is empty after trimming");
|
|
318
|
+
if (trimmed && /[^\x20-\x7E]/.test(trimmed)) {
|
|
319
|
+
reasons.push("it contains characters outside printable ASCII");
|
|
320
|
+
}
|
|
321
|
+
if (normalized.length > 96) reasons.push("it is longer than 96 characters");
|
|
322
|
+
if (/^[A-Z]$/.test(normalized)) {
|
|
323
|
+
reasons.push("a bare slot letter is reserved for reset to automatic");
|
|
324
|
+
}
|
|
325
|
+
if (reasons.length === 0) return undefined;
|
|
326
|
+
return `Invalid Telegram Thread display name: ${reasons.join("; ")}. Use 1–96 printable ASCII characters.`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function isTelegramTopicThreadNameValidForSlot(
|
|
330
|
+
threadName: string,
|
|
331
|
+
slot: string | undefined,
|
|
332
|
+
): boolean {
|
|
333
|
+
return !getTelegramTopicThreadNameValidationError(threadName, slot);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function applyTopicNameTemplate(
|
|
337
|
+
template: string,
|
|
338
|
+
request: TelegramThreadTitleInput,
|
|
339
|
+
slot?: string,
|
|
340
|
+
): string {
|
|
341
|
+
const threadName =
|
|
342
|
+
request.threadName?.replace(/\s+/g, " ").trim() || request.profileKey;
|
|
343
|
+
let result = template
|
|
344
|
+
.replaceAll("{threadName}", threadName)
|
|
345
|
+
.replaceAll("{profileKey}", request.profileKey)
|
|
346
|
+
.replaceAll("{instanceId}", request.instanceId);
|
|
347
|
+
if (slot) result = result.replaceAll("{slot}", slot);
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function getTelegramTopicName(
|
|
352
|
+
request: TelegramThreadTitleInput,
|
|
353
|
+
template = "{slot}",
|
|
354
|
+
slot?: string,
|
|
355
|
+
): string {
|
|
356
|
+
const name = applyTopicNameTemplate(template, request, slot)
|
|
357
|
+
.replace(/\s+/g, " ")
|
|
358
|
+
.trim();
|
|
359
|
+
return (name || slot || "Pi").slice(0, 128);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function getTelegramTopicTitleForThreadName(
|
|
363
|
+
threadName: string,
|
|
364
|
+
slot: string,
|
|
365
|
+
template = "{threadName}",
|
|
366
|
+
): string {
|
|
367
|
+
return getTelegramTopicName(
|
|
368
|
+
{
|
|
369
|
+
instanceId: "",
|
|
370
|
+
profileKey: normalizeTelegramTopicTargetThreadName(threadName) || "Pi",
|
|
371
|
+
threadName,
|
|
372
|
+
},
|
|
373
|
+
template,
|
|
374
|
+
slot,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
@@ -202,6 +202,8 @@ export interface ThreadReconciliationApplyResult {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
export interface ThreadReconciliationApplyPorts {
|
|
205
|
+
/** Private-chat temporary tabs support deletion, not forum closing. */
|
|
206
|
+
skipCloseBeforeDelete?: boolean;
|
|
205
207
|
isCleanupTargetProtected?: (target: ThreadTarget, action: ThreadReconciliationAction) => boolean;
|
|
206
208
|
callApi?: <TResponse>(
|
|
207
209
|
method: string,
|
|
@@ -636,7 +638,7 @@ export async function applyThreadReconciliationPlan(
|
|
|
636
638
|
}
|
|
637
639
|
let deleteConfirmed = false;
|
|
638
640
|
let superseded = false;
|
|
639
|
-
for (const method of ["closeForumTopic", "deleteForumTopic"]) {
|
|
641
|
+
for (const method of ports.skipCloseBeforeDelete ? ["deleteForumTopic"] : ["closeForumTopic", "deleteForumTopic"]) {
|
|
640
642
|
if (ports.isCleanupTargetProtected?.(action.target, action)) {
|
|
641
643
|
superseded = true;
|
|
642
644
|
break;
|