@llblab/pi-telegram 0.45.11 → 0.47.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 +5 -1
- package/CHANGELOG.md +12 -0
- package/README.md +6 -6
- package/dist/api/activity.d.ts +6 -0
- package/dist/api/activity.js +6 -0
- package/dist/api/commands.d.ts +6 -0
- package/dist/api/commands.js +6 -0
- package/dist/api/delivery.d.ts +6 -0
- package/dist/api/delivery.js +6 -0
- package/dist/api/inbound.d.ts +6 -0
- package/dist/api/inbound.js +6 -0
- package/dist/api/keyboard.d.ts +6 -0
- package/dist/api/keyboard.js +6 -0
- package/dist/api/outbound.d.ts +6 -0
- package/dist/api/outbound.js +6 -0
- package/dist/api/sections.d.ts +7 -0
- package/dist/api/sections.js +6 -0
- package/dist/api/status.d.ts +6 -0
- package/dist/api/status.js +6 -0
- package/dist/api/updates.d.ts +6 -0
- package/dist/api/updates.js +6 -0
- package/dist/api/voice.d.ts +6 -0
- package/dist/api/voice.js +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/lib/activity-verbosity.d.ts +52 -0
- package/dist/lib/activity-verbosity.js +596 -0
- package/dist/lib/activity.d.ts +220 -0
- package/dist/lib/activity.js +574 -0
- package/dist/lib/agent-messages.d.ts +28 -0
- package/dist/lib/agent-messages.js +86 -0
- package/dist/lib/bindings.d.ts +250 -0
- package/dist/lib/bindings.js +936 -0
- package/dist/lib/bus-api.d.ts +18 -0
- package/dist/lib/bus-api.js +254 -0
- package/dist/lib/bus-follower.d.ts +464 -0
- package/dist/lib/bus-follower.js +1686 -0
- package/dist/lib/bus-leader.d.ts +292 -0
- package/dist/lib/bus-leader.js +2242 -0
- package/dist/lib/bus-transport.d.ts +64 -0
- package/dist/lib/bus-transport.js +140 -0
- package/dist/lib/bus.d.ts +518 -0
- package/dist/lib/bus.js +2055 -0
- package/dist/lib/channel-posts.d.ts +170 -0
- package/dist/lib/channel-posts.js +611 -0
- package/dist/lib/command-templates.d.ts +70 -0
- package/dist/lib/command-templates.js +744 -0
- package/dist/lib/commands.d.ts +541 -0
- package/dist/lib/commands.js +1155 -0
- package/dist/lib/config.d.ts +252 -0
- package/dist/lib/config.js +889 -0
- package/dist/lib/delivery.d.ts +163 -0
- package/dist/lib/delivery.js +542 -0
- package/dist/lib/extension.d.ts +7 -0
- package/dist/lib/extension.js +1608 -0
- package/dist/lib/generative-app-worker.mjs +104 -0
- package/dist/lib/generative-apps.d.ts +212 -0
- package/dist/lib/generative-apps.js +1006 -0
- package/dist/lib/inbound.d.ts +91 -0
- package/dist/lib/inbound.js +502 -0
- package/dist/lib/journal.d.ts +665 -0
- package/dist/lib/journal.js +3701 -0
- package/dist/lib/keyboard.d.ts +23 -0
- package/dist/lib/keyboard.js +37 -0
- package/dist/lib/lifecycle.d.ts +157 -0
- package/dist/lib/lifecycle.js +395 -0
- package/dist/lib/locks.d.ts +164 -0
- package/dist/lib/locks.js +1208 -0
- package/dist/lib/logging.d.ts +50 -0
- package/dist/lib/logging.js +274 -0
- package/dist/lib/media.d.ts +181 -0
- package/dist/lib/media.js +602 -0
- package/dist/lib/menu-model.d.ts +217 -0
- package/dist/lib/menu-model.js +663 -0
- package/dist/lib/menu-queue.d.ts +37 -0
- package/dist/lib/menu-queue.js +408 -0
- package/dist/lib/menu-settings.d.ts +116 -0
- package/dist/lib/menu-settings.js +595 -0
- package/dist/lib/menu-status.d.ts +32 -0
- package/dist/lib/menu-status.js +112 -0
- package/dist/lib/menu-thinking.d.ts +29 -0
- package/dist/lib/menu-thinking.js +82 -0
- package/dist/lib/menu.d.ts +171 -0
- package/dist/lib/menu.js +323 -0
- package/dist/lib/model.d.ts +127 -0
- package/dist/lib/model.js +409 -0
- package/dist/lib/outbound-attachments.d.ts +241 -0
- package/dist/lib/outbound-attachments.js +639 -0
- package/dist/lib/outbound-buttons.d.ts +69 -0
- package/dist/lib/outbound-buttons.js +248 -0
- package/dist/lib/outbound-markup.d.ts +42 -0
- package/dist/lib/outbound-markup.js +678 -0
- package/dist/lib/outbound-voice.d.ts +55 -0
- package/dist/lib/outbound-voice.js +152 -0
- package/dist/lib/outbound.d.ts +185 -0
- package/dist/lib/outbound.js +516 -0
- package/dist/lib/ownership.d.ts +77 -0
- package/dist/lib/ownership.js +174 -0
- package/dist/lib/paths.d.ts +40 -0
- package/dist/lib/paths.js +94 -0
- package/dist/lib/pi.d.ts +72 -0
- package/dist/lib/pi.js +121 -0
- package/dist/lib/polling.d.ts +351 -0
- package/dist/lib/polling.js +1196 -0
- package/dist/lib/preview.d.ts +192 -0
- package/dist/lib/preview.js +614 -0
- package/dist/lib/prompt-templates.d.ts +24 -0
- package/dist/lib/prompt-templates.js +118 -0
- package/dist/lib/prompts.d.ts +57 -0
- package/dist/lib/prompts.js +167 -0
- package/dist/lib/queue.d.ts +766 -0
- package/dist/lib/queue.js +1965 -0
- package/dist/lib/recovery.d.ts +86 -0
- package/dist/lib/recovery.js +285 -0
- package/dist/lib/rendering.d.ts +20 -0
- package/dist/lib/rendering.js +983 -0
- package/dist/lib/replies.d.ts +214 -0
- package/dist/lib/replies.js +737 -0
- package/dist/lib/routing.d.ts +207 -0
- package/dist/lib/routing.js +2282 -0
- package/dist/lib/runtime.d.ts +172 -0
- package/dist/lib/runtime.js +402 -0
- package/dist/lib/sections.d.ts +165 -0
- package/dist/lib/sections.js +327 -0
- package/dist/lib/setup.d.ts +82 -0
- package/dist/lib/setup.js +150 -0
- package/dist/lib/skills.d.ts +8 -0
- package/dist/lib/skills.js +12 -0
- package/dist/lib/status.d.ts +442 -0
- package/dist/lib/status.js +1006 -0
- package/dist/lib/sync.d.ts +179 -0
- package/dist/lib/sync.js +782 -0
- package/dist/lib/target.d.ts +20 -0
- package/dist/lib/target.js +27 -0
- package/dist/lib/telegram-api.d.ts +541 -0
- package/dist/lib/telegram-api.js +1159 -0
- package/dist/lib/text-groups.d.ts +89 -0
- package/dist/lib/text-groups.js +317 -0
- package/dist/lib/thread-cleanup-manager.d.ts +288 -0
- package/dist/lib/thread-cleanup-manager.js +560 -0
- package/dist/lib/thread-display.d.ts +46 -0
- package/dist/lib/thread-display.js +257 -0
- package/dist/lib/thread-naming.d.ts +46 -0
- package/dist/lib/thread-naming.js +78 -0
- package/dist/lib/thread-reconciler.d.ts +239 -0
- package/dist/lib/thread-reconciler.js +644 -0
- package/dist/lib/threads.d.ts +621 -0
- package/dist/lib/threads.js +3679 -0
- package/dist/lib/time-injection.d.ts +15 -0
- package/dist/lib/time-injection.js +56 -0
- package/dist/lib/turns.d.ts +109 -0
- package/dist/lib/turns.js +500 -0
- package/dist/lib/updates.d.ts +1290 -0
- package/dist/lib/updates.js +3634 -0
- package/dist/lib/voice.d.ts +117 -0
- package/dist/lib/voice.js +174 -0
- package/dist/lib/workspace-admission.d.ts +264 -0
- package/dist/lib/workspace-admission.js +1136 -0
- package/dist/lib/workspace-retirement.d.ts +219 -0
- package/dist/lib/workspace-retirement.js +587 -0
- package/dist/lib/workspace-slots.d.ts +30 -0
- package/dist/lib/workspace-slots.js +54 -0
- package/dist/package.json +126 -0
- package/dist/pi-telegram/index.js +1 -0
- package/dist/skills/generated-control-surface/SKILL.md +107 -0
- package/dist/skills/generated-control-surface/references/capability-adapters.md +27 -0
- package/dist/skills/generated-control-surface/references/layout-and-state.md +37 -0
- package/dist/skills/generative-apps/SKILL.md +115 -0
- package/dist/skills/show-me/SKILL.md +166 -0
- package/dist/skills/show-me/references/telegram-surfaces.md +43 -0
- package/dist/skills/telegram-bridge/SKILL.md +129 -0
- package/dist/skills/telegram-bridge/references/configuration.md +15 -0
- package/dist/skills/telegram-bridge/references/delivery-and-threads.md +27 -0
- package/dist/skills/telegram-bridge/references/diagnosis.md +18 -0
- package/docs/architecture.md +1 -1
- package/docs/compact-matrix-literal.md +10 -3
- package/docs/generative-apps.md +15 -13
- package/docs/multi-instance-bus.md +17 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +2 -2
- package/lib/bindings.ts +89 -6
- package/lib/bus-follower.ts +11 -3
- package/lib/bus-leader.ts +57 -20
- package/lib/bus.ts +12 -2
- package/lib/commands.ts +2 -2
- package/lib/config.ts +14 -7
- package/lib/delivery.ts +38 -6
- package/lib/extension.ts +26 -1
- package/lib/generative-apps.ts +379 -13
- package/lib/menu-settings.ts +21 -5
- package/lib/outbound-markup.ts +16 -7
- package/lib/pi.ts +4 -0
- package/lib/sync.ts +3 -0
- package/lib/telegram-api.ts +12 -1
- package/lib/thread-cleanup-manager.ts +36 -6
- package/lib/thread-display.ts +96 -18
- package/lib/threads.ts +150 -30
- package/package.json +56 -13
- package/scripts/build-dist.mjs +44 -0
- package/scripts/measure-bus.mjs +8 -1
- package/scripts/measure-workspace.mjs +8 -1
- package/skills/generative-apps/SKILL.md +1 -1
- package/skills/show-me/SKILL.md +1 -1
- package/skills/telegram-bridge/SKILL.md +1 -1
package/lib/threads.ts
CHANGED
|
@@ -52,6 +52,10 @@ export interface TelegramThreadNameInput {
|
|
|
52
52
|
export interface TelegramWorkspaceBindingIdentity {
|
|
53
53
|
cwd: string;
|
|
54
54
|
workspaceKey: string;
|
|
55
|
+
/** Exact durable Pi session identity; absent only on legacy cwd-only bindings. */
|
|
56
|
+
sessionId?: string;
|
|
57
|
+
/** Full SHA-256 index component for session-qualified bindings. */
|
|
58
|
+
sessionKey?: string;
|
|
55
59
|
/** Immutable legacy binding-key component, not the displayed global letter. */
|
|
56
60
|
instanceSlot: string;
|
|
57
61
|
bindingKey: string;
|
|
@@ -60,6 +64,27 @@ export interface TelegramWorkspaceBindingIdentity {
|
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
const TELEGRAM_WORKSPACE_KEY_MAX_LENGTH = 180;
|
|
67
|
+
const TELEGRAM_SESSION_ID_MAX_LENGTH = 256;
|
|
68
|
+
|
|
69
|
+
export function normalizeTelegramSessionId(
|
|
70
|
+
sessionId: string,
|
|
71
|
+
): string | undefined {
|
|
72
|
+
if (typeof sessionId !== "string") return undefined;
|
|
73
|
+
const normalized = sessionId.trim();
|
|
74
|
+
return normalized && Buffer.byteLength(normalized, "utf8") <=
|
|
75
|
+
TELEGRAM_SESSION_ID_MAX_LENGTH
|
|
76
|
+
? normalized
|
|
77
|
+
: undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function createTelegramSessionKey(
|
|
81
|
+
sessionId: string,
|
|
82
|
+
): string | undefined {
|
|
83
|
+
const normalized = normalizeTelegramSessionId(sessionId);
|
|
84
|
+
return normalized
|
|
85
|
+
? createHash("sha256").update(normalized).digest("hex")
|
|
86
|
+
: undefined;
|
|
87
|
+
}
|
|
63
88
|
|
|
64
89
|
export function normalizeTelegramWorkspacePath(
|
|
65
90
|
cwd: string,
|
|
@@ -116,21 +141,39 @@ function createTelegramWorkspaceBindingIdentityWithKey(
|
|
|
116
141
|
cwd: string,
|
|
117
142
|
workspaceKey: string,
|
|
118
143
|
ordinal: number,
|
|
144
|
+
sessionId?: string,
|
|
119
145
|
): TelegramWorkspaceBindingIdentity | undefined {
|
|
120
146
|
const instanceSlot = createTelegramWorkspaceInstanceSlot(ordinal);
|
|
121
147
|
if (!instanceSlot) return undefined;
|
|
148
|
+
const normalizedSessionId = sessionId === undefined
|
|
149
|
+
? undefined
|
|
150
|
+
: normalizeTelegramSessionId(sessionId);
|
|
151
|
+
const sessionKey = normalizedSessionId
|
|
152
|
+
? createTelegramSessionKey(normalizedSessionId)
|
|
153
|
+
: undefined;
|
|
154
|
+
if (sessionId !== undefined && (!normalizedSessionId || !sessionKey)) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
const legacyBindingKey = instanceSlot === "a"
|
|
158
|
+
? workspaceKey
|
|
159
|
+
: `${workspaceKey}${instanceSlot}`;
|
|
122
160
|
return {
|
|
123
161
|
cwd,
|
|
124
162
|
workspaceKey,
|
|
163
|
+
...(normalizedSessionId && sessionKey
|
|
164
|
+
? { sessionId: normalizedSessionId, sessionKey }
|
|
165
|
+
: {}),
|
|
125
166
|
instanceSlot,
|
|
126
|
-
bindingKey:
|
|
127
|
-
|
|
167
|
+
bindingKey: sessionKey
|
|
168
|
+
? `${legacyBindingKey}-s-${sessionKey}`
|
|
169
|
+
: legacyBindingKey,
|
|
128
170
|
};
|
|
129
171
|
}
|
|
130
172
|
|
|
131
173
|
export function createTelegramWorkspaceBindingIdentity(
|
|
132
174
|
cwd: string,
|
|
133
175
|
ordinal = 0,
|
|
176
|
+
sessionId?: string,
|
|
134
177
|
): TelegramWorkspaceBindingIdentity | undefined {
|
|
135
178
|
const normalized = normalizeTelegramWorkspacePath(cwd);
|
|
136
179
|
const workspaceKey = normalized
|
|
@@ -141,6 +184,7 @@ export function createTelegramWorkspaceBindingIdentity(
|
|
|
141
184
|
normalized,
|
|
142
185
|
workspaceKey,
|
|
143
186
|
ordinal,
|
|
187
|
+
sessionId,
|
|
144
188
|
);
|
|
145
189
|
}
|
|
146
190
|
|
|
@@ -249,6 +293,10 @@ export interface TelegramThreadIdentityRecord {
|
|
|
249
293
|
export interface TelegramWorkspaceThreadBinding {
|
|
250
294
|
cwd: string;
|
|
251
295
|
workspaceKey: string;
|
|
296
|
+
/** Exact durable Pi session identity; absent only on legacy cwd-only bindings. */
|
|
297
|
+
sessionId?: string;
|
|
298
|
+
/** Full SHA-256 index component for session-qualified bindings. */
|
|
299
|
+
sessionKey?: string;
|
|
252
300
|
instanceSlot: string;
|
|
253
301
|
bindingKey: string;
|
|
254
302
|
target: TelegramTarget & { threadId: number };
|
|
@@ -453,7 +501,8 @@ export interface TelegramTopicTargetStore {
|
|
|
453
501
|
) => Promise<boolean>;
|
|
454
502
|
commitInactiveWorkspaceCleanup: (
|
|
455
503
|
expected: TelegramWorkspaceThreadBinding | {
|
|
456
|
-
cwd: string; workspaceKey: string;
|
|
504
|
+
cwd: string; workspaceKey: string; sessionId?: string; sessionKey?: string;
|
|
505
|
+
instanceSlot: string; slot: string; bindingKey: string;
|
|
457
506
|
target: { chatId: number; threadId: number }; inactiveSinceMs: number; bindingUpdatedAtMs: number;
|
|
458
507
|
},
|
|
459
508
|
isCurrent: () => boolean,
|
|
@@ -465,7 +514,7 @@ export interface TelegramTopicTargetStore {
|
|
|
465
514
|
) => TelegramWorkspaceExternalProtectionEvidence,
|
|
466
515
|
options?: { expectedRetirement?: TelegramWorkspaceRetirementIntent },
|
|
467
516
|
) => TelegramWorkspaceSlotOccupancySnapshot;
|
|
468
|
-
hasWorkspaceBinding: (cwd: string) => boolean;
|
|
517
|
+
hasWorkspaceBinding: (cwd: string, sessionId?: string) => boolean;
|
|
469
518
|
setWorkspaceDisplayTitle: (
|
|
470
519
|
expected: TelegramWorkspaceThreadBinding,
|
|
471
520
|
title: string,
|
|
@@ -478,6 +527,7 @@ export interface TelegramTopicTargetStore {
|
|
|
478
527
|
getWorkspaceBinding: (
|
|
479
528
|
cwd: string,
|
|
480
529
|
instanceSlot?: string,
|
|
530
|
+
sessionId?: string,
|
|
481
531
|
) => TelegramWorkspaceThreadBinding | undefined;
|
|
482
532
|
claimWorkspaceIdentity: (
|
|
483
533
|
cwd: string,
|
|
@@ -485,6 +535,7 @@ export interface TelegramTopicTargetStore {
|
|
|
485
535
|
previousInstanceId?: string,
|
|
486
536
|
options?: {
|
|
487
537
|
existingBindingOnly?: boolean;
|
|
538
|
+
sessionId?: string;
|
|
488
539
|
onCapacityUnavailable?: () => void;
|
|
489
540
|
},
|
|
490
541
|
) => TelegramWorkspaceBindingIdentity | undefined;
|
|
@@ -1124,9 +1175,12 @@ function cloneIdentityRecord(
|
|
|
1124
1175
|
}
|
|
1125
1176
|
|
|
1126
1177
|
function getWorkspaceBindingMapKey(
|
|
1127
|
-
binding: Pick<
|
|
1178
|
+
binding: Pick<
|
|
1179
|
+
TelegramWorkspaceThreadBinding,
|
|
1180
|
+
"cwd" | "instanceSlot" | "sessionId"
|
|
1181
|
+
>,
|
|
1128
1182
|
): string {
|
|
1129
|
-
return `${binding.cwd}\u0000${binding.instanceSlot}`;
|
|
1183
|
+
return `${binding.cwd}\u0000${binding.sessionId ?? ""}\u0000${binding.instanceSlot}`;
|
|
1130
1184
|
}
|
|
1131
1185
|
|
|
1132
1186
|
function normalizeWorkspaceBindingRecord(
|
|
@@ -1145,15 +1199,30 @@ function normalizeWorkspaceBindingRecord(
|
|
|
1145
1199
|
return undefined;
|
|
1146
1200
|
}
|
|
1147
1201
|
const cwd = normalizeTelegramWorkspacePath(record.cwd);
|
|
1202
|
+
const sessionId = typeof record.sessionId === "string"
|
|
1203
|
+
? normalizeTelegramSessionId(record.sessionId)
|
|
1204
|
+
: undefined;
|
|
1205
|
+
const sessionKey = typeof record.sessionKey === "string"
|
|
1206
|
+
? record.sessionKey
|
|
1207
|
+
: undefined;
|
|
1208
|
+
const hasSessionFields = record.sessionId !== undefined ||
|
|
1209
|
+
record.sessionKey !== undefined;
|
|
1210
|
+
const expectedSessionKey = sessionId
|
|
1211
|
+
? createTelegramSessionKey(sessionId)
|
|
1212
|
+
: undefined;
|
|
1213
|
+
const legacyBindingKey = record.instanceSlot === "a"
|
|
1214
|
+
? record.workspaceKey
|
|
1215
|
+
: `${record.workspaceKey}${record.instanceSlot}`;
|
|
1216
|
+
const expectedBindingKey = expectedSessionKey
|
|
1217
|
+
? `${legacyBindingKey}-s-${expectedSessionKey}`
|
|
1218
|
+
: legacyBindingKey;
|
|
1148
1219
|
if (
|
|
1149
1220
|
!cwd ||
|
|
1150
1221
|
cwd !== record.cwd ||
|
|
1151
1222
|
!record.workspaceKey ||
|
|
1152
1223
|
!/^[a-z]+$/u.test(record.instanceSlot) ||
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
? record.workspaceKey
|
|
1156
|
-
: `${record.workspaceKey}${record.instanceSlot}`)
|
|
1224
|
+
(hasSessionFields && (!sessionId || sessionKey !== expectedSessionKey)) ||
|
|
1225
|
+
record.bindingKey !== expectedBindingKey
|
|
1157
1226
|
) {
|
|
1158
1227
|
return undefined;
|
|
1159
1228
|
}
|
|
@@ -1191,6 +1260,7 @@ function normalizeWorkspaceBindingRecord(
|
|
|
1191
1260
|
return {
|
|
1192
1261
|
cwd,
|
|
1193
1262
|
workspaceKey: record.workspaceKey,
|
|
1263
|
+
...(sessionId && sessionKey ? { sessionId, sessionKey } : {}),
|
|
1194
1264
|
instanceSlot: record.instanceSlot,
|
|
1195
1265
|
bindingKey: record.bindingKey,
|
|
1196
1266
|
...(record.showSlotSuffix === true ? { showSlotSuffix: true } : {}),
|
|
@@ -1684,7 +1754,10 @@ export function createTelegramTopicTargetStore(
|
|
|
1684
1754
|
);
|
|
1685
1755
|
let workspaceClaims = new Map<
|
|
1686
1756
|
string,
|
|
1687
|
-
{
|
|
1757
|
+
{
|
|
1758
|
+
identity: TelegramWorkspaceBindingIdentity;
|
|
1759
|
+
instanceId: string;
|
|
1760
|
+
}
|
|
1688
1761
|
>();
|
|
1689
1762
|
let reservations: TelegramThreadReservation[] = [];
|
|
1690
1763
|
let pendingProvisions: TelegramThreadPendingProvision[] = [];
|
|
@@ -2404,8 +2477,16 @@ export function createTelegramTopicTargetStore(
|
|
|
2404
2477
|
if (workspaceRetirementCommitInFlight || !isCurrent()) return false;
|
|
2405
2478
|
const cleanupSnapshot = "bindingUpdatedAtMs" in expected ? expected : undefined;
|
|
2406
2479
|
const normalized = cleanupSnapshot ? undefined : normalizeWorkspaceBindingRecord(expected);
|
|
2480
|
+
const cleanupSessionId = cleanupSnapshot?.sessionId === undefined
|
|
2481
|
+
? undefined
|
|
2482
|
+
: normalizeTelegramSessionId(cleanupSnapshot.sessionId);
|
|
2483
|
+
const cleanupSessionKey = cleanupSessionId
|
|
2484
|
+
? createTelegramSessionKey(cleanupSessionId)
|
|
2485
|
+
: undefined;
|
|
2407
2486
|
if (cleanupSnapshot && (!cleanupSnapshot.cwd || !cleanupSnapshot.workspaceKey ||
|
|
2408
2487
|
!cleanupSnapshot.instanceSlot || !cleanupSnapshot.slot || !cleanupSnapshot.bindingKey ||
|
|
2488
|
+
((cleanupSnapshot.sessionId !== undefined || cleanupSnapshot.sessionKey !== undefined) &&
|
|
2489
|
+
(!cleanupSessionId || cleanupSnapshot.sessionKey !== cleanupSessionKey)) ||
|
|
2409
2490
|
!Number.isSafeInteger(cleanupSnapshot.inactiveSinceMs) ||
|
|
2410
2491
|
!Number.isSafeInteger(cleanupSnapshot.bindingUpdatedAtMs) ||
|
|
2411
2492
|
!Number.isSafeInteger(cleanupSnapshot.target.chatId) ||
|
|
@@ -2416,6 +2497,7 @@ export function createTelegramTopicTargetStore(
|
|
|
2416
2497
|
if (!binding) return isCurrent();
|
|
2417
2498
|
const exact = cleanupSnapshot ? binding.cwd === cleanupSnapshot.cwd &&
|
|
2418
2499
|
binding.workspaceKey === cleanupSnapshot.workspaceKey &&
|
|
2500
|
+
binding.sessionId === cleanupSessionId && binding.sessionKey === cleanupSessionKey &&
|
|
2419
2501
|
binding.instanceSlot === cleanupSnapshot.instanceSlot && binding.slot === cleanupSnapshot.slot &&
|
|
2420
2502
|
binding.bindingKey === cleanupSnapshot.bindingKey && targetMatches(binding.target, cleanupSnapshot.target) &&
|
|
2421
2503
|
binding.inactiveSinceMs === cleanupSnapshot.inactiveSinceMs &&
|
|
@@ -2612,10 +2694,17 @@ export function createTelegramTopicTargetStore(
|
|
|
2612
2694
|
: [...localReservedSlots, "invalid"];
|
|
2613
2695
|
return { bindings, reservedSlots };
|
|
2614
2696
|
},
|
|
2615
|
-
hasWorkspaceBinding(cwd) {
|
|
2697
|
+
hasWorkspaceBinding(cwd, sessionId) {
|
|
2616
2698
|
const normalizedCwd = normalizeTelegramWorkspacePath(cwd);
|
|
2617
|
-
|
|
2618
|
-
|
|
2699
|
+
const normalizedSessionId = sessionId === undefined
|
|
2700
|
+
? undefined
|
|
2701
|
+
: normalizeTelegramSessionId(sessionId);
|
|
2702
|
+
if (!normalizedCwd || (sessionId !== undefined && !normalizedSessionId)) {
|
|
2703
|
+
return false;
|
|
2704
|
+
}
|
|
2705
|
+
return Array.from(workspaceBindings.values()).some(
|
|
2706
|
+
(binding) => binding.cwd === normalizedCwd &&
|
|
2707
|
+
binding.sessionId === normalizedSessionId,
|
|
2619
2708
|
);
|
|
2620
2709
|
},
|
|
2621
2710
|
setWorkspaceDisplayTitle(expected, title) {
|
|
@@ -2651,18 +2740,29 @@ export function createTelegramTopicTargetStore(
|
|
|
2651
2740
|
}
|
|
2652
2741
|
return false;
|
|
2653
2742
|
},
|
|
2654
|
-
getWorkspaceBinding(cwd, instanceSlot = "a") {
|
|
2743
|
+
getWorkspaceBinding(cwd, instanceSlot = "a", sessionId) {
|
|
2655
2744
|
const normalizedCwd = normalizeTelegramWorkspacePath(cwd);
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
)
|
|
2745
|
+
const normalizedSessionId = sessionId === undefined
|
|
2746
|
+
? undefined
|
|
2747
|
+
: normalizeTelegramSessionId(sessionId);
|
|
2748
|
+
if (!normalizedCwd || !/^[a-z]+$/u.test(instanceSlot) ||
|
|
2749
|
+
(sessionId !== undefined && !normalizedSessionId)) return undefined;
|
|
2750
|
+
const mapKey = getWorkspaceBindingMapKey({
|
|
2751
|
+
cwd: normalizedCwd,
|
|
2752
|
+
instanceSlot,
|
|
2753
|
+
...(normalizedSessionId ? { sessionId: normalizedSessionId } : {}),
|
|
2754
|
+
});
|
|
2755
|
+
const binding = workspaceBindings.get(mapKey);
|
|
2660
2756
|
return binding ? cloneWorkspaceBinding(binding) : undefined;
|
|
2661
2757
|
},
|
|
2662
2758
|
claimWorkspaceIdentity(cwd, instanceId, previousInstanceId, options) {
|
|
2663
2759
|
if (workspaceRetirementCommitInFlight) return undefined;
|
|
2664
2760
|
const normalizedCwd = normalizeTelegramWorkspacePath(cwd);
|
|
2761
|
+
const normalizedSessionId = options?.sessionId === undefined
|
|
2762
|
+
? undefined
|
|
2763
|
+
: normalizeTelegramSessionId(options.sessionId);
|
|
2665
2764
|
if (!normalizedCwd || !instanceId ||
|
|
2765
|
+
(options?.sessionId !== undefined && !normalizedSessionId) ||
|
|
2666
2766
|
hasWorkspaceRetirementConflict({ cwd: normalizedCwd })) return undefined;
|
|
2667
2767
|
const externalReservedSlots = captureExternalReservedSlots();
|
|
2668
2768
|
if (!externalReservedSlots) {
|
|
@@ -2674,7 +2774,9 @@ export function createTelegramTopicTargetStore(
|
|
|
2674
2774
|
);
|
|
2675
2775
|
for (const claim of workspaceClaims.values()) {
|
|
2676
2776
|
if (claim.instanceId !== instanceId) continue;
|
|
2677
|
-
if (claim.identity.cwd !== normalizedCwd ||
|
|
2777
|
+
if (claim.identity.cwd !== normalizedCwd ||
|
|
2778
|
+
claim.identity.sessionId !== normalizedSessionId ||
|
|
2779
|
+
!claim.identity.slot ||
|
|
2678
2780
|
externalReservedSlotKeys.includes(claim.identity.slot.toLowerCase())) {
|
|
2679
2781
|
return undefined;
|
|
2680
2782
|
}
|
|
@@ -2686,11 +2788,13 @@ export function createTelegramTopicTargetStore(
|
|
|
2686
2788
|
}
|
|
2687
2789
|
const workspaceKey = resolveWorkspaceKey(normalizedCwd);
|
|
2688
2790
|
if (!workspaceKey) return undefined;
|
|
2689
|
-
let legacyRecord =
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2791
|
+
let legacyRecord = normalizedSessionId
|
|
2792
|
+
? undefined
|
|
2793
|
+
: findLegacyWorkspaceMigrationRecord(
|
|
2794
|
+
normalizedCwd,
|
|
2795
|
+
instanceId,
|
|
2796
|
+
previousInstanceId,
|
|
2797
|
+
);
|
|
2694
2798
|
const legacyTarget = legacyRecord?.target;
|
|
2695
2799
|
const targetBinding = legacyTarget
|
|
2696
2800
|
? Array.from(workspaceBindings.values()).find(
|
|
@@ -2772,12 +2876,16 @@ export function createTelegramTopicTargetStore(
|
|
|
2772
2876
|
}
|
|
2773
2877
|
if (!slot || !/^[A-Z]$/u.test(slot)) return undefined;
|
|
2774
2878
|
const claimedIdentity = { ...identity, slot };
|
|
2775
|
-
workspaceClaims.set(mapKey, {
|
|
2879
|
+
workspaceClaims.set(mapKey, {
|
|
2880
|
+
identity: claimedIdentity,
|
|
2881
|
+
instanceId,
|
|
2882
|
+
});
|
|
2776
2883
|
return { ...claimedIdentity };
|
|
2777
2884
|
};
|
|
2778
2885
|
if (options?.existingBindingOnly) {
|
|
2779
2886
|
const candidates = Array.from(workspaceBindings.values())
|
|
2780
|
-
.filter((binding) => binding.cwd === normalizedCwd
|
|
2887
|
+
.filter((binding) => binding.cwd === normalizedCwd &&
|
|
2888
|
+
binding.sessionId === normalizedSessionId)
|
|
2781
2889
|
.sort((left, right) =>
|
|
2782
2890
|
left.instanceSlot.length - right.instanceSlot.length ||
|
|
2783
2891
|
left.instanceSlot.localeCompare(right.instanceSlot),
|
|
@@ -2786,6 +2894,9 @@ export function createTelegramTopicTargetStore(
|
|
|
2786
2894
|
const claimed = claimIdentity({
|
|
2787
2895
|
cwd: binding.cwd,
|
|
2788
2896
|
workspaceKey: binding.workspaceKey,
|
|
2897
|
+
...(binding.sessionId && binding.sessionKey
|
|
2898
|
+
? { sessionId: binding.sessionId, sessionKey: binding.sessionKey }
|
|
2899
|
+
: {}),
|
|
2789
2900
|
instanceSlot: binding.instanceSlot,
|
|
2790
2901
|
bindingKey: binding.bindingKey,
|
|
2791
2902
|
});
|
|
@@ -2811,6 +2922,7 @@ export function createTelegramTopicTargetStore(
|
|
|
2811
2922
|
normalizedCwd,
|
|
2812
2923
|
workspaceKey,
|
|
2813
2924
|
ordinal,
|
|
2925
|
+
normalizedSessionId,
|
|
2814
2926
|
);
|
|
2815
2927
|
if (!identity) return undefined;
|
|
2816
2928
|
const mapKey = getWorkspaceBindingMapKey(identity);
|
|
@@ -2850,6 +2962,8 @@ export function createTelegramTopicTargetStore(
|
|
|
2850
2962
|
const next = normalizeWorkspaceBindingRecord(binding);
|
|
2851
2963
|
if (next && hasWorkspaceRetirementConflict(next)) return undefined;
|
|
2852
2964
|
if (!next) return undefined;
|
|
2965
|
+
const nextMapKey = getWorkspaceBindingMapKey(next);
|
|
2966
|
+
const claim = workspaceClaims.get(nextMapKey);
|
|
2853
2967
|
if (next.slot && Array.from(workspaceBindings.values()).some((existing) =>
|
|
2854
2968
|
existing.bindingKey !== next.bindingKey && existing.slot === next.slot,
|
|
2855
2969
|
)) return undefined;
|
|
@@ -2861,9 +2975,11 @@ export function createTelegramTopicTargetStore(
|
|
|
2861
2975
|
return undefined;
|
|
2862
2976
|
}
|
|
2863
2977
|
}
|
|
2864
|
-
const nextMapKey = getWorkspaceBindingMapKey(next);
|
|
2865
2978
|
const previous = workspaceBindings.get(nextMapKey);
|
|
2866
2979
|
if (previous?.showSlotSuffix) next.showSlotSuffix = true;
|
|
2980
|
+
if (previous?.threadName && !next.threadName) {
|
|
2981
|
+
next.threadName = previous.threadName;
|
|
2982
|
+
}
|
|
2867
2983
|
if (previous?.manualThreadName && !next.manualThreadName) {
|
|
2868
2984
|
next.manualThreadName = previous.manualThreadName;
|
|
2869
2985
|
}
|
|
@@ -2884,7 +3000,6 @@ export function createTelegramTopicTargetStore(
|
|
|
2884
3000
|
if (previous.displayTitle) next.displayTitle = previous.displayTitle;
|
|
2885
3001
|
if (previous.inactiveSinceMs !== undefined) next.inactiveSinceMs = previous.inactiveSinceMs;
|
|
2886
3002
|
}
|
|
2887
|
-
const claim = workspaceClaims.get(nextMapKey);
|
|
2888
3003
|
if (claimInstanceId) {
|
|
2889
3004
|
if (claim?.instanceId !== claimInstanceId) return undefined;
|
|
2890
3005
|
if (next.slot !== undefined && claim.identity.slot !== next.slot) return undefined;
|
|
@@ -3460,6 +3575,7 @@ export interface TelegramPromoteFollowerBindingToLeaderDeps {
|
|
|
3460
3575
|
store: TelegramTopicTargetStore;
|
|
3461
3576
|
instanceId: string;
|
|
3462
3577
|
cwd?: string;
|
|
3578
|
+
sessionId?: string;
|
|
3463
3579
|
telegramProfile?: string;
|
|
3464
3580
|
target?: TelegramTarget;
|
|
3465
3581
|
slot?: string;
|
|
@@ -3483,11 +3599,15 @@ export async function promoteTelegramFollowerBindingToLeader(
|
|
|
3483
3599
|
);
|
|
3484
3600
|
const workspaceIdentity = deps.cwd
|
|
3485
3601
|
? deps.store.claimWorkspaceIdentity(deps.cwd, deps.instanceId,
|
|
3486
|
-
existing?.instanceId, {
|
|
3602
|
+
existing?.instanceId, {
|
|
3603
|
+
existingBindingOnly: true,
|
|
3604
|
+
sessionId: deps.sessionId,
|
|
3605
|
+
})
|
|
3487
3606
|
: undefined;
|
|
3488
3607
|
const workspaceBinding = workspaceIdentity
|
|
3489
3608
|
? deps.store.getWorkspaceBinding(
|
|
3490
3609
|
workspaceIdentity.cwd, workspaceIdentity.instanceSlot,
|
|
3610
|
+
workspaceIdentity.sessionId,
|
|
3491
3611
|
)
|
|
3492
3612
|
: undefined;
|
|
3493
3613
|
const exactWorkspaceBinding = workspaceBinding &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-telegram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -30,15 +30,19 @@
|
|
|
30
30
|
"test": "node --experimental-strip-types --test --test-concurrency=4 --test-reporter=dot tests/*.test.ts",
|
|
31
31
|
"test:verbose": "node --experimental-strip-types --test --test-reporter=spec tests/*.test.ts",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
|
+
"build": "node scripts/build-dist.mjs",
|
|
34
|
+
"prepack": "npm run build",
|
|
35
|
+
"check": "node -e \"await import('./dist/pi-telegram/index.js'); console.log('pi-telegram: extension import ok')\"",
|
|
33
36
|
"audit": "npm audit --omit=peer",
|
|
34
37
|
"audit:host": "npm audit",
|
|
35
38
|
"pack:check": "npm pack --dry-run",
|
|
36
|
-
"validate": "npm run typecheck && npm test && npm run audit && npm run pack:check"
|
|
39
|
+
"validate": "npm run build && npm run typecheck && npm test && npm run audit && npm run check && npm run pack:check"
|
|
37
40
|
},
|
|
38
41
|
"files": [
|
|
39
42
|
"index.ts",
|
|
40
43
|
"api/",
|
|
41
44
|
"lib/",
|
|
45
|
+
"dist/",
|
|
42
46
|
"README.md",
|
|
43
47
|
"AGENTS.md",
|
|
44
48
|
"BACKLOG.md",
|
|
@@ -49,23 +53,62 @@
|
|
|
49
53
|
"screenshot.png"
|
|
50
54
|
],
|
|
51
55
|
"exports": {
|
|
52
|
-
".":
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"./
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"./
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
".": {
|
|
57
|
+
"types": "./dist/index.d.ts",
|
|
58
|
+
"default": "./dist/index.js"
|
|
59
|
+
},
|
|
60
|
+
"./inbound": {
|
|
61
|
+
"types": "./dist/api/inbound.d.ts",
|
|
62
|
+
"default": "./dist/api/inbound.js"
|
|
63
|
+
},
|
|
64
|
+
"./outbound": {
|
|
65
|
+
"types": "./dist/api/outbound.d.ts",
|
|
66
|
+
"default": "./dist/api/outbound.js"
|
|
67
|
+
},
|
|
68
|
+
"./delivery": {
|
|
69
|
+
"types": "./dist/api/delivery.d.ts",
|
|
70
|
+
"default": "./dist/api/delivery.js"
|
|
71
|
+
},
|
|
72
|
+
"./activity": {
|
|
73
|
+
"types": "./dist/api/activity.d.ts",
|
|
74
|
+
"default": "./dist/api/activity.js"
|
|
75
|
+
},
|
|
76
|
+
"./updates": {
|
|
77
|
+
"types": "./dist/api/updates.d.ts",
|
|
78
|
+
"default": "./dist/api/updates.js"
|
|
79
|
+
},
|
|
80
|
+
"./commands": {
|
|
81
|
+
"types": "./dist/api/commands.d.ts",
|
|
82
|
+
"default": "./dist/api/commands.js"
|
|
83
|
+
},
|
|
84
|
+
"./sections": {
|
|
85
|
+
"types": "./dist/api/sections.d.ts",
|
|
86
|
+
"default": "./dist/api/sections.js"
|
|
87
|
+
},
|
|
88
|
+
"./status": {
|
|
89
|
+
"types": "./dist/api/status.d.ts",
|
|
90
|
+
"default": "./dist/api/status.js"
|
|
91
|
+
},
|
|
92
|
+
"./voice": {
|
|
93
|
+
"types": "./dist/api/voice.d.ts",
|
|
94
|
+
"default": "./dist/api/voice.js"
|
|
95
|
+
},
|
|
96
|
+
"./keyboard": {
|
|
97
|
+
"types": "./dist/api/keyboard.d.ts",
|
|
98
|
+
"default": "./dist/api/keyboard.js"
|
|
99
|
+
}
|
|
63
100
|
},
|
|
64
101
|
"pi": {
|
|
65
102
|
"extensions": [
|
|
103
|
+
"./dist/pi-telegram/index.js"
|
|
104
|
+
],
|
|
105
|
+
"sourceExtensions": [
|
|
66
106
|
"./index.ts"
|
|
67
107
|
],
|
|
68
108
|
"skills": [
|
|
109
|
+
"./dist/skills"
|
|
110
|
+
],
|
|
111
|
+
"sourceSkills": [
|
|
69
112
|
"./skills"
|
|
70
113
|
],
|
|
71
114
|
"image": "https://raw.githubusercontent.com/llblab/pi-telegram/main/screenshot.png"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds the distributive JavaScript, declarations, Pi entrypoint, and Skills.
|
|
5
|
+
* Usage: node scripts/build-dist.mjs
|
|
6
|
+
*
|
|
7
|
+
* The build replaces `dist`, compiles with `tsconfig.build.json`, then copies
|
|
8
|
+
* package-owned runtime assets. It does not publish, pack, or modify sources.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { cpSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
|
|
15
|
+
function run(command, args) {
|
|
16
|
+
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
17
|
+
if (result.error) throw result.error;
|
|
18
|
+
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
rmSync("dist", { recursive: true, force: true });
|
|
22
|
+
mkdirSync("dist", { recursive: true });
|
|
23
|
+
|
|
24
|
+
run(process.execPath, [
|
|
25
|
+
join("node_modules", "typescript", "bin", "tsc"),
|
|
26
|
+
"-p",
|
|
27
|
+
"tsconfig.build.json",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
mkdirSync(join("dist", "pi-telegram"), { recursive: true });
|
|
31
|
+
writeFileSync(
|
|
32
|
+
join("dist", "pi-telegram", "index.js"),
|
|
33
|
+
'export { default } from "../index.js";\n',
|
|
34
|
+
"utf8",
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
cpSync("skills", join("dist", "skills"), { recursive: true });
|
|
38
|
+
cpSync("package.json", join("dist", "package.json"));
|
|
39
|
+
cpSync(
|
|
40
|
+
join("lib", "generative-app-worker.mjs"),
|
|
41
|
+
join("dist", "lib", "generative-app-worker.mjs"),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
run(process.execPath, ["--check", join("dist", "pi-telegram", "index.js")]);
|
package/scripts/measure-bus.mjs
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Counts isolated synchronous bus-registry work at representative sizes.
|
|
3
|
+
* Usage: node --experimental-strip-types scripts/measure-bus.mjs
|
|
4
|
+
*
|
|
5
|
+
* This assertion-backed fixture instruments Map operations only. It does not
|
|
6
|
+
* open sockets, read configured profiles, call Telegram, or measure latency.
|
|
7
|
+
*/
|
|
8
|
+
|
|
2
9
|
import assert from "node:assert/strict";
|
|
3
10
|
import { createTelegramBusFollowerRegistry, createTelegramBusProtocolIdentity } from "../lib/bus.ts";
|
|
4
11
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Measures isolated Thread-store filesystem work at representative sizes.
|
|
3
|
+
* Usage: node --experimental-strip-types scripts/measure-workspace.mjs
|
|
4
|
+
*
|
|
5
|
+
* This assertion-backed fixture uses and removes its own temporary directory.
|
|
6
|
+
* It never reads configured profiles, calls Telegram, or measures latency.
|
|
7
|
+
*/
|
|
8
|
+
|
|
2
9
|
import assert from "node:assert/strict";
|
|
3
10
|
import fs from "node:fs/promises";
|
|
4
11
|
import { syncBuiltinESMExports } from "node:module";
|
|
@@ -97,7 +97,7 @@ ordinary prompt → model-mediated interpretation or explanation
|
|
|
97
97
|
- Keep credentials and unrelated private state out of source, state, output, and diagnostics.
|
|
98
98
|
- Route consequences requiring contextual judgment through an ordinary model prompt.
|
|
99
99
|
- Fail closed on unavailable owners, stale actions, malformed state, absent methods, process failures, or uncertain effects.
|
|
100
|
-
-
|
|
100
|
+
- Treat `refreshAfterMs` validation, output-only `refresh`, scheduling after a successful hinted explicit `telegram_bind` delivery, same-handle rescheduling after a current-surface bound action, Delivery edit-failure classification, known unavailable-message invalidation with bounded classified diagnostics, session/runtime live-surface cancellation, and exact direct classic/leader/authenticated-follower target retention as implemented. The Bot API has no ordinary private-message deletion update; `deleted_business_messages` is limited to connected business accounts. Do not claim removal, voice output, proactive deletion-update invalidation, or other behavior still marked incomplete in the runtime document.
|
|
101
101
|
|
|
102
102
|
## Validation
|
|
103
103
|
|
package/skills/show-me/SKILL.md
CHANGED
|
@@ -10,7 +10,7 @@ Help the user understand the current topic. Skip the preamble and keep prose bri
|
|
|
10
10
|
- Infer the subject from the conversation and honor an explicitly requested format. With plain `show me`, choose the form that best explains the subject.
|
|
11
11
|
- Markdown in the reply is a complete output format for both chat and terminal surfaces. Use headings, emphasis, lists, and focused code blocks to explain outcomes, comparisons, and reasoning; add a diagram when relationships need one.
|
|
12
12
|
- `Show me markdown` requests a rendered Markdown reply. Create a Markdown file only when the user asks for a saved document or file artifact.
|
|
13
|
-
- `Show me html` requests a focused HTML file.
|
|
13
|
+
- `Show me html` requests a focused HTML file. Prefer an accessible, art-directed interactive infographic when timelines, comparisons, layers, or progressive disclosure clarify the subject; otherwise stay static. Preserve truthful labels and reduced motion. Save and deliver the artifact through the active environment; open it locally when requested.
|
|
14
14
|
|
|
15
15
|
### Surface Routing
|
|
16
16
|
|
|
@@ -60,7 +60,7 @@ Every enabled button has a self-contained prompt and an optional selection style
|
|
|
60
60
|
- `{label|prompt}` separates visible label from queued prompt.
|
|
61
61
|
- `{label|prompt|selected_style}` and `{|prompt|selected_style}` accept `primary`, `success`, or `danger`.
|
|
62
62
|
- Fourth-position `1`/`true` disables, `0`/`false` enables; omission means enabled. JSON uses boolean `disabled`. `{|Next||1}` omits label/style; `{Next|||1}` omits prompt/style; `{|||1}` is blank (Telegram receives a non-breaking space). Prefer meaningful labels and retain a useful enabled action. Enabled CML requires a prompt. Disabled controls stay visible but have no callback, queued prompt, or bound-method invocation.
|
|
63
|
-
- Top-level cells form vertical rows;
|
|
63
|
+
- Top-level cells form vertical rows; inside a fenced block, adjacent top-level JSON/CML objects may omit the outer array and commas. One nested row groups horizontal peers.
|
|
64
64
|
- Prefer one matrix per related group. Fenced blocks stay in place in Rich mode; HTML compatibility moves them to the footer. Native rows allow eight buttons. Malformed/oversized/unclosed blocks activate nothing; drafts hide them. Outer code fences and quoted/indented examples remain literal.
|
|
65
65
|
- Both placements share prompt/app routing. In-body clicks acknowledge without recoloring; selected-style highlighting remains footer-only.
|
|
66
66
|
|