@llblab/pi-kit 0.27.5 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKLOG.md +3 -1
- package/CHANGELOG.md +9 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +6 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +4 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +26 -13
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -1
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +2 -2
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +2 -1
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +4 -3
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +25 -12
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +10 -8
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -9
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
- package/node_modules/@llblab/pi-telegram/LICENSE +21 -0
- package/node_modules/@llblab/pi-telegram/README.md +15 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +5 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +5 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +6 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +4 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +54 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +206 -139
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +21 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +181 -25
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +4 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +28 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +52 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +78 -246
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +1 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +23 -26
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +0 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +15 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +0 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/config.js +12 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +2 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +255 -61
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -19
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +177 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +1115 -249
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +100 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +380 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +30 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +129 -72
- package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +28 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/media.js +26 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +0 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +8 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +0 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +18 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +4 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +12 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +1 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +1 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +4 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +0 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +50 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +155 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +0 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +3 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +3 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +69 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +29 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +104 -33
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +73 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2206 -307
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +44 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +149 -27
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +5 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +11 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +34 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +18 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +32 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +191 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +291 -59
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +1647 -369
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +184 -28
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +1032 -114
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.d.ts +12 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +21 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +25 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +95 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.d.ts +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +103 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +43 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +148 -52
- package/node_modules/@llblab/pi-telegram/dist/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/activity.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +152 -34
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/delivery.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +125 -19
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -1
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +22 -8
- package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +5 -9
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +6 -0
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +243 -224
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +178 -31
- package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +30 -8
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +110 -334
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +29 -29
- package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +15 -15
- package/node_modules/@llblab/pi-telegram/lib/config.ts +12 -17
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +2 -8
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +257 -72
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +0 -22
- package/node_modules/@llblab/pi-telegram/lib/inbound.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +1182 -326
- package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +402 -23
- package/node_modules/@llblab/pi-telegram/lib/logging.ts +151 -101
- package/node_modules/@llblab/pi-telegram/lib/media.ts +51 -9
- package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +8 -8
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +18 -18
- package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +11 -0
- package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/menu.ts +5 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +1 -10
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +5 -24
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +177 -17
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +3 -3
- package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +67 -9
- package/node_modules/@llblab/pi-telegram/lib/recovery.ts +104 -44
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +1955 -375
- package/node_modules/@llblab/pi-telegram/lib/sections.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/status.ts +156 -36
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +48 -33
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +24 -21
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +267 -9
- package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +3 -1
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1697 -488
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1072 -151
- package/node_modules/@llblab/pi-telegram/lib/wire.ts +28 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +97 -47
- package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +147 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +188 -89
- package/node_modules/@llblab/pi-telegram/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/scripts/audit-exports.mjs +100 -0
- package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +80 -43
- package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/package.json +3 -3
|
@@ -143,19 +143,19 @@ export interface TelegramSettingsMenuRuntimeDeps<
|
|
|
143
143
|
) => Promise<void>;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
const SETTINGS_MENU_TITLE = "<b>⚙️ Settings:</b>";
|
|
147
|
+
const AUTOMATIC_THREAD_CLEANUP_SETTINGS_TITLE =
|
|
148
148
|
"<b>🧹 Thread cleanup:</b>";
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
const INACTIVE_THREAD_REVIEW_TITLE = "<b>🔎 Inactive tabs review:</b>";
|
|
150
|
+
const DRAFT_PREVIEWS_SETTINGS_TITLE = "<b>📝 Draft previews:</b>";
|
|
151
|
+
const ASSISTANT_RENDERING_SETTINGS_TITLE =
|
|
152
152
|
"<b>🧾 Assistant rendering:</b>";
|
|
153
|
-
|
|
153
|
+
const ACTIVITY_VERBOSITY_SETTINGS_TITLE =
|
|
154
154
|
"<b>🔬 Activity:</b>";
|
|
155
|
-
|
|
155
|
+
const TIME_INJECTION_MODE_SETTINGS_TITLE =
|
|
156
156
|
"<b>🕒 Time injection mode:</b>";
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
const VOICE_REPLY_MODE_SETTINGS_TITLE = "<b>👄 Voice reply mode:</b>";
|
|
158
|
+
const THREAD_DISPLAY_SETTINGS_TITLE = "<b>🧵 Thread display:</b>";
|
|
159
159
|
|
|
160
160
|
function getVoiceReplyModeLabel(mode: TelegramVoiceReplyMode): string {
|
|
161
161
|
return mode;
|
|
@@ -209,7 +209,7 @@ export function buildAutomaticThreadCleanupSettingsText(
|
|
|
209
209
|
].join("\n");
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
function buildInactiveThreadReviewText(count: number): string {
|
|
213
213
|
return [INACTIVE_THREAD_REVIEW_TITLE, "",
|
|
214
214
|
`${count} proven inactive tab${count === 1 ? "" : "s"}.`,
|
|
215
215
|
"No tabs were deleted.",
|
|
@@ -393,7 +393,7 @@ export function buildTelegramSettingsMenuReplyMarkup(
|
|
|
393
393
|
return { inline_keyboard: rows };
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
|
|
396
|
+
async function openTelegramSettingsMenu<
|
|
397
397
|
TModel extends MenuModel = MenuModel,
|
|
398
398
|
>(
|
|
399
399
|
deps: TelegramSettingsMenuOpenDeps<TModel>,
|
|
@@ -560,7 +560,7 @@ export function buildVoiceReplyModeSettingsReplyMarkup(
|
|
|
560
560
|
};
|
|
561
561
|
}
|
|
562
562
|
|
|
563
|
-
|
|
563
|
+
async function updateTelegramSettingsMenuMessage(
|
|
564
564
|
deps: TelegramSettingsMenuMessageUpdateDeps,
|
|
565
565
|
sectionRegistry?: TelegramSectionRegistry,
|
|
566
566
|
): Promise<void> {
|
|
@@ -581,7 +581,7 @@ export async function updateTelegramSettingsMenuMessage(
|
|
|
581
581
|
);
|
|
582
582
|
}
|
|
583
583
|
|
|
584
|
-
|
|
584
|
+
async function updateAutomaticThreadCleanupSettingsMessage(
|
|
585
585
|
deps: TelegramSettingsMenuCallbackDeps,
|
|
586
586
|
): Promise<void> {
|
|
587
587
|
const enabled = deps.isAutomaticThreadCleanupEnabled();
|
|
@@ -591,7 +591,7 @@ export async function updateAutomaticThreadCleanupSettingsMessage(
|
|
|
591
591
|
);
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
|
|
594
|
+
async function updateDraftPreviewsSettingsMessage(
|
|
595
595
|
deps: TelegramSettingsMenuCallbackDeps,
|
|
596
596
|
): Promise<void> {
|
|
597
597
|
const enabled = deps.areDraftPreviewsEnabled();
|
|
@@ -601,7 +601,7 @@ export async function updateDraftPreviewsSettingsMessage(
|
|
|
601
601
|
);
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
-
|
|
604
|
+
async function updateAssistantRenderingSettingsMessage(
|
|
605
605
|
deps: TelegramSettingsMenuCallbackDeps,
|
|
606
606
|
): Promise<void> {
|
|
607
607
|
const mode = deps.getAssistantRenderingMode();
|
|
@@ -611,7 +611,7 @@ export async function updateAssistantRenderingSettingsMessage(
|
|
|
611
611
|
);
|
|
612
612
|
}
|
|
613
613
|
|
|
614
|
-
|
|
614
|
+
async function updateActivityVerbositySettingsMessage(
|
|
615
615
|
deps: TelegramSettingsMenuCallbackDeps,
|
|
616
616
|
): Promise<void> {
|
|
617
617
|
const verbosity = deps.getActivityVerbosity();
|
|
@@ -621,7 +621,7 @@ export async function updateActivityVerbositySettingsMessage(
|
|
|
621
621
|
);
|
|
622
622
|
}
|
|
623
623
|
|
|
624
|
-
|
|
624
|
+
async function updateTimeInjectionModeSettingsMessage(
|
|
625
625
|
deps: TelegramSettingsMenuCallbackDeps,
|
|
626
626
|
): Promise<void> {
|
|
627
627
|
const mode = deps.getTimeInjectionMode();
|
|
@@ -631,7 +631,7 @@ export async function updateTimeInjectionModeSettingsMessage(
|
|
|
631
631
|
);
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
-
|
|
634
|
+
async function updateVoiceReplyModeSettingsMessage(
|
|
635
635
|
deps: TelegramSettingsMenuCallbackDeps,
|
|
636
636
|
): Promise<void> {
|
|
637
637
|
const mode = deps.getVoiceReplyMode();
|
|
@@ -160,6 +160,7 @@ export function buildStatusReplyMarkup(
|
|
|
160
160
|
queueItemCount = 0,
|
|
161
161
|
sectionRegistry?: TelegramSectionRegistry,
|
|
162
162
|
isVoiceReplyActive?: boolean,
|
|
163
|
+
pendingCancellationCount = 0,
|
|
163
164
|
): TelegramReplyMarkup {
|
|
164
165
|
const rows: Array<Array<{ text: string; callback_data: string }>> = [];
|
|
165
166
|
rows.push([
|
|
@@ -188,6 +189,10 @@ export function buildStatusReplyMarkup(
|
|
|
188
189
|
callback_data: "menu:queue",
|
|
189
190
|
},
|
|
190
191
|
]);
|
|
192
|
+
if (pendingCancellationCount > 0) rows.push([{
|
|
193
|
+
text: `❌ Pending cancellations: ${pendingCancellationCount}`,
|
|
194
|
+
callback_data: "reroutecancel:review:open",
|
|
195
|
+
}]);
|
|
191
196
|
if (sectionRegistry) {
|
|
192
197
|
const sectionRows = getTelegramSectionMainMenuRows(sectionRegistry);
|
|
193
198
|
for (const row of sectionRows) {
|
|
@@ -210,6 +215,7 @@ export function buildTelegramStatusMenuRenderPayload(
|
|
|
210
215
|
queueItemCount = 0,
|
|
211
216
|
sectionRegistry?: TelegramSectionRegistry,
|
|
212
217
|
isVoiceReplyActive?: boolean,
|
|
218
|
+
pendingCancellationCount = 0,
|
|
213
219
|
): TelegramMenuRenderPayload {
|
|
214
220
|
return {
|
|
215
221
|
nextMode: "status",
|
|
@@ -221,6 +227,7 @@ export function buildTelegramStatusMenuRenderPayload(
|
|
|
221
227
|
queueItemCount,
|
|
222
228
|
sectionRegistry,
|
|
223
229
|
isVoiceReplyActive,
|
|
230
|
+
pendingCancellationCount,
|
|
224
231
|
),
|
|
225
232
|
};
|
|
226
233
|
}
|
|
@@ -234,6 +241,7 @@ export async function updateTelegramStatusMessage(
|
|
|
234
241
|
queueItemCount = 0,
|
|
235
242
|
sectionRegistry?: TelegramSectionRegistry,
|
|
236
243
|
isVoiceReplyActive?: boolean,
|
|
244
|
+
pendingCancellationCount = 0,
|
|
237
245
|
): Promise<void> {
|
|
238
246
|
await editTelegramMenuMessage(
|
|
239
247
|
state,
|
|
@@ -244,6 +252,7 @@ export async function updateTelegramStatusMessage(
|
|
|
244
252
|
queueItemCount,
|
|
245
253
|
sectionRegistry,
|
|
246
254
|
isVoiceReplyActive,
|
|
255
|
+
pendingCancellationCount,
|
|
247
256
|
),
|
|
248
257
|
deps,
|
|
249
258
|
);
|
|
@@ -258,6 +267,7 @@ export function sendTelegramStatusMessage(
|
|
|
258
267
|
queueItemCount = 0,
|
|
259
268
|
sectionRegistry?: TelegramSectionRegistry,
|
|
260
269
|
isVoiceReplyActive?: boolean,
|
|
270
|
+
pendingCancellationCount = 0,
|
|
261
271
|
): Promise<number | undefined> {
|
|
262
272
|
return sendTelegramMenuMessage(
|
|
263
273
|
state,
|
|
@@ -268,6 +278,7 @@ export function sendTelegramStatusMessage(
|
|
|
268
278
|
queueItemCount,
|
|
269
279
|
sectionRegistry,
|
|
270
280
|
isVoiceReplyActive,
|
|
281
|
+
pendingCancellationCount,
|
|
271
282
|
),
|
|
272
283
|
deps,
|
|
273
284
|
);
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
export interface TelegramThinkingMenuCallbackDeps {
|
|
21
21
|
setThinkingLevel: (level: ThinkingLevel) => void;
|
|
22
22
|
getCurrentThinkingLevel: () => ThinkingLevel;
|
|
23
|
-
|
|
23
|
+
updateThinkingMenuMessage: () => Promise<void>;
|
|
24
24
|
answerCallbackQuery: (
|
|
25
25
|
callbackQueryId: string,
|
|
26
26
|
text?: string,
|
|
@@ -112,7 +112,7 @@ export async function handleTelegramThinkingMenuCallbackAction(
|
|
|
112
112
|
return true;
|
|
113
113
|
}
|
|
114
114
|
deps.setThinkingLevel(action.level);
|
|
115
|
-
await deps.
|
|
115
|
+
await deps.updateThinkingMenuMessage();
|
|
116
116
|
await deps.answerCallbackQuery(
|
|
117
117
|
callbackQueryId,
|
|
118
118
|
`Thinking: ${deps.getCurrentThinkingLevel()}`,
|
|
@@ -255,6 +255,7 @@ export interface TelegramMenuActionRuntimeDeps<
|
|
|
255
255
|
getActiveModel: (ctx: TContext) => TModel | undefined;
|
|
256
256
|
getThinkingLevel: () => ThinkingLevel;
|
|
257
257
|
getQueueItemCount?: () => number;
|
|
258
|
+
getPendingCancellationCount?: () => number;
|
|
258
259
|
buildStatusHtml: (ctx: TContext) => string;
|
|
259
260
|
storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
|
|
260
261
|
isIdle: (ctx: TContext) => boolean;
|
|
@@ -702,7 +703,7 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
702
703
|
deps.updateStatus(ctx);
|
|
703
704
|
},
|
|
704
705
|
getCurrentThinkingLevel: deps.getThinkingLevel,
|
|
705
|
-
|
|
706
|
+
updateThinkingMenuMessage: () => deps.updateThinkingMenuMessage(state, ctx),
|
|
706
707
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
707
708
|
isVoiceReplyActive: deps.isVoiceReplyActive,
|
|
708
709
|
},
|
|
@@ -782,6 +783,7 @@ export function createTelegramMenuActionRuntimeWithStateBuilder<
|
|
|
782
783
|
getActiveModel: deps.getActiveModel,
|
|
783
784
|
getThinkingLevel: deps.getThinkingLevel,
|
|
784
785
|
getQueueItemCount: deps.getQueueItemCount,
|
|
786
|
+
getPendingCancellationCount: deps.getPendingCancellationCount,
|
|
785
787
|
buildStatusHtml: deps.buildStatusHtml,
|
|
786
788
|
storeModelMenuState: deps.storeModelMenuState,
|
|
787
789
|
isIdle: deps.isIdle,
|
|
@@ -819,6 +821,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
819
821
|
deps.getQueueItemCount?.() ?? 0,
|
|
820
822
|
deps.sectionRegistry,
|
|
821
823
|
deps.isVoiceReplyActive?.(),
|
|
824
|
+
deps.getPendingCancellationCount?.() ?? 0,
|
|
822
825
|
),
|
|
823
826
|
sendStatusMessage: (chatId, replyToMessageId, ctx, threadId) =>
|
|
824
827
|
openTelegramStatusMenu({
|
|
@@ -852,6 +855,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
852
855
|
queueItemCount,
|
|
853
856
|
deps.sectionRegistry,
|
|
854
857
|
deps.isVoiceReplyActive?.(),
|
|
858
|
+
deps.getPendingCancellationCount?.() ?? 0,
|
|
855
859
|
),
|
|
856
860
|
storeModelMenuState: deps.storeModelMenuState,
|
|
857
861
|
}),
|
|
@@ -51,7 +51,7 @@ export function getTelegramOutboundAttachmentByteLimitFromEnv(
|
|
|
51
51
|
return defaultValue;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
const TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES =
|
|
55
55
|
getTelegramOutboundAttachmentByteLimitFromEnv(process.env, [
|
|
56
56
|
"PI_TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES",
|
|
57
57
|
"TELEGRAM_MAX_ATTACHMENT_SIZE_BYTES",
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
TelegramInlineKeyboardMarkup,
|
|
12
12
|
} from "./keyboard.ts";
|
|
13
13
|
import {
|
|
14
|
+
getTelegramActionString as getTelegramButtonString,
|
|
14
15
|
parseTelegramActionPayloadRows,
|
|
15
16
|
parseTelegramButtonPayloadRows,
|
|
16
17
|
replaceTelegramButtonFences,
|
|
@@ -101,16 +102,6 @@ function normalizeMarkdownAfterButtonExtraction(markdown: string): string {
|
|
|
101
102
|
return markdown.replace(/\n{3,}/g, "\n\n").trim();
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
function getTelegramButtonString(
|
|
105
|
-
payload: Record<string, unknown>,
|
|
106
|
-
key: string,
|
|
107
|
-
): string | undefined {
|
|
108
|
-
const value = payload[key];
|
|
109
|
-
if (typeof value !== "string") return undefined;
|
|
110
|
-
const trimmed = value.trim();
|
|
111
|
-
return trimmed || undefined;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
105
|
function parseTelegramButtonAction(
|
|
115
106
|
payload: Record<string, unknown>,
|
|
116
107
|
): TelegramOutboundButtonAction | undefined {
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Owns top-level assistant action comment extraction, attribute parsing, and markup stripping shared by voice and outbound delivery
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { isWireRecord as isTelegramActionPayload } from "./wire.ts";
|
|
8
|
+
|
|
7
9
|
export interface TelegramTopLevelHtmlComment {
|
|
8
10
|
raw: string;
|
|
9
11
|
content: string;
|
|
@@ -150,7 +152,7 @@ export function replaceTopLevelHtmlComments(
|
|
|
150
152
|
return result + markdown.slice(offset);
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
function findTopLevelOpenOrPartialHtmlCommentIndex(
|
|
154
156
|
markdown: string,
|
|
155
157
|
): number {
|
|
156
158
|
const { openCommentStart } = collectTopLevelHtmlComments(markdown);
|
|
@@ -180,23 +182,6 @@ export function findTopLevelOpenOrPartialHtmlCommentIndex(
|
|
|
180
182
|
return -1;
|
|
181
183
|
}
|
|
182
184
|
|
|
183
|
-
export function parseTopLevelTelegramComment(
|
|
184
|
-
comment: TelegramTopLevelHtmlComment,
|
|
185
|
-
command: string,
|
|
186
|
-
): { head: string; body?: string } | undefined {
|
|
187
|
-
let normalizedContent = comment.content.replace(/^\s+/, "");
|
|
188
|
-
normalizedContent = normalizedContent.replace(/^!/, "");
|
|
189
|
-
const [rawHead = "", ...bodyLines] = normalizedContent.split(/\r?\n/);
|
|
190
|
-
let head = rawHead.trimStart();
|
|
191
|
-
if (!head.startsWith(command)) return undefined;
|
|
192
|
-
const nextChar = head[command.length];
|
|
193
|
-
if (nextChar !== undefined && !/\s|:/.test(nextChar)) return undefined;
|
|
194
|
-
return {
|
|
195
|
-
head: head.slice(command.length),
|
|
196
|
-
...(bodyLines.length > 0 ? { body: bodyLines.join("\n") } : {}),
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
185
|
function parseTolerantTelegramAttributes(
|
|
201
186
|
source: string,
|
|
202
187
|
names: readonly string[],
|
|
@@ -214,10 +199,6 @@ function parseTolerantTelegramAttributes(
|
|
|
214
199
|
return Object.keys(attributes).length > 0 ? attributes : undefined;
|
|
215
200
|
}
|
|
216
201
|
|
|
217
|
-
function isTelegramActionPayload(value: unknown): value is Record<string, unknown> {
|
|
218
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
202
|
function removeTelegramJsonTrailingCommas(source: string): string {
|
|
222
203
|
let normalized = "";
|
|
223
204
|
let inString = false;
|
|
@@ -268,7 +249,7 @@ function looksLikeTelegramNamedJsonObject(
|
|
|
268
249
|
return /^\{\s*"(?:[^"\\]|\\.)*"\s*:/u.test(source.slice(offset));
|
|
269
250
|
}
|
|
270
251
|
|
|
271
|
-
|
|
252
|
+
function parseTelegramActionPayload(
|
|
272
253
|
comment: TelegramTopLevelHtmlComment,
|
|
273
254
|
command: string,
|
|
274
255
|
): Record<string, unknown> | undefined {
|
|
@@ -711,7 +692,7 @@ export interface TelegramVoiceReplyPlan {
|
|
|
711
692
|
rate?: string;
|
|
712
693
|
}
|
|
713
694
|
|
|
714
|
-
function getTelegramActionString(
|
|
695
|
+
export function getTelegramActionString(
|
|
715
696
|
payload: Record<string, unknown>,
|
|
716
697
|
key: string,
|
|
717
698
|
): string | undefined {
|
|
@@ -9,7 +9,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
11
|
import type { TelegramAssistantSegmentEvent } from "./activity.ts";
|
|
12
|
-
import {
|
|
12
|
+
import { resolveTelegramAttachmentsDir } from "./paths.ts";
|
|
13
13
|
import * as Replies from "./replies.ts";
|
|
14
14
|
import type { TelegramPreparedPreviewDelivery } from "./preview.ts";
|
|
15
15
|
import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
|
|
@@ -202,7 +202,7 @@ export function registerTelegramOutboundHandler(
|
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
|
|
205
|
+
function getTelegramOutboundProgrammaticHandlers(
|
|
206
206
|
kind: string,
|
|
207
207
|
): TelegramOutboundProgrammaticHandler[] {
|
|
208
208
|
const registry = getOrCreateOutboundHandlerRegistry();
|
|
@@ -451,7 +451,7 @@ function getVoiceReplyTemplateValues(
|
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
function getDefaultTelegramVoiceTempDir(): string {
|
|
454
|
-
return
|
|
454
|
+
return resolveTelegramAttachmentsDir();
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
async function generateTelegramVoiceReplyFileWithHandler(
|
|
@@ -598,7 +598,7 @@ async function transformTelegramOutboundTextWithHandler(
|
|
|
598
598
|
return result.stdout.trim() || text;
|
|
599
599
|
}
|
|
600
600
|
|
|
601
|
-
|
|
601
|
+
async function transformTelegramOutboundText(
|
|
602
602
|
text: string,
|
|
603
603
|
options: {
|
|
604
604
|
handlers?: TelegramOutboundHandlerConfig[];
|
|
@@ -658,7 +658,7 @@ async function transformTelegramOutboundReplyMarkup<TReplyMarkup>(
|
|
|
658
658
|
return { ...replyMarkup, inline_keyboard: translatedRows } as TReplyMarkup;
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
-
|
|
661
|
+
async function transformTelegramOutboundTextReply<
|
|
662
662
|
TReplyMarkup = unknown,
|
|
663
663
|
>(
|
|
664
664
|
text: string,
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
* Zones: telemetry paths, filesystem, runtime identity
|
|
4
4
|
* Owns agent-dir detection and extension-local path derivation
|
|
5
5
|
*
|
|
6
|
-
* This domain is
|
|
7
|
-
*
|
|
8
|
-
* state, or import
|
|
6
|
+
* This domain is path-only: it resolves directories and file paths from
|
|
7
|
+
* environment and runtime identity. Its only filesystem read canonicalizes the
|
|
8
|
+
* existing agent-dir prefix; it does not read config, manage state, or import
|
|
9
|
+
* broader Telegram domains.
|
|
9
10
|
*/
|
|
10
11
|
import { createHash } from "node:crypto";
|
|
12
|
+
import { realpathSync } from "node:fs";
|
|
11
13
|
import { homedir } from "node:os";
|
|
12
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
14
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
13
15
|
|
|
14
16
|
export const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
|
|
15
17
|
|
|
@@ -62,12 +64,56 @@ export function resolveTelegramConfigPath(): string {
|
|
|
62
64
|
return join(resolveAgentDir(), "telegram.json");
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Resolve symlinks in the longest existing prefix of `path` and keep the missing suffix. Strict journal reads require
|
|
69
|
+
* canonical anchors, so a symlinked agent directory (for example macOS `/var` → `/private/var`) must not leak in.
|
|
70
|
+
* Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. Windows keeps its
|
|
71
|
+
* spelling: strict journal reads are POSIX-only there, and resolving a drive-less root would add a drive letter.
|
|
72
|
+
*/
|
|
73
|
+
function canonicalizeExistingPrefix(path: string): string {
|
|
74
|
+
if (!isAbsolute(path) || process.platform === "win32") return path;
|
|
75
|
+
const absolute = resolve(path);
|
|
76
|
+
const missing: string[] = [];
|
|
77
|
+
for (let current = absolute; ; current = dirname(current)) {
|
|
78
|
+
try { return join(realpathSync(current), ...missing.reverse()); }
|
|
79
|
+
catch (error) {
|
|
80
|
+
if ((error as { code?: unknown }).code !== "ENOENT" || dirname(current) === current) return absolute;
|
|
81
|
+
missing.push(basename(current));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Telegram bridge temporary directory (<agentDir>/tmp/pi-telegram); releases before 0.52.0 used `tmp/telegram`. */
|
|
66
87
|
export function resolveTelegramTempDir(agentDir = resolveAgentDir()): string {
|
|
67
|
-
return join(agentDir, "tmp", "telegram");
|
|
88
|
+
return join(canonicalizeExistingPrefix(agentDir), "tmp", "pi-telegram");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Consolidated-root service artifacts (transaction guards, staging and IPC), never session custody. */
|
|
92
|
+
export function resolveTelegramRuntimeDir(agentDir = resolveAgentDir()): string {
|
|
93
|
+
return join(resolveTelegramTempDir(agentDir), "runtime");
|
|
68
94
|
}
|
|
69
95
|
|
|
70
|
-
/**
|
|
96
|
+
/** Rotated shared runtime event segments, outside the persistent root-file census. */
|
|
97
|
+
export function resolveTelegramRuntimeLogsDir(agentDir = resolveAgentDir()): string {
|
|
98
|
+
return join(resolveTelegramTempDir(agentDir), "logs");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Consolidated runtime state for every logical profile (`tmp/pi-telegram/state.json`). */
|
|
102
|
+
export function resolveTelegramStatePath(agentDir = resolveAgentDir()): string {
|
|
103
|
+
return join(resolveTelegramTempDir(agentDir), "state.json");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Flat attachment scratch directory (<agentDir>/tmp/pi-telegram/attachments). */
|
|
107
|
+
export function resolveTelegramAttachmentsDir(agentDir = resolveAgentDir()): string {
|
|
108
|
+
return join(resolveTelegramTempDir(agentDir), "attachments");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Read-only pre-0.52.0 ownership file, consulted only to refuse a second poller beside a live older leader. */
|
|
112
|
+
export function resolveLegacyTelegramOwnersPath(agentDir = resolveAgentDir()): string {
|
|
113
|
+
return join(agentDir, "tmp", "telegram", "owners.json");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Telegram transport ownership store (<agentDir>/tmp/pi-telegram/owners.json). */
|
|
71
117
|
export function resolveTelegramOwnersPath(): string {
|
|
72
118
|
return join(resolveTelegramTempDir(), "owners.json");
|
|
73
119
|
}
|
|
@@ -89,19 +135,17 @@ export function resolveTelegramProfileTempFilePath(
|
|
|
89
135
|
);
|
|
90
136
|
}
|
|
91
137
|
|
|
92
|
-
export function getTelegramDiagnosticsDisplayPaths(
|
|
138
|
+
export function getTelegramDiagnosticsDisplayPaths(_profileName?: string): {
|
|
93
139
|
state: string;
|
|
94
140
|
logs: string;
|
|
95
141
|
} {
|
|
96
|
-
const suffix = getTelegramProfilePathSuffix(profileName);
|
|
97
|
-
const profileSlug = suffix.slice(1);
|
|
98
142
|
return {
|
|
99
|
-
state:
|
|
100
|
-
logs:
|
|
143
|
+
state: "~/.pi/agent/tmp/pi-telegram/state.json",
|
|
144
|
+
logs: "~/.pi/agent/tmp/pi-telegram/logs.jsonl",
|
|
101
145
|
};
|
|
102
146
|
}
|
|
103
147
|
|
|
104
|
-
/** Durable Workspace admission ledger (<agentDir>/tmp/telegram/workspace-admission[.<profile>].json). */
|
|
148
|
+
/** Durable Workspace admission ledger (<agentDir>/tmp/pi-telegram/workspace-admission[.<profile>].json). */
|
|
105
149
|
export function resolveTelegramWorkspaceAdmissionPath(
|
|
106
150
|
agentDir = resolveAgentDir(),
|
|
107
151
|
profileName?: string,
|
|
@@ -121,6 +165,31 @@ export function resolveTelegramWorkspaceAdmissionPathForProfile(
|
|
|
121
165
|
return resolveTelegramWorkspaceAdmissionPath(resolveAgentDir(), profileName);
|
|
122
166
|
}
|
|
123
167
|
|
|
168
|
+
/** Consolidated non-session service journal (`journals/<kind>.<profile sha256>.json`) and its runtime namespace. */
|
|
169
|
+
export function resolveTelegramServiceJournalStorage(
|
|
170
|
+
kind: "thread-cleanup" | "channel-posts",
|
|
171
|
+
agentDir = resolveAgentDir(),
|
|
172
|
+
profileName = TELEGRAM_DEFAULT_PROFILE_NAME,
|
|
173
|
+
): { path: string; runtimeDir: string } {
|
|
174
|
+
const profileHash = createHash("sha256").update(profileName).digest("hex");
|
|
175
|
+
return {
|
|
176
|
+
path: join(resolveTelegramTempDir(agentDir), "journals", `${kind}.${profileHash}.json`),
|
|
177
|
+
runtimeDir: resolveTelegramRuntimeDir(agentDir),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Bind one non-session journal to its exact colocated-root service namespace, never a format fallback. */
|
|
182
|
+
export function getTelegramJournalPublicationPaths(path: string, runtimeDir?: string): {
|
|
183
|
+
transactionPath: string;
|
|
184
|
+
temporaryBasePath: string;
|
|
185
|
+
} {
|
|
186
|
+
if (runtimeDir === undefined) return { transactionPath: `${path}.transaction`, temporaryBasePath: path };
|
|
187
|
+
requireTelegramStoragePathReference(path, resolve(path));
|
|
188
|
+
requireTelegramStoragePathReference(runtimeDir, join(dirname(dirname(path)), "runtime"));
|
|
189
|
+
if (basename(dirname(path)) !== "journals") throw new Error("Telegram service journal must use its journals namespace.");
|
|
190
|
+
return { transactionPath: join(runtimeDir, `${basename(path)}.transaction`), temporaryBasePath: join(runtimeDir, basename(path)) };
|
|
191
|
+
}
|
|
192
|
+
|
|
124
193
|
/** Durable inactive Thread cleanup work-set journal. */
|
|
125
194
|
export function resolveTelegramThreadCleanupWorkPath(
|
|
126
195
|
agentDir = resolveAgentDir(),
|
|
@@ -137,7 +206,7 @@ export function resolveTelegramChannelPostJournalPath(
|
|
|
137
206
|
return resolveTelegramProfileTempFilePath("channel-posts", "json", agentDir, profileName);
|
|
138
207
|
}
|
|
139
208
|
|
|
140
|
-
/** Durable inbound update journal (<agentDir>/tmp/telegram/inbox[.<profile>].json). */
|
|
209
|
+
/** Durable inbound update journal (<agentDir>/tmp/pi-telegram/inbox[.<profile>].json). */
|
|
141
210
|
export function resolveTelegramUpdateJournalPath(
|
|
142
211
|
agentDir = resolveAgentDir(),
|
|
143
212
|
profileName?: string,
|
|
@@ -178,7 +247,98 @@ export function resolveTelegramFollowerJournalPath(
|
|
|
178
247
|
);
|
|
179
248
|
}
|
|
180
249
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
250
|
+
const TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH = 200;
|
|
251
|
+
const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\..*)?$/u;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Directory name for a session id. Lowercase letters, digits, `_`, `-` and inner `.` stay verbatim, so every real
|
|
255
|
+
* (UUID-shaped) id is unchanged; any other UTF-8 byte is percent-encoded. Uppercase is encoded so case-insensitive
|
|
256
|
+
* filesystems never merge two sessions; a leading or trailing dot and Windows reserved names are encoded so the name
|
|
257
|
+
* can never be `.`/`..`, hidden, or a device. The encoding is reversible; an empty or over-long result is refused.
|
|
258
|
+
*/
|
|
259
|
+
export function encodeTelegramSessionDirectoryName(sessionId: string): string | undefined {
|
|
260
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) return undefined;
|
|
261
|
+
const bytes = Buffer.from(sessionId, "utf8");
|
|
262
|
+
if (bytes.toString("utf8") !== sessionId) return undefined;
|
|
263
|
+
let name = "";
|
|
264
|
+
bytes.forEach((byte, index) => {
|
|
265
|
+
const char = String.fromCharCode(byte);
|
|
266
|
+
const safe = /[a-z0-9_-]/u.test(char) || (char === "." && index > 0 && index < bytes.length - 1);
|
|
267
|
+
name += safe ? char : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
268
|
+
});
|
|
269
|
+
if (WINDOWS_RESERVED_NAME.test(name)) name = `%${name.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}${name.slice(1)}`;
|
|
270
|
+
return name.length <= TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH ? name : undefined;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Inverse of `encodeTelegramSessionDirectoryName`; undefined unless the name is exactly canonical. */
|
|
274
|
+
export function decodeTelegramSessionDirectoryName(name: string): string | undefined {
|
|
275
|
+
if (!/^[a-z0-9_.%A-F-]+$/u.test(name)) return undefined;
|
|
276
|
+
const bytes: number[] = [];
|
|
277
|
+
for (let index = 0; index < name.length; index++) {
|
|
278
|
+
if (name[index] === "%") {
|
|
279
|
+
const hex = name.slice(index + 1, index + 3);
|
|
280
|
+
if (!/^[0-9A-F]{2}$/u.test(hex)) return undefined;
|
|
281
|
+
bytes.push(Number.parseInt(hex, 16));
|
|
282
|
+
index += 2;
|
|
283
|
+
} else bytes.push(name.charCodeAt(index));
|
|
284
|
+
}
|
|
285
|
+
const decoded = Buffer.from(bytes).toString("utf8");
|
|
286
|
+
return encodeTelegramSessionDirectoryName(decoded) === name ? decoded : undefined;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Root of every session folder (<agentDir>/tmp/pi-telegram/sessions). */
|
|
290
|
+
export function resolveTelegramSessionsDir(agentDir = resolveAgentDir()): string {
|
|
291
|
+
return join(resolveTelegramTempDir(agentDir), "sessions");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** One session's folder (<agentDir>/tmp/pi-telegram/sessions/<session id>). */
|
|
295
|
+
export function resolveTelegramSessionDir(sessionId: string, agentDir = resolveAgentDir()): string {
|
|
296
|
+
const name = encodeTelegramSessionDirectoryName(sessionId);
|
|
297
|
+
if (!name) throw new Error("Telegram session directory requires a usable session id.");
|
|
298
|
+
return join(resolveTelegramSessionsDir(agentDir), name);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Hash naming one recipient incarnation inside its session folder; unchanged from the former follower inbox. */
|
|
302
|
+
export function getTelegramRecipientJournalHash(recipientBindingKey: string): string {
|
|
303
|
+
if (!recipientBindingKey) throw new Error("Telegram session journal binding key is required.");
|
|
304
|
+
return createHash("sha256").update(recipientBindingKey).digest("hex").slice(0, 16);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Durable per-session journal (`sessions/<session id>/journal.<hash>[.<profile>].json`); the name never reveals a role. */
|
|
308
|
+
export function resolveTelegramSessionJournalPath(
|
|
309
|
+
sessionId: string,
|
|
310
|
+
recipientBindingKey: string,
|
|
311
|
+
agentDir = resolveAgentDir(),
|
|
312
|
+
profileName?: string,
|
|
313
|
+
): string {
|
|
314
|
+
return join(
|
|
315
|
+
resolveTelegramSessionDir(sessionId, agentDir),
|
|
316
|
+
`journal.${getTelegramRecipientJournalHash(recipientBindingKey)}${getTelegramProfilePathSuffix(profileName)}.json`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Leader polling journal hosted by the session that first led (`sessions/<id>/inbox[.<profile>].json`); owners.json names it. */
|
|
321
|
+
export function resolveTelegramSessionPollingJournalPath(
|
|
322
|
+
sessionId: string,
|
|
323
|
+
agentDir = resolveAgentDir(),
|
|
324
|
+
profileName?: string,
|
|
325
|
+
): string {
|
|
326
|
+
return join(resolveTelegramSessionDir(sessionId, agentDir), `inbox${getTelegramProfilePathSuffix(profileName)}.json`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Accept only a canonical `sessions/<id>/inbox[.<profile>].json` beneath this agent's runtime. */
|
|
330
|
+
export function isTelegramSessionPollingJournalPath(path: string, agentDir = resolveAgentDir()): boolean {
|
|
331
|
+
const parts = relative(resolveTelegramSessionsDir(agentDir), path).split(sep);
|
|
332
|
+
return isAbsolute(path) && parts.length === 2 && decodeTelegramSessionDirectoryName(parts[0]!) !== undefined &&
|
|
333
|
+
/^inbox(?:\.[a-zA-Z0-9._-]+)?\.json$/u.test(parts[1]!);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Runtime event log (<agentDir>/tmp/pi-telegram/logs.jsonl). */
|
|
337
|
+
export function resolveTelegramRuntimeLogPath(agentDir = resolveAgentDir()): string {
|
|
338
|
+
return resolveTelegramProfileTempFilePath("logs", "jsonl", agentDir);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Bounded previous shared log segment below `logs/`. */
|
|
342
|
+
export function resolveTelegramPreviousSharedRuntimeLogPath(agentDir = resolveAgentDir()): string {
|
|
343
|
+
return join(resolveTelegramRuntimeLogsDir(agentDir), "logs._prev.jsonl");
|
|
184
344
|
}
|
|
@@ -263,8 +263,11 @@ export function getExtensionContextCwd(ctx: ExtensionContext): string {
|
|
|
263
263
|
return ctx.cwd;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
export function getExtensionContextSessionId(ctx: ExtensionContext): string
|
|
267
|
-
|
|
266
|
+
export function getExtensionContextSessionId(ctx: ExtensionContext): string;
|
|
267
|
+
export function getExtensionContextSessionId(ctx: ExtensionContext | undefined): string | undefined;
|
|
268
|
+
export function getExtensionContextSessionId(ctx: ExtensionContext | undefined): string | undefined {
|
|
269
|
+
// Minimal hosts and stale contexts may omit a session manager; absence is not an invented ID.
|
|
270
|
+
return ctx?.sessionManager?.getSessionId?.();
|
|
268
271
|
}
|
|
269
272
|
|
|
270
273
|
export function isExtensionContextIdle(ctx: ExtensionContext): boolean {
|