@llblab/pi-telegram 0.52.2 → 0.53.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/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +11 -0
- package/README.md +7 -11
- package/api/commands.ts +3 -0
- package/dist/api/commands.d.ts +1 -1
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +15 -42
- package/dist/lib/activity.d.ts +9 -0
- package/dist/lib/activity.js +39 -10
- package/dist/lib/agent-messages.js +7 -3
- package/dist/lib/bindings.d.ts +43 -5
- package/dist/lib/bindings.js +163 -20
- package/dist/lib/bus-api.d.ts +12 -0
- package/dist/lib/bus-api.js +182 -79
- package/dist/lib/bus-follower.d.ts +111 -31
- package/dist/lib/bus-follower.js +1324 -359
- package/dist/lib/bus-leader.d.ts +41 -91
- package/dist/lib/bus-leader.js +969 -637
- package/dist/lib/bus-transport.js +16 -9
- package/dist/lib/bus.d.ts +215 -18
- package/dist/lib/bus.js +1048 -327
- package/dist/lib/channel-posts.d.ts +5 -0
- package/dist/lib/channel-posts.js +434 -261
- package/dist/lib/command-templates.d.ts +26 -5
- package/dist/lib/command-templates.js +92 -176
- package/dist/lib/commands.d.ts +168 -82
- package/dist/lib/commands.js +1409 -291
- package/dist/lib/config.d.ts +1 -8
- package/dist/lib/config.js +34 -76
- package/dist/lib/delivery.d.ts +2 -2
- package/dist/lib/delivery.js +16 -16
- package/dist/lib/extension.js +639 -171
- package/dist/lib/generative-app-worker.mjs +19 -7
- package/dist/lib/generative-apps.d.ts +0 -5
- package/dist/lib/generative-apps.js +61 -49
- package/dist/lib/inbound.d.ts +5 -0
- package/dist/lib/inbound.js +35 -59
- package/dist/lib/journal.d.ts +33 -3
- package/dist/lib/journal.js +2063 -782
- package/dist/lib/lifecycle.d.ts +2 -6
- package/dist/lib/lifecycle.js +75 -58
- package/dist/lib/locks.d.ts +29 -3
- package/dist/lib/locks.js +289 -108
- package/dist/lib/logging.js +50 -14
- package/dist/lib/media.d.ts +22 -1
- package/dist/lib/media.js +46 -28
- package/dist/lib/menu-model.d.ts +19 -5
- package/dist/lib/menu-model.js +53 -11
- package/dist/lib/menu-queue.d.ts +5 -4
- package/dist/lib/menu-queue.js +45 -63
- package/dist/lib/menu-settings.d.ts +5 -4
- package/dist/lib/menu-settings.js +114 -78
- package/dist/lib/menu-status.d.ts +1 -1
- package/dist/lib/menu-status.js +8 -25
- package/dist/lib/menu-thinking.d.ts +6 -1
- package/dist/lib/menu-thinking.js +14 -21
- package/dist/lib/menu.d.ts +8 -61
- package/dist/lib/menu.js +92 -164
- package/dist/lib/model.d.ts +1 -1
- package/dist/lib/model.js +2 -16
- package/dist/lib/outbound-attachments.d.ts +6 -27
- package/dist/lib/outbound-attachments.js +45 -38
- package/dist/lib/outbound-buttons.js +22 -16
- package/dist/lib/outbound-markup.d.ts +0 -2
- package/dist/lib/outbound-markup.js +51 -64
- package/dist/lib/outbound-voice.js +6 -3
- package/dist/lib/outbound.d.ts +6 -6
- package/dist/lib/outbound.js +61 -51
- package/dist/lib/ownership.js +6 -2
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +37 -21
- package/dist/lib/pi.d.ts +2 -0
- package/dist/lib/pi.js +8 -1
- package/dist/lib/polling.d.ts +21 -37
- package/dist/lib/polling.js +59 -50
- package/dist/lib/preview.js +39 -19
- package/dist/lib/process-identity.d.ts +24 -0
- package/dist/lib/process-identity.js +133 -0
- package/dist/lib/prompts.js +2 -7
- package/dist/lib/queue.d.ts +14 -16
- package/dist/lib/queue.js +249 -126
- package/dist/lib/recovery.d.ts +2 -83
- package/dist/lib/recovery.js +20 -274
- package/dist/lib/replies.d.ts +16 -24
- package/dist/lib/replies.js +99 -116
- package/dist/lib/routing.d.ts +120 -7
- package/dist/lib/routing.js +4667 -1152
- package/dist/lib/runtime.d.ts +1 -11
- package/dist/lib/sections.d.ts +4 -29
- package/dist/lib/sections.js +7 -20
- package/dist/lib/skills.js +4 -3
- package/dist/lib/status.js +86 -57
- package/dist/lib/sync.d.ts +1 -3
- package/dist/lib/sync.js +67 -43
- package/dist/lib/target.d.ts +4 -0
- package/dist/lib/target.js +18 -0
- package/dist/lib/telegram-api.d.ts +18 -13
- package/dist/lib/telegram-api.js +148 -53
- package/dist/lib/text-groups.js +4 -39
- package/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/dist/lib/thread-cleanup-manager.js +369 -186
- package/dist/lib/thread-display.d.ts +1 -2
- package/dist/lib/thread-display.js +58 -38
- package/dist/lib/thread-naming.d.ts +28 -8
- package/dist/lib/thread-naming.js +175 -66
- package/dist/lib/thread-reconciler.d.ts +42 -16
- package/dist/lib/thread-reconciler.js +90 -50
- package/dist/lib/threads.d.ts +82 -20
- package/dist/lib/threads.js +2247 -855
- package/dist/lib/time-injection.js +1 -2
- package/dist/lib/turns.js +1 -19
- package/dist/lib/updates.d.ts +169 -80
- package/dist/lib/updates.js +2418 -641
- package/dist/lib/voice.d.ts +7 -22
- package/dist/lib/voice.js +7 -22
- package/dist/lib/wire.js +1 -1
- package/dist/lib/workspace-admission.d.ts +3 -1
- package/dist/lib/workspace-admission.js +425 -211
- package/dist/lib/workspace-identity.js +8 -13
- package/dist/lib/workspace-retirement.d.ts +1 -1
- package/dist/lib/workspace-retirement.js +328 -140
- package/dist/lib/workspace-slots.js +8 -4
- package/dist/package.json +1 -1
- package/docs/architecture.md +31 -29
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +15 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +29 -1
- package/docs/ui-style.md +25 -8
- package/lib/activity-verbosity.ts +59 -82
- package/lib/activity.ts +146 -102
- package/lib/agent-messages.ts +12 -5
- package/lib/bindings.ts +389 -82
- package/lib/bus-api.ts +263 -102
- package/lib/bus-follower.ts +2440 -601
- package/lib/bus-leader.ts +2209 -1290
- package/lib/bus-transport.ts +46 -12
- package/lib/bus.ts +1673 -444
- package/lib/channel-posts.ts +848 -327
- package/lib/command-templates.ts +152 -265
- package/lib/commands.ts +2398 -555
- package/lib/config.ts +177 -170
- package/lib/delivery.ts +41 -29
- package/lib/extension.ts +1206 -433
- package/lib/generative-app-worker.mjs +19 -7
- package/lib/generative-apps.ts +324 -211
- package/lib/inbound.ts +95 -97
- package/lib/journal.ts +5311 -1763
- package/lib/keyboard.ts +1 -3
- package/lib/lifecycle.ts +128 -78
- package/lib/locks.ts +677 -203
- package/lib/logging.ts +147 -58
- package/lib/media.ts +82 -43
- package/lib/menu-model.ts +85 -18
- package/lib/menu-queue.ts +88 -129
- package/lib/menu-settings.ts +260 -155
- package/lib/menu-status.ts +20 -60
- package/lib/menu-thinking.ts +41 -56
- package/lib/menu.ts +112 -285
- package/lib/model.ts +8 -22
- package/lib/outbound-attachments.ts +156 -125
- package/lib/outbound-buttons.ts +76 -49
- package/lib/outbound-markup.ts +96 -94
- package/lib/outbound-voice.ts +33 -27
- package/lib/outbound.ts +125 -125
- package/lib/ownership.ts +19 -10
- package/lib/paths.ts +148 -49
- package/lib/pi.ts +35 -15
- package/lib/polling.ts +144 -147
- package/lib/preview.ts +93 -40
- package/lib/process-identity.ts +164 -0
- package/lib/prompts.ts +4 -11
- package/lib/queue.ts +511 -300
- package/lib/recovery.ts +50 -454
- package/lib/rendering.ts +1 -3
- package/lib/replies.ts +248 -209
- package/lib/routing.ts +8509 -2186
- package/lib/runtime.ts +1 -12
- package/lib/sections.ts +24 -89
- package/lib/setup.ts +1 -3
- package/lib/skills.ts +9 -4
- package/lib/status.ts +215 -110
- package/lib/sync.ts +448 -361
- package/lib/target.ts +22 -3
- package/lib/telegram-api.ts +302 -127
- package/lib/text-groups.ts +5 -39
- package/lib/thread-cleanup-manager.ts +833 -331
- package/lib/thread-display.ts +213 -97
- package/lib/thread-naming.ts +232 -87
- package/lib/thread-reconciler.ts +155 -72
- package/lib/threads.ts +5318 -1845
- package/lib/time-injection.ts +1 -4
- package/lib/turns.ts +15 -28
- package/lib/updates.ts +5184 -1612
- package/lib/voice.ts +7 -35
- package/lib/wire.ts +1 -1
- package/lib/workspace-admission.ts +996 -416
- package/lib/workspace-identity.ts +14 -17
- package/lib/workspace-retirement.ts +878 -376
- package/lib/workspace-slots.ts +22 -13
- package/package.json +1 -1
- package/scripts/check-downgrade.mjs +0 -472
- package/scripts/measure-workspace.mjs +0 -108
package/dist/lib/polling.js
CHANGED
|
@@ -158,7 +158,10 @@ export function createTelegramDurablePollingRuntimeAssembly(deps) {
|
|
|
158
158
|
}
|
|
159
159
|
catch (error) {
|
|
160
160
|
try {
|
|
161
|
-
deps.recordRuntimeEvent?.("polling", error, {
|
|
161
|
+
deps.recordRuntimeEvent?.("polling", error, {
|
|
162
|
+
phase: "batch-preparation",
|
|
163
|
+
updateCount: updates.length,
|
|
164
|
+
});
|
|
162
165
|
}
|
|
163
166
|
catch {
|
|
164
167
|
// Already-published input must still reach the worker if diagnostics fail.
|
|
@@ -362,8 +365,13 @@ export function startTelegramPollingRuntime(ctx, deps) {
|
|
|
362
365
|
deps.setPollingController(undefined);
|
|
363
366
|
if (!ownsPromise && !ownsController)
|
|
364
367
|
return;
|
|
365
|
-
deps.onPollingStopped?.(controller.signal.aborted
|
|
366
|
-
? "
|
|
368
|
+
deps.onPollingStopped?.(controller.signal.aborted
|
|
369
|
+
? "requested"
|
|
370
|
+
: persistentConflict
|
|
371
|
+
? "persistent-conflict"
|
|
372
|
+
: failed
|
|
373
|
+
? "failed"
|
|
374
|
+
: "completed");
|
|
367
375
|
// Detach the inner promise before outer teardown calls polling.stop().
|
|
368
376
|
if (persistentConflict && !controller.signal.aborted) {
|
|
369
377
|
try {
|
|
@@ -373,12 +381,15 @@ export function startTelegramPollingRuntime(ctx, deps) {
|
|
|
373
381
|
else {
|
|
374
382
|
deps.stopTypingLoop();
|
|
375
383
|
deps.recordRuntimeEvent?.("polling", persistentConflict, {
|
|
376
|
-
phase: "persistent-conflict",
|
|
384
|
+
phase: "persistent-conflict",
|
|
385
|
+
count: persistentConflict.count,
|
|
377
386
|
});
|
|
378
387
|
}
|
|
379
388
|
}
|
|
380
389
|
catch (error) {
|
|
381
|
-
deps.recordRuntimeEvent?.("polling", error, {
|
|
390
|
+
deps.recordRuntimeEvent?.("polling", error, {
|
|
391
|
+
phase: "conflict-stand-down",
|
|
392
|
+
});
|
|
382
393
|
}
|
|
383
394
|
if (deps.getPollingController() || deps.getPollingPromise())
|
|
384
395
|
return;
|
|
@@ -435,7 +446,9 @@ export function createTelegramThreadCapabilityOrchestration(deps) {
|
|
|
435
446
|
const expected = generation;
|
|
436
447
|
return () => expected === generation;
|
|
437
448
|
},
|
|
438
|
-
invalidate() {
|
|
449
|
+
invalidate() {
|
|
450
|
+
generation++;
|
|
451
|
+
},
|
|
439
452
|
};
|
|
440
453
|
const capabilityDeps = {
|
|
441
454
|
lifecycle,
|
|
@@ -543,6 +556,29 @@ function hasTelegramThreadCapabilityBindings(store) {
|
|
|
543
556
|
record.status !== "stale");
|
|
544
557
|
}) ?? false);
|
|
545
558
|
}
|
|
559
|
+
/** Falls back to classic polling, recording a failed restore durably; false once the attempt is stale. */
|
|
560
|
+
async function restoreTelegramClassicPolling(ctx, phase, deps, isCurrent) {
|
|
561
|
+
try {
|
|
562
|
+
await deps.startClassicPolling(ctx);
|
|
563
|
+
return isCurrent();
|
|
564
|
+
}
|
|
565
|
+
catch (classicError) {
|
|
566
|
+
if (!isCurrent())
|
|
567
|
+
return false;
|
|
568
|
+
deps.topicTargetStore.setBotState({
|
|
569
|
+
threadMode: "disabled",
|
|
570
|
+
updatedAtMs: (deps.getNowMs ?? Date.now)(),
|
|
571
|
+
lastReconcileAction: `${phase}-classic-restore-failed`,
|
|
572
|
+
});
|
|
573
|
+
await deps.topicTargetStore.persist();
|
|
574
|
+
if (!isCurrent())
|
|
575
|
+
return false;
|
|
576
|
+
deps.recordEvent("bus", classicError, {
|
|
577
|
+
phase: `${phase}-classic-restore`,
|
|
578
|
+
});
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
546
582
|
export async function applyTelegramThreadCapability(ctx, threadModeEnabled, phase, deps, isCurrent = deps.lifecycle?.capture() ?? (() => true)) {
|
|
547
583
|
if (!isCurrent())
|
|
548
584
|
return;
|
|
@@ -580,26 +616,8 @@ export async function applyTelegramThreadCapability(ctx, threadModeEnabled, phas
|
|
|
580
616
|
deps.setPollingStartedWithTelegramBus(false);
|
|
581
617
|
if (hadLiveThreadTransport)
|
|
582
618
|
deps.suspendLiveThreadTarget?.();
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
if (!isCurrent())
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
catch (classicError) {
|
|
589
|
-
if (!isCurrent())
|
|
590
|
-
return;
|
|
591
|
-
deps.topicTargetStore.setBotState({
|
|
592
|
-
threadMode: "disabled",
|
|
593
|
-
updatedAtMs: (deps.getNowMs ?? Date.now)(),
|
|
594
|
-
lastReconcileAction: `${phase}-classic-restore-failed`,
|
|
595
|
-
});
|
|
596
|
-
await deps.topicTargetStore.persist();
|
|
597
|
-
if (!isCurrent())
|
|
598
|
-
return;
|
|
599
|
-
deps.recordEvent("bus", classicError, {
|
|
600
|
-
phase: `${phase}-classic-restore`,
|
|
601
|
-
});
|
|
602
|
-
}
|
|
619
|
+
if (!(await restoreTelegramClassicPolling(ctx, phase, deps, isCurrent)))
|
|
620
|
+
return;
|
|
603
621
|
}
|
|
604
622
|
deps.updateStatus(ctx);
|
|
605
623
|
return;
|
|
@@ -641,26 +659,8 @@ export async function applyTelegramThreadCapability(ctx, threadModeEnabled, phas
|
|
|
641
659
|
return;
|
|
642
660
|
deps.setTopicModeUnavailable(true);
|
|
643
661
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (!isCurrent())
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
catch (classicError) {
|
|
650
|
-
if (!isCurrent())
|
|
651
|
-
return;
|
|
652
|
-
deps.topicTargetStore.setBotState({
|
|
653
|
-
threadMode: "disabled",
|
|
654
|
-
updatedAtMs: (deps.getNowMs ?? Date.now)(),
|
|
655
|
-
lastReconcileAction: `${phase}-classic-restore-failed`,
|
|
656
|
-
});
|
|
657
|
-
await deps.topicTargetStore.persist();
|
|
658
|
-
if (!isCurrent())
|
|
659
|
-
return;
|
|
660
|
-
deps.recordEvent("bus", classicError, {
|
|
661
|
-
phase: `${phase}-classic-restore`,
|
|
662
|
-
});
|
|
663
|
-
}
|
|
662
|
+
if (!(await restoreTelegramClassicPolling(ctx, phase, deps, isCurrent)))
|
|
663
|
+
return;
|
|
664
664
|
deps.updateStatus(ctx);
|
|
665
665
|
if (threadModeUnavailable)
|
|
666
666
|
return;
|
|
@@ -759,11 +759,17 @@ export function createTelegramThreadAwarePollingPorts(deps) {
|
|
|
759
759
|
};
|
|
760
760
|
const restoreFollowerWithOwner = async (ctx, owner) => {
|
|
761
761
|
if (!(await refreshFollowerState())) {
|
|
762
|
-
deps.recordEvent("bus", "Telegram follower auto-connect skipped: Threaded Mode is not enabled.", {
|
|
762
|
+
deps.recordEvent("bus", "Telegram follower auto-connect skipped: Threaded Mode is not enabled.", {
|
|
763
|
+
phase: "follower-auto-connect-skip",
|
|
764
|
+
reason: "thread-mode-unavailable",
|
|
765
|
+
});
|
|
763
766
|
return undefined;
|
|
764
767
|
}
|
|
765
768
|
if (!deps.hasRememberedWorkspaceBinding?.(ctx)) {
|
|
766
|
-
deps.recordEvent("bus", "Telegram follower auto-connect skipped: no binding for this session.", {
|
|
769
|
+
deps.recordEvent("bus", "Telegram follower auto-connect skipped: no binding for this session.", {
|
|
770
|
+
phase: "follower-auto-connect-skip",
|
|
771
|
+
reason: "session-binding-unavailable",
|
|
772
|
+
});
|
|
767
773
|
return undefined;
|
|
768
774
|
}
|
|
769
775
|
return deps.restoreFollowerWithLeader?.(ctx, owner);
|
|
@@ -898,7 +904,9 @@ export function createTelegramThreadCapabilityMonitor(deps) {
|
|
|
898
904
|
try {
|
|
899
905
|
stop();
|
|
900
906
|
}
|
|
901
|
-
catch {
|
|
907
|
+
catch {
|
|
908
|
+
/* Timer shutdown must not escape the callback. */
|
|
909
|
+
}
|
|
902
910
|
try {
|
|
903
911
|
deps.recordEvent("bus", error, { phase: "capability-monitor" });
|
|
904
912
|
}
|
|
@@ -1115,7 +1123,8 @@ export async function runTelegramPollLoop(deps) {
|
|
|
1115
1123
|
if (consecutiveGetUpdatesConflicts >= TELEGRAM_GET_UPDATES_CONFLICT_STOP_LIMIT) {
|
|
1116
1124
|
throw new TelegramPersistentGetUpdatesConflictError(consecutiveGetUpdatesConflicts);
|
|
1117
1125
|
}
|
|
1118
|
-
await deps.sleep(consecutiveGetUpdatesConflicts <
|
|
1126
|
+
await deps.sleep(consecutiveGetUpdatesConflicts <
|
|
1127
|
+
TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_LIMIT
|
|
1119
1128
|
? TELEGRAM_GET_UPDATES_CONFLICT_FAST_RETRY_MS
|
|
1120
1129
|
: TELEGRAM_GET_UPDATES_CONFLICT_SLOW_RETRY_MS, deps.signal);
|
|
1121
1130
|
};
|
package/dist/lib/preview.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Zones: telegram outbound, native rich markdown drafts
|
|
4
4
|
* Owns safe draft preview selection, runtime updates, and preview finalization
|
|
5
5
|
*/
|
|
6
|
-
import { normalizeTelegramNativeMarkdown } from "./replies.js";
|
|
7
6
|
import { stripTelegramCommentMarkupForPreview } from "./outbound-markup.js";
|
|
7
|
+
import { isTelegramMarkdownFenceClosing, normalizeTelegramNativeMarkdown, parseTelegramMarkdownFenceOpening, } from "./replies.js";
|
|
8
8
|
import { getTelegramTargetThreadParams, } from "./target.js";
|
|
9
9
|
import { shouldSuppressPreviewForVoice } from "./voice.js";
|
|
10
10
|
const TELEGRAM_DRAFT_ID_MAX = 2_147_483_647;
|
|
@@ -73,9 +73,15 @@ export function createTelegramAssistantPreviewRuntime(deps) {
|
|
|
73
73
|
clearPreview: async (chatId, options) => {
|
|
74
74
|
if (controller.getState() !== state || !isDeliveryActive())
|
|
75
75
|
return;
|
|
76
|
-
await controller.prepareClear(chatId, {
|
|
76
|
+
await controller.prepareClear(chatId, {
|
|
77
|
+
...options,
|
|
78
|
+
isDeliveryActive,
|
|
79
|
+
})();
|
|
77
80
|
},
|
|
78
|
-
finalizeMarkdownPreview: prepareTelegramNativeMarkdownPreviewFinalizer({
|
|
81
|
+
finalizeMarkdownPreview: prepareTelegramNativeMarkdownPreviewFinalizer({
|
|
82
|
+
...finalizerDeps,
|
|
83
|
+
isDeliveryActive,
|
|
84
|
+
}),
|
|
79
85
|
};
|
|
80
86
|
},
|
|
81
87
|
...createTelegramAssistantMessagePreviewHooks({
|
|
@@ -130,7 +136,10 @@ export function createTelegramPreviewController(deps) {
|
|
|
130
136
|
createState: () => createTelegramPreviewRuntimeState(),
|
|
131
137
|
resetState: () => {
|
|
132
138
|
generation += 1;
|
|
133
|
-
setState({
|
|
139
|
+
setState({
|
|
140
|
+
...createTelegramPreviewRuntimeState(),
|
|
141
|
+
nextDraftAt: state?.nextDraftAt,
|
|
142
|
+
});
|
|
134
143
|
},
|
|
135
144
|
invalidate: () => {
|
|
136
145
|
generation += 1;
|
|
@@ -143,8 +152,15 @@ export function createTelegramPreviewController(deps) {
|
|
|
143
152
|
sealTelegramPreviewState(state);
|
|
144
153
|
const prior = state.publicationPromise ?? state.flushPromise ?? state.precedingFlush;
|
|
145
154
|
let settle;
|
|
146
|
-
state.publicationPromise = new Promise((resolve) => {
|
|
147
|
-
|
|
155
|
+
state.publicationPromise = new Promise((resolve) => {
|
|
156
|
+
settle = resolve;
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
wait: async () => {
|
|
160
|
+
await prior?.catch(() => { });
|
|
161
|
+
},
|
|
162
|
+
settle,
|
|
163
|
+
};
|
|
148
164
|
},
|
|
149
165
|
prepareClear: (chatId, options) => {
|
|
150
166
|
const admittedState = state;
|
|
@@ -154,7 +170,8 @@ export function createTelegramPreviewController(deps) {
|
|
|
154
170
|
return;
|
|
155
171
|
await clearTelegramPreview(chatId, runtime, {
|
|
156
172
|
...options,
|
|
157
|
-
isDeliveryActive: () => runtime.canSend?.() !== false &&
|
|
173
|
+
isDeliveryActive: () => runtime.canSend?.() !== false &&
|
|
174
|
+
options?.isDeliveryActive?.() !== false,
|
|
158
175
|
});
|
|
159
176
|
};
|
|
160
177
|
},
|
|
@@ -208,7 +225,8 @@ async function handleTelegramAssistantMessagePreviewStart(message, deps) {
|
|
|
208
225
|
// Carry the previous delivery boundary; permanent text remains with its publication owner.
|
|
209
226
|
next.draftId = state?.draftId;
|
|
210
227
|
next.nextDraftAt = state?.nextDraftAt;
|
|
211
|
-
next.precedingFlush =
|
|
228
|
+
next.precedingFlush =
|
|
229
|
+
state?.publicationPromise ?? state?.flushPromise ?? state?.precedingFlush;
|
|
212
230
|
deps.setState(next);
|
|
213
231
|
}
|
|
214
232
|
async function handleTelegramAssistantMessagePreviewUpdate(message, deps) {
|
|
@@ -280,7 +298,9 @@ export async function clearTelegramPreview(chatId, deps, options = {}) {
|
|
|
280
298
|
if (options.isDeliveryActive?.() === false)
|
|
281
299
|
return;
|
|
282
300
|
deps.setState(undefined);
|
|
283
|
-
if (state.mode === "draft" &&
|
|
301
|
+
if (state.mode === "draft" &&
|
|
302
|
+
state.draftId !== undefined &&
|
|
303
|
+
deps.canSend?.() !== false) {
|
|
284
304
|
try {
|
|
285
305
|
await deps.sendDraft(chatId, state.draftId, undefined, {
|
|
286
306
|
...getTelegramTargetThreadParams(options.target ?? { chatId }),
|
|
@@ -423,9 +443,8 @@ function updateTelegramDraftInlineStateForLine(line, state) {
|
|
|
423
443
|
}
|
|
424
444
|
}
|
|
425
445
|
function updateTelegramDraftBlockStateForLine(line, state) {
|
|
426
|
-
const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
427
446
|
if (state.fence) {
|
|
428
|
-
if (
|
|
447
|
+
if (isTelegramMarkdownFenceClosing(line, state.fence)) {
|
|
429
448
|
state.fence = undefined;
|
|
430
449
|
}
|
|
431
450
|
return true;
|
|
@@ -435,12 +454,9 @@ function updateTelegramDraftBlockStateForLine(line, state) {
|
|
|
435
454
|
state.displayMath = false;
|
|
436
455
|
return true;
|
|
437
456
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
state.fence =
|
|
441
|
-
marker: markerText[0],
|
|
442
|
-
length: markerText.length,
|
|
443
|
-
};
|
|
457
|
+
const openingFence = parseTelegramMarkdownFenceOpening(line);
|
|
458
|
+
if (openingFence) {
|
|
459
|
+
state.fence = openingFence;
|
|
444
460
|
return true;
|
|
445
461
|
}
|
|
446
462
|
if (line.trim() === "$$") {
|
|
@@ -523,7 +539,9 @@ async function performTelegramPreviewFlush(chatId, state, deps, options = {}) {
|
|
|
523
539
|
state.nextDraftAt = Date.now() + (deps.minDraftIntervalMs ?? 0);
|
|
524
540
|
try {
|
|
525
541
|
const delivered = await deps.sendDraft(chatId, draftId, normalizeTelegramNativeMarkdown(snapshot.text), { ...getTelegramTargetThreadParams(options.target ?? { chatId }) });
|
|
526
|
-
if (delivered === false ||
|
|
542
|
+
if (delivered === false ||
|
|
543
|
+
deps.getState() !== state ||
|
|
544
|
+
deps.canSend?.() === false)
|
|
527
545
|
return;
|
|
528
546
|
deps.setDraftSupport("supported");
|
|
529
547
|
state.mode = "draft";
|
|
@@ -584,7 +602,9 @@ export async function flushTelegramPreview(chatId, deps, options = {}) {
|
|
|
584
602
|
});
|
|
585
603
|
break;
|
|
586
604
|
}
|
|
587
|
-
} while (deps.getState() === state &&
|
|
605
|
+
} while (deps.getState() === state &&
|
|
606
|
+
!state.sealed &&
|
|
607
|
+
state.flushRequested);
|
|
588
608
|
})();
|
|
589
609
|
try {
|
|
590
610
|
await state.flushPromise;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process identity and liveness proofs
|
|
3
|
+
* Zones: shared utils, multi-instance bus, durable admission
|
|
4
|
+
* Owns PID liveness probes and stable Linux/macOS/Windows process-birth identity. Only an absent PID or a
|
|
5
|
+
* mismatched birth proof establishes death; inaccessible metadata stays unverifiable.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
8
|
+
interface TelegramProcessBirthIdentityOptions {
|
|
9
|
+
platform?: NodeJS.Platform;
|
|
10
|
+
readProcStat?: (pid: number) => string;
|
|
11
|
+
readDarwinProcessStart?: (pid: number) => string;
|
|
12
|
+
readWindowsProcessStart?: (pid: number) => string;
|
|
13
|
+
}
|
|
14
|
+
export type TelegramProcessLiveness = "alive" | "dead" | "unverifiable";
|
|
15
|
+
interface TelegramProcessLivenessOptions extends TelegramProcessBirthIdentityOptions {
|
|
16
|
+
isProcessAlive?: (pid: number) => boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function getTelegramProcessBirthIdentity(pid: number, fallbackGeneration: number | string, options?: TelegramProcessBirthIdentityOptions): string;
|
|
19
|
+
export declare function getTelegramProcessLiveness(owner: {
|
|
20
|
+
processId: number;
|
|
21
|
+
processBirthId: string;
|
|
22
|
+
}, options?: TelegramProcessLivenessOptions): TelegramProcessLiveness;
|
|
23
|
+
export declare function getTelegramProcessBirthIdentityLiveness(processBirthId: string, options?: TelegramProcessLivenessOptions): TelegramProcessLiveness;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process identity and liveness proofs
|
|
3
|
+
* Zones: shared utils, multi-instance bus, durable admission
|
|
4
|
+
* Owns PID liveness probes and stable Linux/macOS/Windows process-birth identity. Only an absent PID or a
|
|
5
|
+
* mismatched birth proof establishes death; inaccessible metadata stays unverifiable.
|
|
6
|
+
*/
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { platform as getPlatform } from "node:os";
|
|
11
|
+
export function isProcessAlive(pid) {
|
|
12
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
13
|
+
return false;
|
|
14
|
+
try {
|
|
15
|
+
process.kill(pid, 0);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
// Only an absent PID proves death; permission and unexpected failures do not.
|
|
20
|
+
return error.code !== "ESRCH";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function readDarwinProcessStart(pid) {
|
|
24
|
+
return execFileSync("/bin/ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27
|
+
}).trim();
|
|
28
|
+
}
|
|
29
|
+
/** Creation time in UTC 100 ns ticks; PowerShell ships with every supported Windows and needs no native addon. */
|
|
30
|
+
function readWindowsProcessStart(pid) {
|
|
31
|
+
const root = process.env.SystemRoot ?? "C:\\Windows";
|
|
32
|
+
return execFileSync(`${root}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, [
|
|
33
|
+
"-NoProfile",
|
|
34
|
+
"-NonInteractive",
|
|
35
|
+
"-Command",
|
|
36
|
+
`(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,
|
|
37
|
+
], {
|
|
38
|
+
encoding: "utf8",
|
|
39
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
40
|
+
timeout: 10_000,
|
|
41
|
+
windowsHide: true,
|
|
42
|
+
}).trim();
|
|
43
|
+
}
|
|
44
|
+
/** The current process never changes birth, so its slow Windows proof is read once; other PIDs are never cached. */
|
|
45
|
+
let ownWindowsBirthProof;
|
|
46
|
+
function getTelegramProcessBirthProof(pid, options = {}) {
|
|
47
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
48
|
+
return { status: "unverifiable" };
|
|
49
|
+
const platform = options.platform ?? getPlatform();
|
|
50
|
+
if (platform === "linux") {
|
|
51
|
+
try {
|
|
52
|
+
const stat = (options.readProcStat ??
|
|
53
|
+
((targetPid) => readFileSync(`/proc/${targetPid}/stat`, "utf8")))(pid);
|
|
54
|
+
const closeParen = stat.lastIndexOf(")");
|
|
55
|
+
const fields = stat
|
|
56
|
+
.slice(closeParen + 2)
|
|
57
|
+
.trim()
|
|
58
|
+
.split(/\s+/u);
|
|
59
|
+
const startTicks = fields[19];
|
|
60
|
+
if (startTicks) {
|
|
61
|
+
return { status: "proven", identity: `${pid}:start:${startTicks}` };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* inaccessible process metadata */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (platform === "darwin") {
|
|
69
|
+
try {
|
|
70
|
+
const startedAt = (options.readDarwinProcessStart ?? readDarwinProcessStart)(pid);
|
|
71
|
+
if (startedAt) {
|
|
72
|
+
const fingerprint = createHash("sha256")
|
|
73
|
+
.update(startedAt)
|
|
74
|
+
.digest("hex")
|
|
75
|
+
.slice(0, 16);
|
|
76
|
+
return { status: "proven", identity: `${pid}:start:${fingerprint}` };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* inaccessible process metadata */
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (platform === "win32") {
|
|
84
|
+
const injected = options.readWindowsProcessStart;
|
|
85
|
+
if (!injected && pid === process.pid && ownWindowsBirthProof)
|
|
86
|
+
return ownWindowsBirthProof;
|
|
87
|
+
let proof = { status: "unverifiable" };
|
|
88
|
+
try {
|
|
89
|
+
const ticks = (injected ?? readWindowsProcessStart)(pid);
|
|
90
|
+
if (/^\d+$/u.test(ticks))
|
|
91
|
+
proof = { status: "proven", identity: `${pid}:start:${ticks}` };
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
/* inaccessible process metadata, including another user's process */
|
|
95
|
+
}
|
|
96
|
+
if (!injected && pid === process.pid && proof.status === "proven")
|
|
97
|
+
ownWindowsBirthProof = proof;
|
|
98
|
+
return proof;
|
|
99
|
+
}
|
|
100
|
+
return { status: "unverifiable" };
|
|
101
|
+
}
|
|
102
|
+
export function getTelegramProcessBirthIdentity(pid, fallbackGeneration, options = {}) {
|
|
103
|
+
const proof = getTelegramProcessBirthProof(pid, options);
|
|
104
|
+
return proof.status === "proven"
|
|
105
|
+
? proof.identity
|
|
106
|
+
: `${pid}:generation:${fallbackGeneration}`;
|
|
107
|
+
}
|
|
108
|
+
export function getTelegramProcessLiveness(owner, options = {}) {
|
|
109
|
+
const processAlive = options.isProcessAlive ?? isProcessAlive;
|
|
110
|
+
if (!processAlive(owner.processId))
|
|
111
|
+
return "dead";
|
|
112
|
+
const proof = getTelegramProcessBirthProof(owner.processId, options);
|
|
113
|
+
if (proof.status === "unverifiable")
|
|
114
|
+
return "unverifiable";
|
|
115
|
+
return proof.identity === owner.processBirthId ? "alive" : "dead";
|
|
116
|
+
}
|
|
117
|
+
export function getTelegramProcessBirthIdentityLiveness(processBirthId, options = {}) {
|
|
118
|
+
const match = /^(\d+):(start|generation):(.+)$/u.exec(processBirthId);
|
|
119
|
+
if (!match)
|
|
120
|
+
return "unverifiable";
|
|
121
|
+
const processId = Number(match[1]);
|
|
122
|
+
if (!Number.isSafeInteger(processId) || processId <= 0)
|
|
123
|
+
return "unverifiable";
|
|
124
|
+
const processAlive = options.isProcessAlive ?? isProcessAlive;
|
|
125
|
+
if (!processAlive(processId))
|
|
126
|
+
return "dead";
|
|
127
|
+
if (match[2] === "generation")
|
|
128
|
+
return "unverifiable";
|
|
129
|
+
const proof = getTelegramProcessBirthProof(processId, options);
|
|
130
|
+
if (proof.status === "unverifiable")
|
|
131
|
+
return "unverifiable";
|
|
132
|
+
return proof.identity === processBirthId ? "alive" : "dead";
|
|
133
|
+
}
|
package/dist/lib/prompts.js
CHANGED
|
@@ -121,13 +121,8 @@ export function buildTelegramBridgeSystemPrompt(options) {
|
|
|
121
121
|
: "";
|
|
122
122
|
return {
|
|
123
123
|
systemPrompt: Array.isArray(basePrompt)
|
|
124
|
-
? [
|
|
125
|
-
|
|
126
|
-
options.localSystemPromptSuffix + telegramSuffix,
|
|
127
|
-
]
|
|
128
|
-
: basePrompt +
|
|
129
|
-
options.localSystemPromptSuffix +
|
|
130
|
-
telegramSuffix,
|
|
124
|
+
? [...basePrompt, options.localSystemPromptSuffix + telegramSuffix]
|
|
125
|
+
: basePrompt + options.localSystemPromptSuffix + telegramSuffix,
|
|
131
126
|
};
|
|
132
127
|
}
|
|
133
128
|
export function createTelegramBeforeAgentStartHook(options = {}) {
|
package/dist/lib/queue.d.ts
CHANGED
|
@@ -188,9 +188,7 @@ export declare function createTelegramQueueAdmissionReceipt(options: {
|
|
|
188
188
|
scope: string;
|
|
189
189
|
sourceUpdateIds: readonly number[];
|
|
190
190
|
}): TelegramQueueAdmissionReceipt | undefined;
|
|
191
|
-
export declare function isTelegramQueueItemDurablyAdmitted<TContext = unknown>(item: TelegramQueueItem<TContext>, isReceiptCommitted: (receipt: TelegramQueueAdmissionReceipt) => boolean): boolean;
|
|
192
191
|
export declare function getTelegramQueueLaneContract(lane: TelegramQueueLane): TelegramQueueLaneContract;
|
|
193
|
-
export declare function getTelegramQueueItemAdmissionMode(item: Pick<TelegramQueueItem, "queueLane">): TelegramQueueAdmissionMode;
|
|
194
192
|
export declare function isTelegramQueueItemAdmissionValid(item: Pick<TelegramQueueItem, "kind" | "queueLane">): boolean;
|
|
195
193
|
export declare function assertTelegramQueueItemAdmissionValid(item: Pick<TelegramQueueItem, "kind" | "queueLane" | "admissionReceipts">): void;
|
|
196
194
|
export declare function createTelegramQueueStore<TContext = unknown>(initialItems?: TelegramQueueItem<TContext>[]): TelegramQueueStateStore<TContext>;
|
|
@@ -202,6 +200,13 @@ export declare function createTelegramTransportStampedQueueStore<TContext>(store
|
|
|
202
200
|
export declare function countExecutableTelegramQueueItems<TContext = unknown>(items: readonly TelegramQueueItem<TContext>[]): number;
|
|
203
201
|
export declare function createTelegramQueueItemCountGetter<TContext = unknown>(store: Pick<TelegramQueueStore<TContext>, "getQueuedItems">): () => number;
|
|
204
202
|
export declare function createTelegramActiveTurnStore<TTurn extends PendingTelegramTurn = PendingTelegramTurn>(): TelegramActiveTurnStore<TTurn>;
|
|
203
|
+
/** Current captured targets only; skipped/receipt-held items remain work, never historical census. */
|
|
204
|
+
export declare function observeTelegramTargetQueueWork(target: TelegramQueueTarget & {
|
|
205
|
+
threadId: number;
|
|
206
|
+
}, activeTurn: Pick<TelegramQueueItemBase, "chatId" | "target"> | undefined, items: readonly Pick<TelegramQueueItemBase, "chatId" | "target">[]): {
|
|
207
|
+
protected: boolean;
|
|
208
|
+
unknown: boolean;
|
|
209
|
+
};
|
|
205
210
|
export declare function partitionTelegramQueueItemsForHistory<TContext = unknown>(items: TelegramQueueItem<TContext>[]): {
|
|
206
211
|
historyTurns: PendingTelegramTurn[];
|
|
207
212
|
remainingItems: TelegramQueueItem<TContext>[];
|
|
@@ -586,7 +591,7 @@ export interface TelegramSessionShutdownRuntimeDeps<TQueueItem> {
|
|
|
586
591
|
clearAbort: () => void;
|
|
587
592
|
stopPolling: () => Promise<void>;
|
|
588
593
|
}
|
|
589
|
-
export interface TelegramSessionLifecycleHookRuntimeDeps<TContext, TQueueItem, TModel = unknown> extends TelegramRuntimeEventRecorderPort {
|
|
594
|
+
export interface TelegramSessionLifecycleHookRuntimeDeps<TContext, TQueueItem, TModel = unknown> extends TelegramRuntimeEventRecorderPort, Omit<TelegramSessionShutdownRuntimeDeps<TQueueItem>, "isSessionActive" | "discardQueuedItems" | "applyState"> {
|
|
590
595
|
getCurrentModel: (ctx: TContext) => TModel | undefined;
|
|
591
596
|
loadConfig: () => Promise<void>;
|
|
592
597
|
applySessionStartState: (state: TelegramSessionStartState<TModel>) => void;
|
|
@@ -594,20 +599,8 @@ export interface TelegramSessionLifecycleHookRuntimeDeps<TContext, TQueueItem, T
|
|
|
594
599
|
prepareTempDir: () => Promise<unknown>;
|
|
595
600
|
updateStatus: (ctx: TContext) => void;
|
|
596
601
|
isSessionActive?: (ctx: TContext) => boolean;
|
|
597
|
-
unbindDeferredDispatchContext?: () => void;
|
|
598
602
|
discardQueuedItems?: (ctx: TContext) => void;
|
|
599
603
|
applySessionShutdownState: (state: TelegramSessionShutdownState<TQueueItem>) => void;
|
|
600
|
-
clearPendingMediaGroups: () => void;
|
|
601
|
-
clearModelMenuState: () => void;
|
|
602
|
-
getActiveTurnChatId: () => number | undefined;
|
|
603
|
-
getActiveTurnTarget?: () => TelegramQueueTarget | undefined;
|
|
604
|
-
clearPreview: (chatId: number, options?: {
|
|
605
|
-
target?: TelegramQueueTarget;
|
|
606
|
-
}) => Promise<void>;
|
|
607
|
-
previewShutdownTimeoutMs?: number;
|
|
608
|
-
clearActiveTurn: () => void;
|
|
609
|
-
clearAbort: () => void;
|
|
610
|
-
stopPolling: () => Promise<void>;
|
|
611
604
|
}
|
|
612
605
|
export type TelegramSessionLifecycleHookEvent = unknown;
|
|
613
606
|
export declare function createTelegramSessionStateApplier<TQueueItem, TModel>(deps: TelegramSessionStateApplierDeps<TQueueItem, TModel>): TelegramSessionStateApplier<TQueueItem, TModel>;
|
|
@@ -648,6 +641,8 @@ export interface TelegramPromptEnqueueRuntimeDeps<TMessage, TContext = unknown>
|
|
|
648
641
|
updateStatus: () => void;
|
|
649
642
|
dispatchNextQueuedTelegramTurn: () => void;
|
|
650
643
|
assertExecutionCurrent?: () => void;
|
|
644
|
+
/** Input-specific relocation must not fold or replace unrelated queued work. */
|
|
645
|
+
preserveQueued?: boolean;
|
|
651
646
|
onQueued?: (turn: PendingTelegramTurn) => void;
|
|
652
647
|
}
|
|
653
648
|
export interface TelegramPromptEnqueueControllerDeps<TMessage, TContext = unknown> extends TelegramQueueStore<TContext> {
|
|
@@ -660,7 +655,10 @@ export interface TelegramPromptEnqueueControllerDeps<TMessage, TContext = unknow
|
|
|
660
655
|
assertExecutionCurrent?: (messages: TMessage[]) => void;
|
|
661
656
|
}
|
|
662
657
|
export interface TelegramPromptEnqueueController<TMessage, TContext = unknown> {
|
|
663
|
-
enqueue: (messages: TMessage[], ctx: TContext, onQueued?: (turn: PendingTelegramTurn) => void
|
|
658
|
+
enqueue: (messages: TMessage[], ctx: TContext, onQueued?: (turn: PendingTelegramTurn) => void, options?: {
|
|
659
|
+
assertCurrent?: () => void;
|
|
660
|
+
preserveQueued?: boolean;
|
|
661
|
+
}) => Promise<PendingTelegramTurn>;
|
|
664
662
|
}
|
|
665
663
|
export declare function buildTelegramSessionStartState<TModel = unknown>(currentModel: TModel | undefined): TelegramSessionStartState<TModel>;
|
|
666
664
|
export declare function buildTelegramSessionShutdownState<TQueueItem>(): TelegramSessionShutdownState<TQueueItem>;
|