@llblab/pi-kit 0.15.0 → 0.16.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/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +9 -9
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +0 -13
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +15 -0
- package/node_modules/@llblab/pi-state-flow/README.md +7 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +5 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +17 -16
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +8 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +24 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +1 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +8 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +4 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +5 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +93 -35
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +20 -23
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +8 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +29 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +34 -18
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +14 -16
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +27 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +182 -10
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +34 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +20 -24
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +6 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +5 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +55 -20
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +8 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +10 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +7 -3
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +11 -3
- package/node_modules/@llblab/pi-state-flow/docs/README.md +1 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +5 -3
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +3 -2
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +504 -0
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +11 -8
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +18 -15
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +4 -3
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +8 -4
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +5 -0
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +86 -34
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +23 -22
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +8 -4
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +31 -3
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +33 -16
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +14 -16
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +173 -9
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +32 -4
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +22 -25
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +11 -6
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +46 -18
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +19 -6
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +7 -3
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +11 -3
- package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +7 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +134 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +297 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +11 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +68 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +17 -13
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +55 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +23 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +18 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +152 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +5 -3
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +3 -3
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +4 -3
- package/node_modules/@llblab/pi-telegram/docs/sections.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +2 -1
- package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +3 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +466 -21
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +15 -0
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +77 -9
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +25 -10
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +3 -0
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +84 -17
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +30 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +199 -6
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +5 -2
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
|
@@ -25,12 +25,14 @@ function getVoiceReplyModeSetting(mode, configured) {
|
|
|
25
25
|
export function buildTelegramSettingsMenuText() {
|
|
26
26
|
return SETTINGS_MENU_TITLE;
|
|
27
27
|
}
|
|
28
|
-
export function buildThreadDisplaySettingsText(mode) {
|
|
28
|
+
export function buildThreadDisplaySettingsText(mode, custom = false) {
|
|
29
29
|
return [
|
|
30
|
-
`${THREAD_DISPLAY_SETTINGS_TITLE} <code>${threadDisplayModeLabel(mode)}</code>`,
|
|
30
|
+
`${THREAD_DISPLAY_SETTINGS_TITLE} <code>${custom ? "custom" : threadDisplayModeLabel(mode)}</code>`,
|
|
31
31
|
"",
|
|
32
32
|
"Choose how this bot profile labels Telegram tabs and Pi terminal status. Each slot is unique across this bot profile.",
|
|
33
33
|
"",
|
|
34
|
+
"Set a manual name for the current Thread with /name <code>Name</code>.",
|
|
35
|
+
"",
|
|
34
36
|
"<code>-</code> <code>letters</code> (default): show the unique slot, such as <b><i>A</i></b> or <b><i>B</i></b>.",
|
|
35
37
|
"<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>.",
|
|
36
38
|
"<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>.",
|
|
@@ -122,7 +124,7 @@ export function buildTimeInjectionModeSettingsText(mode) {
|
|
|
122
124
|
"<code>-</code> <code>interval</code> (default): add time at most once per chat interval (1 hour unless configured).",
|
|
123
125
|
].join("\n");
|
|
124
126
|
}
|
|
125
|
-
export function buildTelegramSettingsMenuReplyMarkup(draftPreviewsEnabled, assistantRenderingModeOrVoiceReplyMode, voiceReplyModeOrTimeInjectionMode, timeInjectionModeOrSectionRegistry, sectionRegistryOrVoiceReplyModeConfigured, voiceReplyModeConfigured = true, automaticThreadCleanupEnabled = true, activityVerbosity = "quiet", threadDisplayMode) {
|
|
127
|
+
export function buildTelegramSettingsMenuReplyMarkup(draftPreviewsEnabled, assistantRenderingModeOrVoiceReplyMode, voiceReplyModeOrTimeInjectionMode, timeInjectionModeOrSectionRegistry, sectionRegistryOrVoiceReplyModeConfigured, voiceReplyModeConfigured = true, automaticThreadCleanupEnabled = true, activityVerbosity = "quiet", threadDisplayMode, threadDisplayCustom = false) {
|
|
126
128
|
const hasRenderingMode = assistantRenderingModeOrVoiceReplyMode === "rich" ||
|
|
127
129
|
assistantRenderingModeOrVoiceReplyMode === "html";
|
|
128
130
|
const assistantRenderingMode = hasRenderingMode ? assistantRenderingModeOrVoiceReplyMode : "rich";
|
|
@@ -164,14 +166,13 @@ export function buildTelegramSettingsMenuReplyMarkup(draftPreviewsEnabled, assis
|
|
|
164
166
|
text: `🕒 Time injection: ${getTelegramSettingsStateValueLabel(timeInjectionMode)}`,
|
|
165
167
|
callback_data: "settings:open:time-injection",
|
|
166
168
|
},
|
|
167
|
-
{
|
|
168
|
-
text: `🧹 Thread cleanup: ${automaticThreadCleanupEnabled ? "on" : "off"}`,
|
|
169
|
-
callback_data: "settings:open:automatic-thread-cleanup",
|
|
170
|
-
},
|
|
171
169
|
];
|
|
172
170
|
if (threadDisplayMode)
|
|
173
171
|
settingsButtons.push({
|
|
174
|
-
text:
|
|
172
|
+
text: `🧹 Thread cleanup: ${automaticThreadCleanupEnabled ? "on" : "off"}`,
|
|
173
|
+
callback_data: "settings:open:automatic-thread-cleanup",
|
|
174
|
+
}, {
|
|
175
|
+
text: `🧵 Thread display: ${threadDisplayCustom ? "custom" : threadDisplayMode}`,
|
|
175
176
|
callback_data: "settings:open:thread-display",
|
|
176
177
|
});
|
|
177
178
|
if (sectionRegistry) {
|
|
@@ -186,7 +187,7 @@ export function buildTelegramSettingsMenuReplyMarkup(draftPreviewsEnabled, assis
|
|
|
186
187
|
}
|
|
187
188
|
export async function openTelegramSettingsMenu(deps, sectionRegistry) {
|
|
188
189
|
const state = await deps.getModelMenuState();
|
|
189
|
-
const messageId = await deps.sendSettingsMenu(state, buildTelegramSettingsMenuText(), buildTelegramSettingsMenuReplyMarkup(deps.areDraftPreviewsEnabled(), deps.getAssistantRenderingMode(), deps.getVoiceReplyMode(), deps.getTimeInjectionMode(), sectionRegistry, deps.isVoiceReplyModeConfigured(), deps.isAutomaticThreadCleanupEnabled(), deps.getActivityVerbosity(), deps.getThreadDisplayMode?.()));
|
|
190
|
+
const messageId = await deps.sendSettingsMenu(state, buildTelegramSettingsMenuText(), buildTelegramSettingsMenuReplyMarkup(deps.areDraftPreviewsEnabled(), deps.getAssistantRenderingMode(), deps.getVoiceReplyMode(), deps.getTimeInjectionMode(), sectionRegistry, deps.isVoiceReplyModeConfigured(), deps.isAutomaticThreadCleanupEnabled(), deps.getActivityVerbosity(), deps.getThreadDisplayMode?.(), deps.isThreadDisplayCustom?.() ?? false));
|
|
190
191
|
if (messageId === undefined)
|
|
191
192
|
return;
|
|
192
193
|
state.messageId = messageId;
|
|
@@ -202,11 +203,11 @@ function threadDisplayModeLabel(mode) {
|
|
|
202
203
|
default: return "letters";
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
|
-
export function buildThreadDisplaySettingsReplyMarkup(mode) {
|
|
206
|
+
export function buildThreadDisplaySettingsReplyMarkup(mode, custom = false) {
|
|
206
207
|
return { inline_keyboard: [
|
|
207
208
|
[{ text: "⬆️ Back", callback_data: "settings:list" }],
|
|
208
209
|
...["letters", "names", "directory-title", "directory-snake"].map((value) => [{
|
|
209
|
-
text: `${mode === value ? "🟢 " : ""}${threadDisplayModeLabel(value)}`,
|
|
210
|
+
text: `${!custom && mode === value ? "🟢 " : ""}${threadDisplayModeLabel(value)}`,
|
|
210
211
|
callback_data: `settings:set:thread-display:${value}`,
|
|
211
212
|
}]),
|
|
212
213
|
] };
|
|
@@ -307,7 +308,7 @@ export function buildVoiceReplyModeSettingsReplyMarkup(mode, configured = true)
|
|
|
307
308
|
};
|
|
308
309
|
}
|
|
309
310
|
export async function updateTelegramSettingsMenuMessage(deps, sectionRegistry) {
|
|
310
|
-
await deps.updateSettingsMessage(buildTelegramSettingsMenuText(), buildTelegramSettingsMenuReplyMarkup(deps.areDraftPreviewsEnabled(), deps.getAssistantRenderingMode(), deps.getVoiceReplyMode(), deps.getTimeInjectionMode(), sectionRegistry, deps.isVoiceReplyModeConfigured(), deps.isAutomaticThreadCleanupEnabled(), deps.getActivityVerbosity(), deps.getThreadDisplayMode?.()));
|
|
311
|
+
await deps.updateSettingsMessage(buildTelegramSettingsMenuText(), buildTelegramSettingsMenuReplyMarkup(deps.areDraftPreviewsEnabled(), deps.getAssistantRenderingMode(), deps.getVoiceReplyMode(), deps.getTimeInjectionMode(), sectionRegistry, deps.isVoiceReplyModeConfigured(), deps.isAutomaticThreadCleanupEnabled(), deps.getActivityVerbosity(), deps.getThreadDisplayMode?.(), deps.isThreadDisplayCustom?.() ?? false));
|
|
311
312
|
}
|
|
312
313
|
export async function updateAutomaticThreadCleanupSettingsMessage(deps) {
|
|
313
314
|
const enabled = deps.isAutomaticThreadCleanupEnabled();
|
|
@@ -362,7 +363,7 @@ export async function handleTelegramSettingsMenuCallbackAction(callbackQueryId,
|
|
|
362
363
|
await deps.answerCallbackQuery(callbackQueryId, "Threaded Mode is no longer available.");
|
|
363
364
|
return true;
|
|
364
365
|
}
|
|
365
|
-
await deps.updateSettingsMessage(buildThreadDisplaySettingsText(mode), buildThreadDisplaySettingsReplyMarkup(mode));
|
|
366
|
+
await deps.updateSettingsMessage(buildThreadDisplaySettingsText(mode, deps.isThreadDisplayCustom?.() ?? false), buildThreadDisplaySettingsReplyMarkup(mode, deps.isThreadDisplayCustom?.() ?? false));
|
|
366
367
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
367
368
|
return true;
|
|
368
369
|
}
|
|
@@ -533,6 +534,7 @@ export function createTelegramSettingsMenuRuntime(deps, sectionRegistry) {
|
|
|
533
534
|
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
534
535
|
isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
|
|
535
536
|
getThreadDisplayMode: deps.getThreadDisplayMode,
|
|
537
|
+
isThreadDisplayCustom: deps.isThreadDisplayCustom,
|
|
536
538
|
sendSettingsMenu: (state, text, replyMarkup) => deps.sendInteractiveMessage(state.chatId, text, "html", replyMarkup),
|
|
537
539
|
storeModelMenuState: deps.storeModelMenuState,
|
|
538
540
|
}, sectionRegistry);
|
|
@@ -548,6 +550,7 @@ export function createTelegramSettingsMenuRuntime(deps, sectionRegistry) {
|
|
|
548
550
|
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
549
551
|
isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
|
|
550
552
|
getThreadDisplayMode: deps.getThreadDisplayMode,
|
|
553
|
+
isThreadDisplayCustom: deps.isThreadDisplayCustom,
|
|
551
554
|
updateSettingsMessage: (text, replyMarkup) => deps.editInteractiveMessage(state.chatId, state.messageId, text, "html", replyMarkup),
|
|
552
555
|
}, sectionRegistry);
|
|
553
556
|
},
|
|
@@ -577,6 +580,7 @@ export function createTelegramSettingsMenuRuntime(deps, sectionRegistry) {
|
|
|
577
580
|
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
578
581
|
isAutomaticThreadCleanupEnabled: deps.isAutomaticThreadCleanupEnabled,
|
|
579
582
|
getThreadDisplayMode: deps.getThreadDisplayMode,
|
|
583
|
+
isThreadDisplayCustom: deps.isThreadDisplayCustom,
|
|
580
584
|
setThreadDisplayMode: deps.setThreadDisplayMode,
|
|
581
585
|
setDraftPreviewsEnabled: deps.setDraftPreviewsEnabled,
|
|
582
586
|
setAssistantRenderingMode: deps.setAssistantRenderingMode,
|
|
@@ -56,8 +56,9 @@ export interface PiExtensionApiRuntimePorts {
|
|
|
56
56
|
getActiveTools: ExtensionAPI["getActiveTools"];
|
|
57
57
|
setActiveTools: ExtensionAPI["setActiveTools"];
|
|
58
58
|
setModel: ExtensionAPI["setModel"];
|
|
59
|
+
registerCommand: ExtensionAPI["registerCommand"];
|
|
59
60
|
}
|
|
60
|
-
export declare function createExtensionApiRuntimePorts(api: Pick<ExtensionAPI, "sendUserMessage" | "exec" | "getCommands" | "getThinkingLevel" | "setThinkingLevel" | "getActiveTools" | "setActiveTools" | "setModel">): PiExtensionApiRuntimePorts;
|
|
61
|
+
export declare function createExtensionApiRuntimePorts(api: Pick<ExtensionAPI, "sendUserMessage" | "exec" | "getCommands" | "getThinkingLevel" | "setThinkingLevel" | "getActiveTools" | "setActiveTools" | "setModel" | "registerCommand">): PiExtensionApiRuntimePorts;
|
|
61
62
|
export declare function normalizeSettingsManager(manager: unknown): PiSettingsManager;
|
|
62
63
|
export declare function createSettingsManager(cwd: string): Promise<PiSettingsManager>;
|
|
63
64
|
export declare function createScopedModelPatternPersister(deps: {
|
|
@@ -44,6 +44,7 @@ export function createExtensionApiRuntimePorts(api) {
|
|
|
44
44
|
getActiveTools: () => api.getActiveTools(),
|
|
45
45
|
setActiveTools: (names) => api.setActiveTools(names),
|
|
46
46
|
setModel: (model) => api.setModel(model),
|
|
47
|
+
registerCommand: (name, options) => api.registerCommand(name, options),
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
function readEnabledModels(value) {
|
|
@@ -164,6 +164,7 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
|
|
|
164
164
|
sendUserMessage?: (message: string, options?: Queue.TelegramPromptDeliveryOptions) => void;
|
|
165
165
|
isIdle: (ctx: TContext) => boolean;
|
|
166
166
|
hasPendingMessages: (ctx: TContext) => boolean;
|
|
167
|
+
requestNewSession?: (source: unknown) => void;
|
|
167
168
|
compact: (ctx: TContext, callbacks: {
|
|
168
169
|
onComplete: () => void;
|
|
169
170
|
onError: (error: unknown) => void;
|
|
@@ -322,6 +322,7 @@ const TELEGRAM_OWNED_CALLBACK_PREFIXES = [
|
|
|
322
322
|
"compact:",
|
|
323
323
|
"menu:",
|
|
324
324
|
"model:",
|
|
325
|
+
"new:",
|
|
325
326
|
"queue:",
|
|
326
327
|
"section:",
|
|
327
328
|
"settings:",
|
|
@@ -1216,7 +1217,7 @@ export function createTelegramInboundRouteRuntime(deps) {
|
|
|
1216
1217
|
return;
|
|
1217
1218
|
}
|
|
1218
1219
|
if (selected.kind === "cancel") {
|
|
1219
|
-
await deps.editInteractiveMessage?.(chatId, dialogMessageId, "<b>✖ Rename cancelled
|
|
1220
|
+
await deps.editInteractiveMessage?.(chatId, dialogMessageId, "<b>✖ Rename cancelled.</b>", "html", { inline_keyboard: [] });
|
|
1220
1221
|
await deps.answerCallbackQuery(query.id);
|
|
1221
1222
|
return;
|
|
1222
1223
|
}
|
|
@@ -1241,6 +1242,36 @@ export function createTelegramInboundRouteRuntime(deps) {
|
|
|
1241
1242
|
}
|
|
1242
1243
|
return;
|
|
1243
1244
|
}
|
|
1245
|
+
const handledByNew = await Commands.handleTelegramNewConfirmationCallback(query, {
|
|
1246
|
+
ctx,
|
|
1247
|
+
answerCallbackQuery: deps.answerCallbackQuery,
|
|
1248
|
+
editInteractiveMessage: deps.editInteractiveMessage ?? (async () => { }),
|
|
1249
|
+
deleteMessage: deps.deleteMessage ?? (async () => { }),
|
|
1250
|
+
runNew: async (newCtx) => {
|
|
1251
|
+
await Commands.handleTelegramNewCommand({
|
|
1252
|
+
isIdle: () => deps.isIdle(newCtx),
|
|
1253
|
+
hasPendingMessages: () => deps.hasPendingMessages(newCtx),
|
|
1254
|
+
hasActiveTelegramTurn: deps.activeTurnRuntime.has,
|
|
1255
|
+
hasDispatchPending: deps.bridgeRuntime.lifecycle.hasDispatchPending,
|
|
1256
|
+
hasQueuedTelegramItems: deps.telegramQueueStore.hasQueuedItems,
|
|
1257
|
+
isCompactionInProgress: deps.bridgeRuntime.lifecycle.isCompactionInProgress,
|
|
1258
|
+
requestNewSession: deps.requestNewSession
|
|
1259
|
+
? () => deps.requestNewSession(query)
|
|
1260
|
+
: undefined,
|
|
1261
|
+
sendTextReply: async (text) => {
|
|
1262
|
+
const chatId = query.message?.chat?.id;
|
|
1263
|
+
const messageId = query.message?.message_id;
|
|
1264
|
+
if (typeof chatId !== "number" || typeof messageId !== "number")
|
|
1265
|
+
return;
|
|
1266
|
+
await deps.editInteractiveMessage?.(chatId, messageId, text, "html", { inline_keyboard: [] });
|
|
1267
|
+
},
|
|
1268
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
1269
|
+
});
|
|
1270
|
+
},
|
|
1271
|
+
});
|
|
1272
|
+
assertExecutionCurrent();
|
|
1273
|
+
if (handledByNew)
|
|
1274
|
+
return;
|
|
1244
1275
|
const handledByCompact = await Commands.handleTelegramCompactConfirmationCallback(query, {
|
|
1245
1276
|
ctx,
|
|
1246
1277
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
@@ -1392,6 +1423,7 @@ export function createTelegramInboundRouteRuntime(deps) {
|
|
|
1392
1423
|
stopTypingLoop: deps.stopTypingLoop,
|
|
1393
1424
|
enqueueContinueTurn,
|
|
1394
1425
|
compact: deps.compact,
|
|
1426
|
+
requestNewSession: deps.requestNewSession,
|
|
1395
1427
|
allocateItemOrder: deps.bridgeRuntime.queue.allocateItemOrder,
|
|
1396
1428
|
allocateControlOrder: deps.bridgeRuntime.queue.allocateControlOrder,
|
|
1397
1429
|
appendControlItem: deps.queueMutationRuntime.append,
|
|
@@ -1652,9 +1684,18 @@ export function createTelegramInboundRouteRuntime(deps) {
|
|
|
1652
1684
|
dialogMessageId: candidate.dialogMessageId,
|
|
1653
1685
|
});
|
|
1654
1686
|
}
|
|
1655
|
-
|
|
1687
|
+
const replyText = result.ok && !result.message
|
|
1656
1688
|
? Commands.formatTelegramAutomaticThreadDisplayNameRestoredHeading(result.threadName ?? name)
|
|
1657
|
-
: Commands.formatTelegramInformationHeading(result.ok ? "✅" : "⚠️", result.message ?? "Thread display name reset failed.")
|
|
1689
|
+
: Commands.formatTelegramInformationHeading(result.ok ? "✅" : "⚠️", result.message ?? "Thread display name reset failed.");
|
|
1690
|
+
if (result.ok) {
|
|
1691
|
+
Updates.assertTelegramUpdateExecutionCurrent(message);
|
|
1692
|
+
Updates.reportTelegramUpdateCompleted(message);
|
|
1693
|
+
void deps.sendTextReply(target.chatId, message.message_id, replyText, { parseMode: "HTML", target }).catch((error) => deps.recordRuntimeEvent?.("telegram-command", error, {
|
|
1694
|
+
command: "name", phase: "reset-result",
|
|
1695
|
+
}));
|
|
1696
|
+
return true;
|
|
1697
|
+
}
|
|
1698
|
+
await deps.sendTextReply(target.chatId, message.message_id, replyText, { parseMode: "HTML", target });
|
|
1658
1699
|
return true;
|
|
1659
1700
|
}
|
|
1660
1701
|
const validationError = deps.validateThreadName?.(name);
|
|
@@ -1677,9 +1718,18 @@ export function createTelegramInboundRouteRuntime(deps) {
|
|
|
1677
1718
|
dialogMessageId: candidate.dialogMessageId,
|
|
1678
1719
|
});
|
|
1679
1720
|
}
|
|
1680
|
-
|
|
1721
|
+
const replyText = result?.ok && !result.message
|
|
1681
1722
|
? Commands.formatTelegramThreadDisplayNameSavedHeading(result.threadName ?? consumed.name)
|
|
1682
|
-
: Commands.formatTelegramInformationHeading(result?.ok ? "✅" : "⚠️", result?.message ?? "Thread display name update failed.")
|
|
1723
|
+
: Commands.formatTelegramInformationHeading(result?.ok ? "✅" : "⚠️", result?.message ?? "Thread display name update failed.");
|
|
1724
|
+
if (result?.ok) {
|
|
1725
|
+
Updates.assertTelegramUpdateExecutionCurrent(message);
|
|
1726
|
+
Updates.reportTelegramUpdateCompleted(message);
|
|
1727
|
+
void deps.sendTextReply(target.chatId, message.message_id, replyText, { parseMode: "HTML", target }).catch((error) => deps.recordRuntimeEvent?.("telegram-command", error, {
|
|
1728
|
+
command: "name", phase: "rename-result",
|
|
1729
|
+
}));
|
|
1730
|
+
return true;
|
|
1731
|
+
}
|
|
1732
|
+
await deps.sendTextReply(target.chatId, message.message_id, replyText, { parseMode: "HTML", target });
|
|
1683
1733
|
return true;
|
|
1684
1734
|
},
|
|
1685
1735
|
handleCommand: commandHandler,
|
|
@@ -38,6 +38,29 @@ export interface TelegramThreadDisplayReconcilerDeps {
|
|
|
38
38
|
captureLiveBindingKeys(bindings: readonly TelegramWorkspaceThreadBinding[]): ReadonlySet<string>;
|
|
39
39
|
callApi<TResponse>(method: string, body: Record<string, unknown>, options?: TelegramApiCallOptions): Promise<TResponse>;
|
|
40
40
|
}
|
|
41
|
+
export declare function createTelegramThreadDisplaySettingsRuntime(deps: {
|
|
42
|
+
getTarget(): {
|
|
43
|
+
chatId: number;
|
|
44
|
+
threadId?: number;
|
|
45
|
+
} | undefined;
|
|
46
|
+
getBinding(target: {
|
|
47
|
+
chatId: number;
|
|
48
|
+
threadId?: number;
|
|
49
|
+
}): {
|
|
50
|
+
manualThreadName?: string;
|
|
51
|
+
} | undefined;
|
|
52
|
+
apply(mode: TelegramThreadDisplayMode): Promise<void>;
|
|
53
|
+
reset(target: {
|
|
54
|
+
chatId: number;
|
|
55
|
+
threadId?: number;
|
|
56
|
+
}): Promise<{
|
|
57
|
+
ok: boolean;
|
|
58
|
+
message?: string;
|
|
59
|
+
}>;
|
|
60
|
+
}): {
|
|
61
|
+
isCustom(): boolean;
|
|
62
|
+
setMode(mode: TelegramThreadDisplayMode): Promise<void>;
|
|
63
|
+
};
|
|
41
64
|
/** Caller owns triggering and reporting; no timer or background retry is created. */
|
|
42
65
|
export declare function createTelegramThreadDisplayReconciler(deps: TelegramThreadDisplayReconcilerDeps): {
|
|
43
66
|
reconcile(): Promise<{
|
|
@@ -192,6 +192,26 @@ export async function applyTelegramThreadDisplaySetting(mode, deps) {
|
|
|
192
192
|
if (deps.getProfileKey() !== profile)
|
|
193
193
|
throw new Error("Telegram Thread display setting changed profile.");
|
|
194
194
|
}
|
|
195
|
+
export function createTelegramThreadDisplaySettingsRuntime(deps) {
|
|
196
|
+
return {
|
|
197
|
+
isCustom() {
|
|
198
|
+
const target = deps.getTarget();
|
|
199
|
+
return !!target && typeof deps.getBinding(target)?.manualThreadName === "string";
|
|
200
|
+
},
|
|
201
|
+
async setMode(mode) {
|
|
202
|
+
const target = deps.getTarget();
|
|
203
|
+
const hadManualName = !!target &&
|
|
204
|
+
typeof deps.getBinding(target)?.manualThreadName === "string";
|
|
205
|
+
await deps.apply(mode);
|
|
206
|
+
if (!hadManualName || !target)
|
|
207
|
+
return;
|
|
208
|
+
const reset = await deps.reset(target);
|
|
209
|
+
if (!reset.ok) {
|
|
210
|
+
throw new Error(reset.message ?? "Telegram Thread display override reset failed.");
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
195
215
|
/** Caller owns triggering and reporting; no timer or background retry is created. */
|
|
196
216
|
export function createTelegramThreadDisplayReconciler(deps) {
|
|
197
217
|
let tail = Promise.resolve();
|
|
@@ -175,6 +175,19 @@ export interface TelegramBotStateSnapshot {
|
|
|
175
175
|
lastSlot?: string;
|
|
176
176
|
lastReconcileAction?: string;
|
|
177
177
|
}
|
|
178
|
+
export interface TelegramSessionReplacementIntent {
|
|
179
|
+
continuity: "workspace-thread" | "classic-chat";
|
|
180
|
+
cwd: string;
|
|
181
|
+
profileName: string;
|
|
182
|
+
sourceSessionId: string;
|
|
183
|
+
sourceUpdateId: number;
|
|
184
|
+
target: TelegramTarget;
|
|
185
|
+
messageId: number;
|
|
186
|
+
slot?: string;
|
|
187
|
+
threadName?: string;
|
|
188
|
+
createdAtMs: number;
|
|
189
|
+
expiresAtMs: number;
|
|
190
|
+
}
|
|
178
191
|
export interface TelegramTopicTargetFile {
|
|
179
192
|
version: 1;
|
|
180
193
|
source: "snapshot";
|
|
@@ -187,6 +200,7 @@ export interface TelegramTopicTargetFile {
|
|
|
187
200
|
identities?: TelegramThreadIdentityRecord[];
|
|
188
201
|
workspaceBindings?: TelegramWorkspaceThreadBinding[];
|
|
189
202
|
workspaceRetirements?: TelegramWorkspaceRetirementIntent[];
|
|
203
|
+
sessionReplacement?: TelegramSessionReplacementIntent;
|
|
190
204
|
reservations?: TelegramThreadReservation[];
|
|
191
205
|
pendingProvisions?: TelegramThreadPendingProvision[];
|
|
192
206
|
pendingCleanups?: TelegramThreadCleanupIntent[];
|
|
@@ -227,6 +241,10 @@ export interface TelegramTopicTargetStore {
|
|
|
227
241
|
getIdentityByProfileKey: (profileKey: string) => TelegramThreadIdentityRecord | undefined;
|
|
228
242
|
forgetIdentityByProfileKey: (profileKey: string) => boolean;
|
|
229
243
|
listWorkspaceBindings: () => TelegramWorkspaceThreadBinding[];
|
|
244
|
+
getWorkspaceBindingByTarget: (target: TelegramTarget, sessionId?: string) => TelegramWorkspaceThreadBinding | undefined;
|
|
245
|
+
getSessionReplacementIntent: () => TelegramSessionReplacementIntent | undefined;
|
|
246
|
+
commitSessionReplacementIntent: (intent: TelegramSessionReplacementIntent, isCurrent: () => boolean) => Promise<boolean>;
|
|
247
|
+
removeSessionReplacementIntent: (expected: TelegramSessionReplacementIntent, isCurrent: () => boolean) => Promise<boolean>;
|
|
230
248
|
listWorkspaceRetirementIntents: () => TelegramWorkspaceRetirementIntent[];
|
|
231
249
|
commitWorkspaceJournalEvidence: (expected: TelegramWorkspaceThreadBinding, journalBindingKeys: readonly string[], complete: boolean) => TelegramWorkspaceThreadBinding | undefined;
|
|
232
250
|
upsertWorkspaceRetirementIntent: (intent: TelegramWorkspaceRetirementIntent) => boolean;
|
|
@@ -645,6 +645,50 @@ function normalizeWorkspaceBindingRecord(value) {
|
|
|
645
645
|
function cloneWorkspaceBinding(binding) {
|
|
646
646
|
return { ...binding, target: { ...binding.target } };
|
|
647
647
|
}
|
|
648
|
+
function cloneSessionReplacementIntent(intent) {
|
|
649
|
+
return { ...intent, target: { ...intent.target } };
|
|
650
|
+
}
|
|
651
|
+
function normalizeSessionReplacementIntent(value) {
|
|
652
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
653
|
+
return undefined;
|
|
654
|
+
const record = value;
|
|
655
|
+
const target = record.target;
|
|
656
|
+
if (typeof record.cwd !== "string" || !normalizeTelegramWorkspacePath(record.cwd) ||
|
|
657
|
+
typeof record.profileName !== "string" || !record.profileName ||
|
|
658
|
+
typeof record.sourceSessionId !== "string" || !normalizeTelegramSessionId(record.sourceSessionId) ||
|
|
659
|
+
typeof record.sourceUpdateId !== "number" || !Number.isSafeInteger(record.sourceUpdateId) ||
|
|
660
|
+
!target || typeof target.chatId !== "number" ||
|
|
661
|
+
(target.threadId !== undefined &&
|
|
662
|
+
(typeof target.threadId !== "number" || !Number.isSafeInteger(target.threadId))) ||
|
|
663
|
+
typeof record.messageId !== "number" || !Number.isSafeInteger(record.messageId) ||
|
|
664
|
+
typeof record.createdAtMs !== "number" || !Number.isSafeInteger(record.createdAtMs) ||
|
|
665
|
+
typeof record.expiresAtMs !== "number" || !Number.isSafeInteger(record.expiresAtMs) ||
|
|
666
|
+
record.expiresAtMs <= record.createdAtMs)
|
|
667
|
+
return undefined;
|
|
668
|
+
const continuity = record.continuity === "workspace-thread" ||
|
|
669
|
+
record.continuity === "classic-chat"
|
|
670
|
+
? record.continuity
|
|
671
|
+
: target.threadId !== undefined ? "workspace-thread" : "classic-chat";
|
|
672
|
+
if ((continuity === "workspace-thread") !== (target.threadId !== undefined)) {
|
|
673
|
+
return undefined;
|
|
674
|
+
}
|
|
675
|
+
return {
|
|
676
|
+
continuity,
|
|
677
|
+
cwd: normalizeTelegramWorkspacePath(record.cwd),
|
|
678
|
+
profileName: record.profileName,
|
|
679
|
+
sourceSessionId: normalizeTelegramSessionId(record.sourceSessionId),
|
|
680
|
+
sourceUpdateId: record.sourceUpdateId,
|
|
681
|
+
target: {
|
|
682
|
+
chatId: target.chatId,
|
|
683
|
+
...(typeof target.threadId === "number" ? { threadId: target.threadId } : {}),
|
|
684
|
+
},
|
|
685
|
+
messageId: record.messageId,
|
|
686
|
+
...(typeof record.slot === "string" ? { slot: record.slot } : {}),
|
|
687
|
+
...(typeof record.threadName === "string" ? { threadName: record.threadName } : {}),
|
|
688
|
+
createdAtMs: record.createdAtMs,
|
|
689
|
+
expiresAtMs: record.expiresAtMs,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
648
692
|
function normalizeWorkspaceRetirementIntent(value) {
|
|
649
693
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
650
694
|
return undefined;
|
|
@@ -905,6 +949,7 @@ function parseTopicTargetFile(value) {
|
|
|
905
949
|
return normalized ? [normalized] : [];
|
|
906
950
|
})
|
|
907
951
|
: [],
|
|
952
|
+
sessionReplacement: normalizeSessionReplacementIntent(file.sessionReplacement),
|
|
908
953
|
reservations: Array.isArray(file.reservations)
|
|
909
954
|
? file.reservations.flatMap((reservation) => {
|
|
910
955
|
const normalized = normalizeReservation(reservation);
|
|
@@ -1026,6 +1071,7 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1026
1071
|
let identities = new Map();
|
|
1027
1072
|
let workspaceBindings = new Map();
|
|
1028
1073
|
let workspaceRetirements = [];
|
|
1074
|
+
let sessionReplacement;
|
|
1029
1075
|
let workspaceRetirementCommitInFlight = false;
|
|
1030
1076
|
const hasWorkspaceRetirementConflict = (input) => workspaceRetirements.some((intent) => (input.bindingKey !== undefined && intent.binding.bindingKey === input.bindingKey) ||
|
|
1031
1077
|
(input.cwd !== undefined && intent.binding.cwd === input.cwd) ||
|
|
@@ -1150,6 +1196,7 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1150
1196
|
identities = new Map();
|
|
1151
1197
|
workspaceBindings = new Map();
|
|
1152
1198
|
workspaceRetirements = [];
|
|
1199
|
+
sessionReplacement = undefined;
|
|
1153
1200
|
workspaceRetirementCommitInFlight = false;
|
|
1154
1201
|
workspaceClaims = new Map();
|
|
1155
1202
|
reservations = [];
|
|
@@ -1171,6 +1218,7 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1171
1218
|
identities = new Map();
|
|
1172
1219
|
workspaceBindings = new Map();
|
|
1173
1220
|
workspaceRetirements = [];
|
|
1221
|
+
sessionReplacement = undefined;
|
|
1174
1222
|
workspaceRetirementCommitInFlight = false;
|
|
1175
1223
|
reservations = [];
|
|
1176
1224
|
pendingProvisions = [];
|
|
@@ -1208,6 +1256,9 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1208
1256
|
cloneWorkspaceBinding(binding),
|
|
1209
1257
|
]));
|
|
1210
1258
|
workspaceRetirements = (file.workspaceRetirements ?? []).map(cloneWorkspaceRetirementIntent);
|
|
1259
|
+
sessionReplacement = file.sessionReplacement
|
|
1260
|
+
? cloneSessionReplacementIntent(file.sessionReplacement)
|
|
1261
|
+
: undefined;
|
|
1211
1262
|
reconcileWorkspaceSuffixExposure();
|
|
1212
1263
|
for (const record of records.values())
|
|
1213
1264
|
rememberIdentity(record);
|
|
@@ -1286,6 +1337,9 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1286
1337
|
identities: Array.from(identities.values()).map(cloneIdentityRecord),
|
|
1287
1338
|
workspaceBindings: Array.from(workspaceBindings.values()).map(cloneWorkspaceBinding),
|
|
1288
1339
|
workspaceRetirements: workspaceRetirements.map(cloneWorkspaceRetirementIntent),
|
|
1340
|
+
...(sessionReplacement
|
|
1341
|
+
? { sessionReplacement: cloneSessionReplacementIntent(sessionReplacement) }
|
|
1342
|
+
: {}),
|
|
1289
1343
|
reservations: reservations.map((reservation) => ({ ...reservation })),
|
|
1290
1344
|
pendingProvisions: pendingProvisions.map((provision) => ({
|
|
1291
1345
|
...provision,
|
|
@@ -1563,6 +1617,40 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1563
1617
|
listWorkspaceBindings() {
|
|
1564
1618
|
return Array.from(workspaceBindings.values()).map(cloneWorkspaceBinding);
|
|
1565
1619
|
},
|
|
1620
|
+
getWorkspaceBindingByTarget(target, sessionId) {
|
|
1621
|
+
const binding = Array.from(workspaceBindings.values()).find((candidate) => targetMatches(candidate.target, target) &&
|
|
1622
|
+
(sessionId === undefined || candidate.sessionId === sessionId));
|
|
1623
|
+
return binding ? cloneWorkspaceBinding(binding) : undefined;
|
|
1624
|
+
},
|
|
1625
|
+
getSessionReplacementIntent() {
|
|
1626
|
+
return sessionReplacement
|
|
1627
|
+
? cloneSessionReplacementIntent(sessionReplacement)
|
|
1628
|
+
: undefined;
|
|
1629
|
+
},
|
|
1630
|
+
async commitSessionReplacementIntent(intent, isCurrent) {
|
|
1631
|
+
const next = normalizeSessionReplacementIntent(intent);
|
|
1632
|
+
if (!next || !isCurrent())
|
|
1633
|
+
return false;
|
|
1634
|
+
await loadFromDisk();
|
|
1635
|
+
if (!isCurrent())
|
|
1636
|
+
return false;
|
|
1637
|
+
const existing = sessionReplacement;
|
|
1638
|
+
if (existing && existing.expiresAtMs > getNowMs() &&
|
|
1639
|
+
!isDeepStrictEqual(existing, next))
|
|
1640
|
+
return false;
|
|
1641
|
+
sessionReplacement = cloneSessionReplacementIntent(next);
|
|
1642
|
+
markDirty();
|
|
1643
|
+
return await persistSnapshot() && isCurrent();
|
|
1644
|
+
},
|
|
1645
|
+
async removeSessionReplacementIntent(expected, isCurrent) {
|
|
1646
|
+
await loadFromDisk();
|
|
1647
|
+
if (!isCurrent() || !sessionReplacement ||
|
|
1648
|
+
!isDeepStrictEqual(sessionReplacement, expected))
|
|
1649
|
+
return false;
|
|
1650
|
+
sessionReplacement = undefined;
|
|
1651
|
+
markDirty();
|
|
1652
|
+
return await persistSnapshot() && isCurrent();
|
|
1653
|
+
},
|
|
1566
1654
|
listWorkspaceRetirementIntents() {
|
|
1567
1655
|
return workspaceRetirements.map(cloneWorkspaceRetirementIntent);
|
|
1568
1656
|
},
|
|
@@ -1959,6 +2047,36 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1959
2047
|
(options?.sessionId !== undefined && !normalizedSessionId) ||
|
|
1960
2048
|
hasWorkspaceRetirementConflict({ cwd: normalizedCwd }))
|
|
1961
2049
|
return undefined;
|
|
2050
|
+
let replacementPreviousInstanceId;
|
|
2051
|
+
const replacement = sessionReplacement;
|
|
2052
|
+
if (replacement?.continuity === "workspace-thread" && normalizedSessionId &&
|
|
2053
|
+
replacement.expiresAtMs > getNowMs() &&
|
|
2054
|
+
replacement.profileName === (getTelegramProfile() ?? "default") &&
|
|
2055
|
+
replacement.cwd === normalizedCwd &&
|
|
2056
|
+
replacement.sourceSessionId !== normalizedSessionId) {
|
|
2057
|
+
const sourceEntry = Array.from(workspaceBindings.entries()).find(([, binding]) => binding.cwd === normalizedCwd &&
|
|
2058
|
+
binding.sessionId === replacement.sourceSessionId &&
|
|
2059
|
+
targetMatches(binding.target, replacement.target));
|
|
2060
|
+
if (sourceEntry) {
|
|
2061
|
+
const [sourceKey, sourceBinding] = sourceEntry;
|
|
2062
|
+
const replacementIdentity = Array.from({ length: TELEGRAM_WORKSPACE_SLOTS.length })
|
|
2063
|
+
.map((_, ordinal) => createTelegramWorkspaceBindingIdentityWithKey(sourceBinding.cwd, sourceBinding.workspaceKey, ordinal, normalizedSessionId))
|
|
2064
|
+
.find((identity) => identity?.instanceSlot === sourceBinding.instanceSlot);
|
|
2065
|
+
if (replacementIdentity &&
|
|
2066
|
+
replacementIdentity.instanceSlot === sourceBinding.instanceSlot) {
|
|
2067
|
+
const existingTargetRecord = Array.from(records.values()).find((record) => targetMatches(record.target, replacement.target));
|
|
2068
|
+
replacementPreviousInstanceId = existingTargetRecord?.instanceId;
|
|
2069
|
+
workspaceBindings.delete(sourceKey);
|
|
2070
|
+
workspaceBindings.set(getWorkspaceBindingMapKey(replacementIdentity), {
|
|
2071
|
+
...sourceBinding,
|
|
2072
|
+
...replacementIdentity,
|
|
2073
|
+
updatedAtMs: getNowMs(),
|
|
2074
|
+
});
|
|
2075
|
+
markDirty();
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
const effectivePreviousInstanceId = previousInstanceId ?? replacementPreviousInstanceId;
|
|
1962
2080
|
const externalReservedSlots = captureExternalReservedSlots();
|
|
1963
2081
|
if (!externalReservedSlots) {
|
|
1964
2082
|
options?.onCapacityUnavailable?.();
|
|
@@ -1995,9 +2113,16 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
1995
2113
|
const mapKey = getWorkspaceBindingMapKey(identity);
|
|
1996
2114
|
const existingClaim = workspaceClaims.get(mapKey);
|
|
1997
2115
|
if (existingClaim) {
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2116
|
+
if (existingClaim.instanceId === instanceId) {
|
|
2117
|
+
return { ...existingClaim.identity };
|
|
2118
|
+
}
|
|
2119
|
+
if (existingClaim.instanceId !== effectivePreviousInstanceId)
|
|
2120
|
+
return undefined;
|
|
2121
|
+
workspaceClaims.set(mapKey, {
|
|
2122
|
+
identity: existingClaim.identity,
|
|
2123
|
+
instanceId,
|
|
2124
|
+
});
|
|
2125
|
+
return { ...existingClaim.identity };
|
|
2001
2126
|
}
|
|
2002
2127
|
const binding = workspaceBindings.get(mapKey);
|
|
2003
2128
|
const liveRecord = binding
|
|
@@ -2005,7 +2130,7 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
2005
2130
|
: undefined;
|
|
2006
2131
|
if (liveRecord &&
|
|
2007
2132
|
liveRecord.instanceId !== instanceId &&
|
|
2008
|
-
liveRecord.instanceId !==
|
|
2133
|
+
liveRecord.instanceId !== effectivePreviousInstanceId) {
|
|
2009
2134
|
return undefined;
|
|
2010
2135
|
}
|
|
2011
2136
|
const retainedTarget = binding?.target ?? legacyRecord?.target;
|
|
@@ -2147,8 +2272,29 @@ export function createTelegramTopicTargetStore(options) {
|
|
|
2147
2272
|
if (!next)
|
|
2148
2273
|
return undefined;
|
|
2149
2274
|
const nextMapKey = getWorkspaceBindingMapKey(next);
|
|
2150
|
-
|
|
2151
|
-
|
|
2275
|
+
let claim = workspaceClaims.get(nextMapKey);
|
|
2276
|
+
const replacedTargetBinding = Array.from(workspaceBindings.values()).find((existing) => existing.bindingKey !== next.bindingKey &&
|
|
2277
|
+
targetMatches(existing.target, next.target));
|
|
2278
|
+
if (claimInstanceId && claim?.instanceId === claimInstanceId &&
|
|
2279
|
+
replacedTargetBinding?.slot) {
|
|
2280
|
+
claim = {
|
|
2281
|
+
...claim,
|
|
2282
|
+
identity: { ...claim.identity, slot: replacedTargetBinding.slot },
|
|
2283
|
+
};
|
|
2284
|
+
workspaceClaims.set(nextMapKey, claim);
|
|
2285
|
+
next.slot = replacedTargetBinding.slot;
|
|
2286
|
+
if (replacedTargetBinding.threadName && !next.threadName) {
|
|
2287
|
+
next.threadName = replacedTargetBinding.threadName;
|
|
2288
|
+
}
|
|
2289
|
+
if (replacedTargetBinding.manualThreadName && !next.manualThreadName) {
|
|
2290
|
+
next.manualThreadName = replacedTargetBinding.manualThreadName;
|
|
2291
|
+
}
|
|
2292
|
+
if (replacedTargetBinding.displayTitle && !next.displayTitle) {
|
|
2293
|
+
next.displayTitle = replacedTargetBinding.displayTitle;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
if (next.slot && Array.from(workspaceBindings.values()).some((existing) => existing.bindingKey !== next.bindingKey && existing.slot === next.slot &&
|
|
2297
|
+
!targetMatches(existing.target, next.target)))
|
|
2152
2298
|
return undefined;
|
|
2153
2299
|
for (const existing of workspaceBindings.values()) {
|
|
2154
2300
|
if (existing.workspaceKey === next.workspaceKey &&
|
|
@@ -1216,6 +1216,7 @@ export interface TelegramUpdateWorkerOwnerRuntimeDeps<TContext> {
|
|
|
1216
1216
|
isContextCurrent: (ctx: TContext) => boolean;
|
|
1217
1217
|
dispatchNext: (ctx: TContext) => void;
|
|
1218
1218
|
requestQueueHandoffReconciliation: (ctx: TContext) => void;
|
|
1219
|
+
afterUpdateCompleted?: (updateId: number) => void;
|
|
1219
1220
|
}
|
|
1220
1221
|
export declare function createTelegramUpdateWorkerOwnerRuntime<TContext>(deps: TelegramUpdateWorkerOwnerRuntimeDeps<TContext>): TelegramUpdateWorkerOwnerRuntime<TContext>;
|
|
1221
1222
|
export interface TelegramUpdateAdmissionRuntimeBinding<TContext> {
|
|
@@ -3177,9 +3177,11 @@ export function createTelegramUpdateWorkerOwnerRuntime(deps) {
|
|
|
3177
3177
|
deps.dispatchNext(ctx);
|
|
3178
3178
|
deps.requestQueueHandoffReconciliation(ctx);
|
|
3179
3179
|
},
|
|
3180
|
-
onUpdateCompleted(
|
|
3181
|
-
if (deps.isContextCurrent(ctx))
|
|
3182
|
-
|
|
3180
|
+
onUpdateCompleted(updateId, ctx) {
|
|
3181
|
+
if (!deps.isContextCurrent(ctx))
|
|
3182
|
+
return;
|
|
3183
|
+
deps.dispatchNext(ctx);
|
|
3184
|
+
deps.afterUpdateCompleted?.(updateId);
|
|
3183
3185
|
},
|
|
3184
3186
|
};
|
|
3185
3187
|
}
|