@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/extension.ts
CHANGED
|
@@ -64,6 +64,7 @@ const telegramBusProtocolIdentity =
|
|
|
64
64
|
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT,
|
|
65
65
|
Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME,
|
|
66
66
|
Bus.TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE,
|
|
67
|
+
Bus.TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT,
|
|
67
68
|
]);
|
|
68
69
|
|
|
69
70
|
// --- Extension Runtime ---
|
|
@@ -672,11 +673,15 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
672
673
|
getHandlers: configStore.getOutboundHandlers,
|
|
673
674
|
recordRuntimeEvent,
|
|
674
675
|
});
|
|
676
|
+
const generativeAppLiveSurfaceBinding =
|
|
677
|
+
Bindings.createTelegramGenerativeAppLiveSurfaceBinding();
|
|
675
678
|
const invokeGenerativeAppBoundButtonAction =
|
|
676
679
|
Bindings.createTelegramGenerativeAppBoundButtonActionInvoker({
|
|
677
680
|
agentDir: Paths.resolveAgentDir(),
|
|
678
681
|
assertExecutionCurrent: Updates.assertTelegramUpdateExecutionCurrent,
|
|
679
682
|
getExecutionFence: Updates.getTelegramUpdateExecutionFence,
|
|
683
|
+
getActiveProfileName: configStore.getActiveProfileName,
|
|
684
|
+
getLiveSurfaceRuntime: generativeAppLiveSurfaceBinding.get,
|
|
680
685
|
planOutput: planGenerativeAppOutput,
|
|
681
686
|
sendMarkdownReply,
|
|
682
687
|
editInteractiveMessage,
|
|
@@ -861,6 +866,18 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
861
866
|
return threadStore.getBotState().threadMode === "enabled"
|
|
862
867
|
? Config.resolveTelegramThreadDisplayMode(configStore.get()) : undefined;
|
|
863
868
|
},
|
|
869
|
+
getThreadDisplayPreview(mode) {
|
|
870
|
+
const bindings = threadStore.listWorkspaceBindings();
|
|
871
|
+
return [...ThreadDisplay.resolveTelegramWorkspaceDisplayNames(
|
|
872
|
+
bindings,
|
|
873
|
+
mode,
|
|
874
|
+
ThreadDisplay.resolveTelegramLiveWorkspaceBindingKeys(
|
|
875
|
+
bindings,
|
|
876
|
+
threadStore.getActiveByInstanceId(telegramInstanceId)?.target,
|
|
877
|
+
telegramBusFollowerRegistry.list(),
|
|
878
|
+
),
|
|
879
|
+
).values()];
|
|
880
|
+
},
|
|
864
881
|
async setThreadDisplayMode(mode) {
|
|
865
882
|
try {
|
|
866
883
|
await ThreadDisplay.applyTelegramThreadDisplaySetting(mode, {
|
|
@@ -1124,6 +1141,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1124
1141
|
topicTargetStore: threadStore,
|
|
1125
1142
|
instanceId: telegramInstanceId,
|
|
1126
1143
|
getActiveProfileName: configStore.getActiveProfileName,
|
|
1144
|
+
getSessionId: Pi.getExtensionContextSessionId,
|
|
1127
1145
|
getWorkspaceAdmission: workspaceAdmissionRuntime.resolve,
|
|
1128
1146
|
async startLeader(ctx, election, onAcquired): Promise<boolean> {
|
|
1129
1147
|
const result = await lockedPollingRuntime.start(ctx, {
|
|
@@ -1202,6 +1220,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1202
1220
|
getProcessBirthId() {
|
|
1203
1221
|
return telegramQueueProcessBirthId;
|
|
1204
1222
|
},
|
|
1223
|
+
getSessionId: Pi.getExtensionContextSessionId,
|
|
1205
1224
|
getSessionGeneration: telegramSessionContextStore.getGeneration,
|
|
1206
1225
|
async onRegistered(ctx) {
|
|
1207
1226
|
await followerAdmissionLifecycleRuntime.onTransportChanged(ctx);
|
|
@@ -1318,6 +1337,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1318
1337
|
getAllowedUserId: configStore.getAllowedUserId,
|
|
1319
1338
|
instanceId: telegramInstanceId,
|
|
1320
1339
|
getCwd: Pi.getExtensionContextCwd,
|
|
1340
|
+
getSessionId: Pi.getExtensionContextSessionId,
|
|
1321
1341
|
getTelegramProfile: configStore.getActiveProfileName,
|
|
1322
1342
|
getThreadDisplayMode() {
|
|
1323
1343
|
return Config.resolveTelegramThreadDisplayMode(configStore.get());
|
|
@@ -1412,7 +1432,10 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1412
1432
|
});
|
|
1413
1433
|
},
|
|
1414
1434
|
hasRememberedWorkspaceBinding(ctx) {
|
|
1415
|
-
return threadStore.hasWorkspaceBinding(
|
|
1435
|
+
return threadStore.hasWorkspaceBinding(
|
|
1436
|
+
Pi.getExtensionContextCwd(ctx),
|
|
1437
|
+
Pi.getExtensionContextSessionId(ctx),
|
|
1438
|
+
);
|
|
1416
1439
|
},
|
|
1417
1440
|
suspendLiveThreadTarget: telegramBusLeaderState.clear,
|
|
1418
1441
|
stopFollowerRegistration: telegramBusFollowerRegistration.stop,
|
|
@@ -1797,6 +1820,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1797
1820
|
getDefaultChatId: proactivePushChatIdGetter,
|
|
1798
1821
|
getDefaultTarget: proactivePushTargetGetter,
|
|
1799
1822
|
...agentMessageToolRoutingRuntime,
|
|
1823
|
+
setGenerativeAppLiveSurfaceRuntime: generativeAppLiveSurfaceBinding.set,
|
|
1800
1824
|
updateStatus,
|
|
1801
1825
|
recordRuntimeEvent,
|
|
1802
1826
|
});
|
|
@@ -1825,6 +1849,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
1825
1849
|
deferredQueueDispatchRuntime,
|
|
1826
1850
|
modelContextAvailabilityRuntime,
|
|
1827
1851
|
disconnectOnQuit: cleanupTelegramThreadForSessionRestart,
|
|
1852
|
+
shutdownGenerativeAppLiveSurfaces: generativeAppLiveSurfaceBinding.shutdown,
|
|
1828
1853
|
resolveAutomaticThreadCleanupEnabled:
|
|
1829
1854
|
configControls.resolveAutomaticThreadCleanupEnabled,
|
|
1830
1855
|
buttonActionStore,
|
package/lib/generative-apps.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Owns Generative App identity, installation, invocation, state history, and telegram_bind
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { randomUUID } from "node:crypto";
|
|
7
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import {
|
|
9
9
|
appendFile,
|
|
10
10
|
copyFile,
|
|
@@ -33,6 +33,8 @@ const GENERATIVE_APP_LOCK_WAIT_MS = 12_000;
|
|
|
33
33
|
const GENERATIVE_APP_RUN_MAX_TIMEOUT_MS = 30_000;
|
|
34
34
|
const GENERATIVE_APP_RUN_MAX_ARGS = 64;
|
|
35
35
|
const GENERATIVE_APP_RUN_MAX_STREAM_BYTES = 64 * 1024;
|
|
36
|
+
export const GENERATIVE_APP_MIN_REFRESH_AFTER_MS = 2_000;
|
|
37
|
+
export const GENERATIVE_APP_MAX_REFRESH_AFTER_MS = 24 * 60 * 60 * 1_000;
|
|
36
38
|
|
|
37
39
|
export type GenerativeAppJsonValue =
|
|
38
40
|
| null
|
|
@@ -66,6 +68,7 @@ export interface GenerativeAppMethodContext {
|
|
|
66
68
|
|
|
67
69
|
export interface GenerativeAppMethodResult {
|
|
68
70
|
output: string;
|
|
71
|
+
refreshAfterMs?: number;
|
|
69
72
|
state?: GenerativeAppJsonValue;
|
|
70
73
|
viewMode?: "new" | "edit";
|
|
71
74
|
}
|
|
@@ -82,6 +85,7 @@ export interface GenerativeAppInvocationResult {
|
|
|
82
85
|
output: string;
|
|
83
86
|
app: string;
|
|
84
87
|
revision: number;
|
|
88
|
+
refreshAfterMs?: number;
|
|
85
89
|
stateChanged: boolean;
|
|
86
90
|
viewMode: "new" | "edit";
|
|
87
91
|
}
|
|
@@ -104,7 +108,89 @@ export interface GenerativeAppRuntimeOptions {
|
|
|
104
108
|
methodTimeoutMs?: number;
|
|
105
109
|
}
|
|
106
110
|
|
|
111
|
+
export interface GenerativeAppLiveSurfaceFrame<THandle> {
|
|
112
|
+
digest: string;
|
|
113
|
+
handle: THandle;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface GenerativeAppLiveSurface<THandle> {
|
|
117
|
+
app: string;
|
|
118
|
+
appGeneration: string;
|
|
119
|
+
appRevision: number;
|
|
120
|
+
handle: THandle;
|
|
121
|
+
initialDigest: string;
|
|
122
|
+
key: string;
|
|
123
|
+
refreshAfterMs: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface GenerativeAppLiveSurfaceRuntime<THandle> {
|
|
127
|
+
open: (surface: GenerativeAppLiveSurface<THandle>) => void;
|
|
128
|
+
cancel: (key: string) => void;
|
|
129
|
+
take: (key: string) => GenerativeAppLiveSurface<THandle> | undefined;
|
|
130
|
+
resume: (
|
|
131
|
+
surface: GenerativeAppLiveSurface<THandle>,
|
|
132
|
+
result: GenerativeAppInvocationResult,
|
|
133
|
+
) => Promise<void>;
|
|
134
|
+
refreshNow: (key: string) => Promise<void>;
|
|
135
|
+
shutdown: () => void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface GenerativeAppLiveSurfaceRuntimeDeps<THandle> {
|
|
139
|
+
agentDir: string;
|
|
140
|
+
isCurrent: (surface: GenerativeAppLiveSurface<THandle>) => boolean;
|
|
141
|
+
plan: (
|
|
142
|
+
result: GenerativeAppInvocationResult,
|
|
143
|
+
handle: THandle,
|
|
144
|
+
) => GenerativeAppLiveSurfaceFrame<THandle>;
|
|
145
|
+
edit: (
|
|
146
|
+
frame: GenerativeAppLiveSurfaceFrame<THandle>,
|
|
147
|
+
) => Promise<THandle>;
|
|
148
|
+
classifyEditError?: (error: unknown) =>
|
|
149
|
+
| { kind: "retry"; retryAfterMs?: number }
|
|
150
|
+
| { kind: "terminal" | "unavailable" | "unknown" };
|
|
151
|
+
recordRuntimeEvent?: (
|
|
152
|
+
category: string,
|
|
153
|
+
error: unknown,
|
|
154
|
+
details?: Record<string, unknown>,
|
|
155
|
+
) => void;
|
|
156
|
+
setTimer?: (callback: () => void, delayMs: number) => ReturnType<typeof setTimeout>;
|
|
157
|
+
clearTimer?: (timer: ReturnType<typeof setTimeout>) => void;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface TelegramBindDeliveryHandle {
|
|
161
|
+
readonly target: { chatId: number; threadId?: number };
|
|
162
|
+
readonly messageIds: readonly number[];
|
|
163
|
+
readonly generation: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface TelegramBindLiveHandle {
|
|
167
|
+
delivery: TelegramBindDeliveryHandle;
|
|
168
|
+
view?: { text: string; parseMode: "markdown"; replyMarkup?: unknown };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function getTelegramBindLiveSurfaceKey(
|
|
172
|
+
app: string,
|
|
173
|
+
profile: string,
|
|
174
|
+
target: { chatId: number; threadId?: number },
|
|
175
|
+
): string {
|
|
176
|
+
return `${app}/${profile}/${target.chatId}/${target.threadId ?? 0}`;
|
|
177
|
+
}
|
|
178
|
+
|
|
107
179
|
export interface TelegramBindToolRegistrationDeps extends GenerativeAppRuntimeOptions {
|
|
180
|
+
getActiveProfileName?: () => string | undefined;
|
|
181
|
+
liveSurfaceSetTimer?: GenerativeAppLiveSurfaceRuntimeDeps<unknown>["setTimer"];
|
|
182
|
+
liveSurfaceClearTimer?: GenerativeAppLiveSurfaceRuntimeDeps<unknown>["clearTimer"];
|
|
183
|
+
setLiveSurfaceRuntime?: (
|
|
184
|
+
runtime: GenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle> | undefined,
|
|
185
|
+
) => void;
|
|
186
|
+
isDeliveryHandleCurrent?: (handle: TelegramBindDeliveryHandle) => boolean;
|
|
187
|
+
editView?: (
|
|
188
|
+
handle: TelegramBindDeliveryHandle,
|
|
189
|
+
view: { text: string; parseMode: "markdown"; replyMarkup?: unknown },
|
|
190
|
+
) => Promise<
|
|
191
|
+
| { ok: true; value: TelegramBindDeliveryHandle }
|
|
192
|
+
| { ok: false; reason: string; message: string; retryAfterMs?: number }
|
|
193
|
+
>;
|
|
108
194
|
getActiveTurn?: () =>
|
|
109
195
|
| {
|
|
110
196
|
chatId: number;
|
|
@@ -125,6 +211,13 @@ export interface TelegramBindToolRegistrationDeps extends GenerativeAppRuntimeOp
|
|
|
125
211
|
target?: { chatId: number; threadId?: number };
|
|
126
212
|
},
|
|
127
213
|
) => Promise<number | undefined>;
|
|
214
|
+
sendView?: (
|
|
215
|
+
view: { text: string; parseMode: "markdown"; replyMarkup?: unknown },
|
|
216
|
+
options: { scope: { kind: "active-turn" }; replyToMessageId: number },
|
|
217
|
+
) => Promise<
|
|
218
|
+
| { ok: true; value: TelegramBindDeliveryHandle }
|
|
219
|
+
| { ok: false; reason?: string; message: string }
|
|
220
|
+
>;
|
|
128
221
|
recordRuntimeEvent?: (
|
|
129
222
|
category: string,
|
|
130
223
|
error: unknown,
|
|
@@ -365,8 +458,24 @@ function normalizeMethodResult(value: unknown): GenerativeAppMethodResult {
|
|
|
365
458
|
if (viewMode !== "new" && viewMode !== "edit") {
|
|
366
459
|
throw new Error("Generative App viewMode must be new or edit.");
|
|
367
460
|
}
|
|
461
|
+
let refreshAfterMs: number | undefined;
|
|
462
|
+
if (Object.hasOwn(result, "refreshAfterMs")) {
|
|
463
|
+
if (
|
|
464
|
+
typeof result.refreshAfterMs !== "number" ||
|
|
465
|
+
!Number.isFinite(result.refreshAfterMs) ||
|
|
466
|
+
!Number.isInteger(result.refreshAfterMs) ||
|
|
467
|
+
result.refreshAfterMs <= 0
|
|
468
|
+
) {
|
|
469
|
+
throw new Error("Generative App refreshAfterMs must be a finite positive integer.");
|
|
470
|
+
}
|
|
471
|
+
refreshAfterMs = Math.min(
|
|
472
|
+
GENERATIVE_APP_MAX_REFRESH_AFTER_MS,
|
|
473
|
+
Math.max(GENERATIVE_APP_MIN_REFRESH_AFTER_MS, result.refreshAfterMs),
|
|
474
|
+
);
|
|
475
|
+
}
|
|
368
476
|
return {
|
|
369
477
|
output: result.output,
|
|
478
|
+
...(refreshAfterMs !== undefined ? { refreshAfterMs } : {}),
|
|
370
479
|
...(Object.hasOwn(result, "state")
|
|
371
480
|
? { state: assertJsonValue(result.state, "Generative App state") }
|
|
372
481
|
: {}),
|
|
@@ -595,6 +704,9 @@ async function invokeMethod(options: {
|
|
|
595
704
|
if (options.method === "init" && result.state === undefined) {
|
|
596
705
|
throw new Error("Generative App init must return state.");
|
|
597
706
|
}
|
|
707
|
+
if (options.method === "refresh" && result.state !== undefined) {
|
|
708
|
+
throw new Error("Generative App refresh must be output-only.");
|
|
709
|
+
}
|
|
598
710
|
let nextRevision = revision;
|
|
599
711
|
if (result.state !== undefined) {
|
|
600
712
|
options.execution?.assertCurrent();
|
|
@@ -623,6 +735,9 @@ async function invokeMethod(options: {
|
|
|
623
735
|
output: result.output,
|
|
624
736
|
app: options.app,
|
|
625
737
|
revision: nextRevision,
|
|
738
|
+
...(result.refreshAfterMs !== undefined
|
|
739
|
+
? { refreshAfterMs: result.refreshAfterMs }
|
|
740
|
+
: {}),
|
|
626
741
|
stateChanged: result.state !== undefined,
|
|
627
742
|
viewMode: result.viewMode ?? "new",
|
|
628
743
|
};
|
|
@@ -848,6 +963,154 @@ export async function bindGenerativeApp(options: GenerativeAppRuntimeOptions & {
|
|
|
848
963
|
});
|
|
849
964
|
}
|
|
850
965
|
|
|
966
|
+
export function createGenerativeAppLiveSurfaceRuntime<THandle>(
|
|
967
|
+
deps: GenerativeAppLiveSurfaceRuntimeDeps<THandle>,
|
|
968
|
+
): GenerativeAppLiveSurfaceRuntime<THandle> {
|
|
969
|
+
interface Record {
|
|
970
|
+
surface: GenerativeAppLiveSurface<THandle>;
|
|
971
|
+
digest?: string;
|
|
972
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
973
|
+
inFlight: boolean;
|
|
974
|
+
retryDelayMs: number;
|
|
975
|
+
pending?: {
|
|
976
|
+
frame: GenerativeAppLiveSurfaceFrame<THandle>;
|
|
977
|
+
result: GenerativeAppInvocationResult;
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
const records = new Map<string, Record>();
|
|
981
|
+
const setTimer: NonNullable<GenerativeAppLiveSurfaceRuntimeDeps<THandle>["setTimer"]> =
|
|
982
|
+
deps.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
983
|
+
const clearTimer: NonNullable<GenerativeAppLiveSurfaceRuntimeDeps<THandle>["clearTimer"]> =
|
|
984
|
+
deps.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
985
|
+
let active = true;
|
|
986
|
+
|
|
987
|
+
const clearRecordTimer = (record: Record): void => {
|
|
988
|
+
if (record.timer !== undefined) clearTimer(record.timer);
|
|
989
|
+
record.timer = undefined;
|
|
990
|
+
};
|
|
991
|
+
const take = (key: string): GenerativeAppLiveSurface<THandle> | undefined => {
|
|
992
|
+
const record = records.get(key);
|
|
993
|
+
if (!record) return undefined;
|
|
994
|
+
clearRecordTimer(record);
|
|
995
|
+
records.delete(key);
|
|
996
|
+
return { ...record.surface };
|
|
997
|
+
};
|
|
998
|
+
const cancel = (key: string): void => {
|
|
999
|
+
take(key);
|
|
1000
|
+
};
|
|
1001
|
+
const ownsRecord = (key: string, record: Record): boolean =>
|
|
1002
|
+
active && records.get(key) === record;
|
|
1003
|
+
const schedule = (record: Record, delayMs: number): void => {
|
|
1004
|
+
clearRecordTimer(record);
|
|
1005
|
+
if (!active || records.get(record.surface.key) !== record) return;
|
|
1006
|
+
const timer = setTimer(() => void refreshNow(record.surface.key), delayMs);
|
|
1007
|
+
record.timer = timer;
|
|
1008
|
+
timer.unref?.();
|
|
1009
|
+
};
|
|
1010
|
+
const refreshNow = async (key: string): Promise<void> => {
|
|
1011
|
+
const record = records.get(key);
|
|
1012
|
+
if (!active || !record || record.inFlight) return;
|
|
1013
|
+
clearRecordTimer(record);
|
|
1014
|
+
if (!deps.isCurrent(record.surface)) {
|
|
1015
|
+
cancel(key);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
record.inFlight = true;
|
|
1019
|
+
try {
|
|
1020
|
+
let pending = record.pending;
|
|
1021
|
+
if (!pending) {
|
|
1022
|
+
const result = await invokeGenerativeApp({
|
|
1023
|
+
agentDir: deps.agentDir,
|
|
1024
|
+
expectedGeneration: record.surface.appGeneration,
|
|
1025
|
+
expectedRevision: record.surface.appRevision,
|
|
1026
|
+
method: "refresh",
|
|
1027
|
+
app: record.surface.app,
|
|
1028
|
+
});
|
|
1029
|
+
if (!ownsRecord(key, record) || !deps.isCurrent(record.surface)) {
|
|
1030
|
+
if (records.get(key) === record) cancel(key);
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
const frame = deps.plan(result, record.surface.handle);
|
|
1034
|
+
pending = { frame, result };
|
|
1035
|
+
if (frame.digest !== record.digest) record.pending = pending;
|
|
1036
|
+
}
|
|
1037
|
+
const { frame, result } = pending;
|
|
1038
|
+
if (!ownsRecord(key, record)) return;
|
|
1039
|
+
if (frame.digest !== record.digest) {
|
|
1040
|
+
record.surface.handle = await deps.edit(frame);
|
|
1041
|
+
if (!ownsRecord(key, record)) return;
|
|
1042
|
+
record.digest = frame.digest;
|
|
1043
|
+
record.pending = undefined;
|
|
1044
|
+
}
|
|
1045
|
+
if (result.refreshAfterMs === undefined) {
|
|
1046
|
+
cancel(key);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
record.retryDelayMs = GENERATIVE_APP_MIN_REFRESH_AFTER_MS;
|
|
1050
|
+
record.surface.appRevision = result.revision;
|
|
1051
|
+
record.surface.refreshAfterMs = result.refreshAfterMs;
|
|
1052
|
+
schedule(record, result.refreshAfterMs);
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
const classification = deps.classifyEditError?.(error) ?? { kind: "unknown" as const };
|
|
1055
|
+
if (!ownsRecord(key, record)) return;
|
|
1056
|
+
deps.recordRuntimeEvent?.("generative-app", error, {
|
|
1057
|
+
phase: "live-surface-refresh",
|
|
1058
|
+
app: record.surface.app,
|
|
1059
|
+
outcome: classification.kind,
|
|
1060
|
+
});
|
|
1061
|
+
if (classification.kind !== "retry" || !deps.isCurrent(record.surface)) {
|
|
1062
|
+
cancel(key);
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
const delay = classification.retryAfterMs === undefined
|
|
1066
|
+
? record.retryDelayMs
|
|
1067
|
+
: Math.max(GENERATIVE_APP_MIN_REFRESH_AFTER_MS, classification.retryAfterMs);
|
|
1068
|
+
record.retryDelayMs = Math.min(60_000, Math.max(4_000, delay * 2));
|
|
1069
|
+
schedule(record, delay);
|
|
1070
|
+
} finally {
|
|
1071
|
+
record.inFlight = false;
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
const open = (surface: GenerativeAppLiveSurface<THandle>): void => {
|
|
1075
|
+
cancel(surface.key);
|
|
1076
|
+
if (!active) return;
|
|
1077
|
+
const record: Record = {
|
|
1078
|
+
surface: { ...surface },
|
|
1079
|
+
digest: surface.initialDigest,
|
|
1080
|
+
inFlight: false,
|
|
1081
|
+
retryDelayMs: GENERATIVE_APP_MIN_REFRESH_AFTER_MS,
|
|
1082
|
+
};
|
|
1083
|
+
records.set(surface.key, record);
|
|
1084
|
+
schedule(record, surface.refreshAfterMs);
|
|
1085
|
+
};
|
|
1086
|
+
return {
|
|
1087
|
+
open,
|
|
1088
|
+
cancel,
|
|
1089
|
+
take,
|
|
1090
|
+
async resume(surface, result) {
|
|
1091
|
+
if (!active || !deps.isCurrent(surface)) return;
|
|
1092
|
+
const frame = deps.plan(result, surface.handle);
|
|
1093
|
+
const handle = frame.digest === surface.initialDigest
|
|
1094
|
+
? surface.handle
|
|
1095
|
+
: await deps.edit(frame);
|
|
1096
|
+
if (result.refreshAfterMs === undefined || !deps.isCurrent({ ...surface, handle })) return;
|
|
1097
|
+
open({
|
|
1098
|
+
...surface,
|
|
1099
|
+
appGeneration: result.generation,
|
|
1100
|
+
appRevision: result.revision,
|
|
1101
|
+
handle,
|
|
1102
|
+
initialDigest: frame.digest,
|
|
1103
|
+
refreshAfterMs: result.refreshAfterMs,
|
|
1104
|
+
});
|
|
1105
|
+
},
|
|
1106
|
+
refreshNow,
|
|
1107
|
+
shutdown() {
|
|
1108
|
+
active = false;
|
|
1109
|
+
for (const key of [...records.keys()]) cancel(key);
|
|
1110
|
+
},
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
|
|
851
1114
|
export function formatGenerativeAppToolOutput(output: string): string {
|
|
852
1115
|
const normalized = output.replace(/^\n+/u, "");
|
|
853
1116
|
return `\n${normalized || "(Generative App returned no output)"}`;
|
|
@@ -884,6 +1147,72 @@ export function registerTelegramBindTool(
|
|
|
884
1147
|
pi: ExtensionAPI,
|
|
885
1148
|
deps: TelegramBindToolRegistrationDeps,
|
|
886
1149
|
): void {
|
|
1150
|
+
const planFrame = (
|
|
1151
|
+
result: GenerativeAppInvocationResult,
|
|
1152
|
+
handle: TelegramBindLiveHandle,
|
|
1153
|
+
): GenerativeAppLiveSurfaceFrame<TelegramBindLiveHandle> => {
|
|
1154
|
+
const planned = deps.planOutput!(result.output, {
|
|
1155
|
+
binding: {
|
|
1156
|
+
generation: result.generation,
|
|
1157
|
+
app: result.app,
|
|
1158
|
+
revision: result.revision,
|
|
1159
|
+
},
|
|
1160
|
+
});
|
|
1161
|
+
const view = {
|
|
1162
|
+
text: planned.markdown,
|
|
1163
|
+
parseMode: "markdown" as const,
|
|
1164
|
+
...(planned.replyMarkup !== undefined ? { replyMarkup: planned.replyMarkup } : {}),
|
|
1165
|
+
};
|
|
1166
|
+
return {
|
|
1167
|
+
digest: createHash("sha256").update(JSON.stringify(view)).digest("hex"),
|
|
1168
|
+
handle: { delivery: handle.delivery, view },
|
|
1169
|
+
};
|
|
1170
|
+
};
|
|
1171
|
+
const liveSurfaces = deps.planOutput && deps.editView && deps.isDeliveryHandleCurrent
|
|
1172
|
+
? createGenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle>({
|
|
1173
|
+
agentDir: deps.agentDir,
|
|
1174
|
+
isCurrent: (surface) => deps.isDeliveryHandleCurrent!(surface.handle.delivery),
|
|
1175
|
+
plan: planFrame,
|
|
1176
|
+
async edit(frame) {
|
|
1177
|
+
const view = frame.handle.view;
|
|
1178
|
+
if (!view) throw new Error("Generative App live frame is missing its planned view.");
|
|
1179
|
+
const edited = await deps.editView!(frame.handle.delivery, view);
|
|
1180
|
+
if (!edited.ok) throw Object.assign(new Error(edited.message), {
|
|
1181
|
+
deliveryFailureReason: edited.reason,
|
|
1182
|
+
...(edited.retryAfterMs === undefined ? {} : { retryAfterMs: edited.retryAfterMs }),
|
|
1183
|
+
});
|
|
1184
|
+
return { delivery: edited.value, view };
|
|
1185
|
+
},
|
|
1186
|
+
classifyEditError(error) {
|
|
1187
|
+
const failure = error as {
|
|
1188
|
+
deliveryFailureReason?: string;
|
|
1189
|
+
retryAfterMs?: number;
|
|
1190
|
+
};
|
|
1191
|
+
if (failure.deliveryFailureReason === "rate-limited") {
|
|
1192
|
+
return { kind: "retry", retryAfterMs: failure.retryAfterMs };
|
|
1193
|
+
}
|
|
1194
|
+
if (failure.deliveryFailureReason === "transport-retryable") {
|
|
1195
|
+
return { kind: "retry" };
|
|
1196
|
+
}
|
|
1197
|
+
if (failure.deliveryFailureReason === "message-unavailable") {
|
|
1198
|
+
return { kind: "unavailable" };
|
|
1199
|
+
}
|
|
1200
|
+
return {
|
|
1201
|
+
kind: failure.deliveryFailureReason === "commit-unknown" ? "unknown" : "terminal",
|
|
1202
|
+
};
|
|
1203
|
+
},
|
|
1204
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
1205
|
+
...(deps.liveSurfaceSetTimer ? { setTimer: deps.liveSurfaceSetTimer } : {}),
|
|
1206
|
+
...(deps.liveSurfaceClearTimer ? { clearTimer: deps.liveSurfaceClearTimer } : {}),
|
|
1207
|
+
})
|
|
1208
|
+
: undefined;
|
|
1209
|
+
deps.setLiveSurfaceRuntime?.(liveSurfaces);
|
|
1210
|
+
const surfaceKey = (app: string, handle: TelegramBindDeliveryHandle): string =>
|
|
1211
|
+
getTelegramBindLiveSurfaceKey(
|
|
1212
|
+
app,
|
|
1213
|
+
deps.getActiveProfileName?.() ?? "default",
|
|
1214
|
+
handle.target,
|
|
1215
|
+
);
|
|
887
1216
|
pi.registerTool({
|
|
888
1217
|
name: "telegram_bind",
|
|
889
1218
|
label: "Telegram Bind",
|
|
@@ -919,7 +1248,7 @@ export function registerTelegramBindTool(
|
|
|
919
1248
|
const activeTurn = params.display === false
|
|
920
1249
|
? undefined
|
|
921
1250
|
: deps.getActiveTurn?.();
|
|
922
|
-
if (activeTurn && deps.planOutput && deps.sendMarkdownReply) {
|
|
1251
|
+
if (activeTurn && deps.planOutput && (deps.sendView || deps.sendMarkdownReply)) {
|
|
923
1252
|
try {
|
|
924
1253
|
const planned = deps.planOutput(result.output, {
|
|
925
1254
|
binding: {
|
|
@@ -928,17 +1257,54 @@ export function registerTelegramBindTool(
|
|
|
928
1257
|
revision: result.revision,
|
|
929
1258
|
},
|
|
930
1259
|
});
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1260
|
+
let messageId: number | undefined;
|
|
1261
|
+
if (deps.sendView) {
|
|
1262
|
+
const delivered = await deps.sendView(
|
|
1263
|
+
{
|
|
1264
|
+
text: planned.markdown,
|
|
1265
|
+
parseMode: "markdown",
|
|
1266
|
+
...(planned.replyMarkup !== undefined
|
|
1267
|
+
? { replyMarkup: planned.replyMarkup }
|
|
1268
|
+
: {}),
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
scope: { kind: "active-turn" },
|
|
1272
|
+
replyToMessageId: activeTurn.replyToMessageId,
|
|
1273
|
+
},
|
|
1274
|
+
);
|
|
1275
|
+
if (!delivered.ok) throw new Error(delivered.message);
|
|
1276
|
+
messageId = delivered.value.messageIds[0];
|
|
1277
|
+
if (liveSurfaces) {
|
|
1278
|
+
const key = surfaceKey(result.app, delivered.value);
|
|
1279
|
+
if (result.refreshAfterMs === undefined) {
|
|
1280
|
+
liveSurfaces.cancel(key);
|
|
1281
|
+
} else {
|
|
1282
|
+
const handle = { delivery: delivered.value };
|
|
1283
|
+
const frame = planFrame(result, handle);
|
|
1284
|
+
liveSurfaces.open({
|
|
1285
|
+
app: result.app,
|
|
1286
|
+
appGeneration: result.generation,
|
|
1287
|
+
appRevision: result.revision,
|
|
1288
|
+
handle,
|
|
1289
|
+
initialDigest: frame.digest,
|
|
1290
|
+
key,
|
|
1291
|
+
refreshAfterMs: result.refreshAfterMs,
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
} else {
|
|
1296
|
+
messageId = await deps.sendMarkdownReply!(
|
|
1297
|
+
activeTurn.chatId,
|
|
1298
|
+
activeTurn.replyToMessageId,
|
|
1299
|
+
planned.markdown,
|
|
1300
|
+
{
|
|
1301
|
+
...(planned.replyMarkup !== undefined
|
|
1302
|
+
? { replyMarkup: planned.replyMarkup }
|
|
1303
|
+
: {}),
|
|
1304
|
+
...(activeTurn.target ? { target: activeTurn.target } : {}),
|
|
1305
|
+
},
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
942
1308
|
return {
|
|
943
1309
|
content: [{ type: "text", text: formatDisplayedGenerativeAppToolOutput() }],
|
|
944
1310
|
details: { ...result, displayed: true, messageId },
|
package/lib/menu-settings.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type TelegramSettingsMenuReplyMarkup = TelegramInlineKeyboardMarkup;
|
|
|
23
23
|
|
|
24
24
|
export interface TelegramSettingsStateDeps {
|
|
25
25
|
getThreadDisplayMode?: () => TelegramThreadDisplayMode | undefined;
|
|
26
|
+
getThreadDisplayPreview?: (mode: TelegramThreadDisplayMode) => readonly string[];
|
|
26
27
|
areDraftPreviewsEnabled: () => boolean;
|
|
27
28
|
getAssistantRenderingMode: () => TelegramAssistantRenderingMode;
|
|
28
29
|
getActivityVerbosity: () => TelegramActivityVerbosity;
|
|
@@ -175,7 +176,15 @@ export function buildTelegramSettingsMenuText(): string {
|
|
|
175
176
|
return SETTINGS_MENU_TITLE;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
|
|
179
|
+
function escapeThreadDisplayPreview(text: string): string {
|
|
180
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function buildThreadDisplaySettingsText(
|
|
184
|
+
mode: TelegramThreadDisplayMode,
|
|
185
|
+
preview: readonly string[] = [],
|
|
186
|
+
): string {
|
|
187
|
+
const visiblePreview = preview.slice(0, 5).map(escapeThreadDisplayPreview);
|
|
179
188
|
return [
|
|
180
189
|
`${THREAD_DISPLAY_SETTINGS_TITLE} <code>${mode}</code>`,
|
|
181
190
|
"",
|
|
@@ -183,7 +192,10 @@ export function buildThreadDisplaySettingsText(mode: TelegramThreadDisplayMode):
|
|
|
183
192
|
"",
|
|
184
193
|
"<code>-</code> <code>letters</code> (default): show the unique slot, such as <b><i>A</i></b> or <b><i>B</i></b>.",
|
|
185
194
|
"<code>-</code> <code>names</code>: show the generated dictionary name for the slot, such as <b><i>Anchor</i></b> or <b><i>Briar</i></b>.",
|
|
186
|
-
"<code>-</code> <code>directories</code>:
|
|
195
|
+
"<code>-</code> <code>directories</code>: legacy directory formatting retained without automatic migration.",
|
|
196
|
+
"<code>-</code> <code>directory-snake</code>: normalize the distinguishing directory path, such as <b><i>api_tools</i></b> or <b><i>frontend_extensions_a</i></b>.",
|
|
197
|
+
"<code>-</code> <code>directory-title</code>: humanize the distinguishing directory path, such as <b><i>Api Tools</i></b> or <b><i>Frontend / Extensions · A</i></b>.",
|
|
198
|
+
...(visiblePreview.length ? ["", `<b>Live preview:</b> ${visiblePreview.map((label) => `<code>${label}</code>`).join(", ")}${preview.length > visiblePreview.length ? `, +${preview.length - visiblePreview.length} more` : ""}`] : []),
|
|
187
199
|
].join("\n");
|
|
188
200
|
}
|
|
189
201
|
|
|
@@ -414,7 +426,7 @@ export async function openTelegramSettingsMenu<
|
|
|
414
426
|
export function buildThreadDisplaySettingsReplyMarkup(mode: TelegramThreadDisplayMode): TelegramSettingsMenuReplyMarkup {
|
|
415
427
|
return { inline_keyboard: [
|
|
416
428
|
[{ text: "⬆️ Back", callback_data: "settings:list" }],
|
|
417
|
-
...(["letters", "names", "directories"] as const).map((value) => [{
|
|
429
|
+
...(["letters", "names", "directories", "directory-snake", "directory-title"] as const).map((value) => [{
|
|
418
430
|
text: `${mode === value ? "🟢 " : ""}${value}`,
|
|
419
431
|
callback_data: `settings:set:thread-display:${value}`,
|
|
420
432
|
}]),
|
|
@@ -630,7 +642,8 @@ export async function handleTelegramSettingsMenuCallbackAction(
|
|
|
630
642
|
}
|
|
631
643
|
if (data.startsWith("settings:set:thread-display:")) {
|
|
632
644
|
const mode = data.slice("settings:set:thread-display:".length);
|
|
633
|
-
if (mode !== "letters" && mode !== "names" && mode !== "directories"
|
|
645
|
+
if (mode !== "letters" && mode !== "names" && mode !== "directories" &&
|
|
646
|
+
mode !== "directory-snake" && mode !== "directory-title") {
|
|
634
647
|
await deps.answerCallbackQuery(callbackQueryId, "Unknown Thread display mode.");
|
|
635
648
|
return true;
|
|
636
649
|
}
|
|
@@ -647,7 +660,7 @@ export async function handleTelegramSettingsMenuCallbackAction(
|
|
|
647
660
|
return true;
|
|
648
661
|
}
|
|
649
662
|
await deps.updateSettingsMessage(
|
|
650
|
-
buildThreadDisplaySettingsText(mode),
|
|
663
|
+
buildThreadDisplaySettingsText(mode, deps.getThreadDisplayPreview?.(mode)),
|
|
651
664
|
buildThreadDisplaySettingsReplyMarkup(mode),
|
|
652
665
|
);
|
|
653
666
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
@@ -863,6 +876,7 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
863
876
|
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
864
877
|
isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
|
|
865
878
|
getThreadDisplayMode: deps.getThreadDisplayMode,
|
|
879
|
+
getThreadDisplayPreview: deps.getThreadDisplayPreview,
|
|
866
880
|
sendSettingsMenu: (state, text, replyMarkup) =>
|
|
867
881
|
deps.sendInteractiveMessage(
|
|
868
882
|
state.chatId,
|
|
@@ -887,6 +901,7 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
887
901
|
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
888
902
|
isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
|
|
889
903
|
getThreadDisplayMode: deps.getThreadDisplayMode,
|
|
904
|
+
getThreadDisplayPreview: deps.getThreadDisplayPreview,
|
|
890
905
|
updateSettingsMessage: (text, replyMarkup) =>
|
|
891
906
|
deps.editInteractiveMessage(
|
|
892
907
|
state.chatId,
|
|
@@ -931,6 +946,7 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
931
946
|
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
932
947
|
isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
|
|
933
948
|
getThreadDisplayMode: deps.getThreadDisplayMode,
|
|
949
|
+
getThreadDisplayPreview: deps.getThreadDisplayPreview,
|
|
934
950
|
setThreadDisplayMode: deps.setThreadDisplayMode,
|
|
935
951
|
setDraftPreviewsEnabled: deps.setDraftPreviewsEnabled,
|
|
936
952
|
setAssistantRenderingMode: deps.setAssistantRenderingMode,
|