@llblab/pi-telegram 0.27.12 → 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/AGENTS.md +150 -258
- package/BACKLOG.md +1 -169
- package/CHANGELOG.md +393 -443
- package/README.md +9 -6
- package/api/updates.ts +5 -0
- package/docs/architecture.md +71 -26
- package/docs/multi-instance-bus.md +23 -5
- package/docs/public-api.md +7 -6
- package/docs/ui-style.md +6 -6
- package/docs/updates.md +29 -11
- package/index.ts +356 -246
- package/lib/activity-verbosity.ts +26 -0
- package/lib/bindings.ts +240 -2
- package/lib/bus-follower.ts +436 -238
- package/lib/bus-leader.ts +395 -42
- package/lib/bus.ts +994 -153
- package/lib/commands.ts +184 -30
- package/lib/config.ts +23 -2
- package/lib/journal.ts +3140 -0
- package/lib/lifecycle.ts +4 -0
- package/lib/locks.ts +4 -2
- package/lib/media.ts +71 -32
- package/lib/menu-queue.ts +31 -17
- package/lib/menu.ts +5 -3
- package/lib/model.ts +51 -24
- package/lib/ownership.ts +42 -7
- package/lib/paths.ts +35 -0
- package/lib/polling.ts +591 -106
- package/lib/prompts.ts +17 -0
- package/lib/queue.ts +732 -143
- package/lib/routing.ts +291 -64
- package/lib/runtime.ts +26 -10
- package/lib/status.ts +257 -18
- package/lib/sync.ts +131 -5
- package/lib/telegram-api.ts +41 -11
- package/lib/text-groups.ts +75 -35
- package/lib/threads.ts +112 -4
- package/lib/turns.ts +79 -14
- package/lib/updates.ts +3771 -223
- package/package.json +3 -3
- package/scripts/check-downgrade.mjs +435 -0
package/lib/runtime.ts
CHANGED
|
@@ -366,9 +366,13 @@ function updateTelegramRuntimeStatusSafely<TContext>(
|
|
|
366
366
|
try {
|
|
367
367
|
updateStatus(ctx, options.error);
|
|
368
368
|
} catch (statusError) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
369
|
+
try {
|
|
370
|
+
options.recordRuntimeEvent?.(options.category, statusError, {
|
|
371
|
+
phase: options.phase,
|
|
372
|
+
});
|
|
373
|
+
} catch {
|
|
374
|
+
// Status diagnostics cannot escape an asynchronous runtime owner.
|
|
375
|
+
}
|
|
372
376
|
}
|
|
373
377
|
}
|
|
374
378
|
|
|
@@ -383,6 +387,7 @@ export interface TelegramTypingLoopStarterDeps<
|
|
|
383
387
|
) => Promise<unknown>;
|
|
384
388
|
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
|
385
389
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
390
|
+
isContextActive?: (ctx: TContext) => boolean;
|
|
386
391
|
intervalMs?: number;
|
|
387
392
|
}
|
|
388
393
|
|
|
@@ -402,6 +407,7 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
402
407
|
try {
|
|
403
408
|
await deps.sendTypingAction(targetChatId, actionOptions);
|
|
404
409
|
} catch (error) {
|
|
410
|
+
if (deps.isContextActive?.(ctx) === false) return;
|
|
405
411
|
const message =
|
|
406
412
|
error instanceof Error ? error.message : String(error);
|
|
407
413
|
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
|
@@ -410,9 +416,13 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
410
416
|
phase: "status-update",
|
|
411
417
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
412
418
|
});
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
419
|
+
try {
|
|
420
|
+
deps.recordRuntimeEvent?.("typing", error, {
|
|
421
|
+
chatId: targetChatId,
|
|
422
|
+
});
|
|
423
|
+
} catch {
|
|
424
|
+
// Typing diagnostics cannot escape the in-flight action owner.
|
|
425
|
+
}
|
|
416
426
|
}
|
|
417
427
|
},
|
|
418
428
|
sendAggregateTypingAction: deps.sendAggregateTypingAction
|
|
@@ -420,6 +430,7 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
420
430
|
try {
|
|
421
431
|
await deps.sendAggregateTypingAction?.(targetChatId);
|
|
422
432
|
} catch (error) {
|
|
433
|
+
if (deps.isContextActive?.(ctx) === false) return;
|
|
423
434
|
const message =
|
|
424
435
|
error instanceof Error ? error.message : String(error);
|
|
425
436
|
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
|
@@ -428,10 +439,14 @@ export function createTelegramTypingLoopStarter<TContext>(
|
|
|
428
439
|
phase: "status-update",
|
|
429
440
|
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
430
441
|
});
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
442
|
+
try {
|
|
443
|
+
deps.recordRuntimeEvent?.("typing", error, {
|
|
444
|
+
chatId: targetChatId,
|
|
445
|
+
aggregate: true,
|
|
446
|
+
});
|
|
447
|
+
} catch {
|
|
448
|
+
// Typing diagnostics cannot escape the in-flight action owner.
|
|
449
|
+
}
|
|
435
450
|
}
|
|
436
451
|
}
|
|
437
452
|
: undefined,
|
|
@@ -578,6 +593,7 @@ export interface TelegramPromptDispatchRuntimeDeps<
|
|
|
578
593
|
) => Promise<unknown>;
|
|
579
594
|
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
|
580
595
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
596
|
+
isContextActive?: (ctx: TContext) => boolean;
|
|
581
597
|
intervalMs?: number;
|
|
582
598
|
}
|
|
583
599
|
|
package/lib/status.ts
CHANGED
|
@@ -110,6 +110,11 @@ export interface TelegramBridgeStatusBusFollower {
|
|
|
110
110
|
cwd?: string;
|
|
111
111
|
lastHeartbeatMs: number;
|
|
112
112
|
target?: { chatId: number; threadId?: number };
|
|
113
|
+
protocol?: {
|
|
114
|
+
protocolVersion: number;
|
|
115
|
+
runtimeBuild: string;
|
|
116
|
+
capabilities: string[];
|
|
117
|
+
};
|
|
113
118
|
slot?: string;
|
|
114
119
|
threadName?: string;
|
|
115
120
|
status?: string;
|
|
@@ -124,6 +129,11 @@ export interface TelegramBridgeStatusLocalBus {
|
|
|
124
129
|
followerTarget?: { chatId: number; threadId?: number };
|
|
125
130
|
followerSlot?: string;
|
|
126
131
|
followerThreadName?: string;
|
|
132
|
+
leaderProtocol?: {
|
|
133
|
+
protocolVersion: number;
|
|
134
|
+
runtimeBuild: string;
|
|
135
|
+
capabilities: string[];
|
|
136
|
+
};
|
|
127
137
|
}
|
|
128
138
|
|
|
129
139
|
export interface TelegramBridgeStatusTopicTarget {
|
|
@@ -179,6 +189,57 @@ export interface TelegramBridgeThreadReconciliationState {
|
|
|
179
189
|
export type TelegramBridgeBusRole = "leader" | "follower";
|
|
180
190
|
export type TelegramBridgeBusLifecyclePhase = "electing";
|
|
181
191
|
|
|
192
|
+
export interface TelegramBridgePollingState {
|
|
193
|
+
phase: string;
|
|
194
|
+
phaseStartedAtMs?: number;
|
|
195
|
+
currentUpdateId?: number;
|
|
196
|
+
startedAtMs?: number;
|
|
197
|
+
stoppedAtMs?: number;
|
|
198
|
+
lastSuccessfulResponseAtMs?: number;
|
|
199
|
+
lastSuccessfulResponseUpdateCount?: number;
|
|
200
|
+
stopReason?: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface TelegramBridgeInboundWorkerState {
|
|
204
|
+
phase: string;
|
|
205
|
+
generation: number;
|
|
206
|
+
phaseStartedAtMs?: number;
|
|
207
|
+
currentUpdateId?: number;
|
|
208
|
+
blockedReason?: string;
|
|
209
|
+
journalEntryCount: number;
|
|
210
|
+
journalSerializedBytes: number;
|
|
211
|
+
oldestAdmittedAtMs?: number;
|
|
212
|
+
deferredClaimCount: number;
|
|
213
|
+
queuedClaimCount: number;
|
|
214
|
+
foreignQueuedCount: number;
|
|
215
|
+
foreignQueuedOwnerLiveness?: "alive" | "dead" | "unverifiable";
|
|
216
|
+
foreignQueuedOwner?: {
|
|
217
|
+
instanceId: string;
|
|
218
|
+
processId: number;
|
|
219
|
+
processBirthId: string;
|
|
220
|
+
sessionGeneration: number;
|
|
221
|
+
acquisitionId: string;
|
|
222
|
+
acquiredAtMs: number;
|
|
223
|
+
};
|
|
224
|
+
retryWaitCount: number;
|
|
225
|
+
failedCount: number;
|
|
226
|
+
nextRetryUpdateId?: number;
|
|
227
|
+
nextRetryAtMs?: number;
|
|
228
|
+
nextRetryAttemptCount?: number;
|
|
229
|
+
nextRetryFailureClass?: string;
|
|
230
|
+
failedUpdateId?: number;
|
|
231
|
+
failedFailureId?: string;
|
|
232
|
+
failedAttemptCount?: number;
|
|
233
|
+
failedClass?: string;
|
|
234
|
+
failedSummary?: string;
|
|
235
|
+
terminalFailureAtMs?: number;
|
|
236
|
+
unsettledExecutionCount: number;
|
|
237
|
+
lastCompletedUpdateId?: number;
|
|
238
|
+
lastCompletedAtMs?: number;
|
|
239
|
+
lastFailureAtMs?: number;
|
|
240
|
+
lastFailurePhase?: string;
|
|
241
|
+
}
|
|
242
|
+
|
|
182
243
|
export interface TelegramBridgeStatusLineState {
|
|
183
244
|
hasBotToken?: boolean;
|
|
184
245
|
botUsername?: string;
|
|
@@ -189,11 +250,18 @@ export interface TelegramBridgeStatusLineState {
|
|
|
189
250
|
botThreadModeUpdatedAtMs?: number;
|
|
190
251
|
botThreadModeAction?: string;
|
|
191
252
|
busRole?: TelegramBridgeBusRole;
|
|
253
|
+
busProtocol?: {
|
|
254
|
+
protocolVersion: number;
|
|
255
|
+
runtimeBuild: string;
|
|
256
|
+
capabilities: string[];
|
|
257
|
+
};
|
|
192
258
|
busLifecyclePhase?: TelegramBridgeBusLifecyclePhase;
|
|
193
259
|
instanceSlot?: string;
|
|
194
260
|
instanceThreadName?: string;
|
|
195
261
|
lockState?: string;
|
|
196
262
|
pollingActive: boolean;
|
|
263
|
+
polling?: TelegramBridgePollingState;
|
|
264
|
+
inboundWorker?: TelegramBridgeInboundWorkerState;
|
|
197
265
|
lastUpdateId?: number;
|
|
198
266
|
activeSourceMessageIds?: number[];
|
|
199
267
|
pendingDispatch: boolean;
|
|
@@ -214,7 +282,7 @@ export interface TelegramBridgeStatusLineState {
|
|
|
214
282
|
|
|
215
283
|
export interface TelegramStatusBarTheme {
|
|
216
284
|
fg: (
|
|
217
|
-
token: "accent" | "error" | "muted" | "warning" | "success",
|
|
285
|
+
token: "accent" | "dim" | "error" | "muted" | "warning" | "success",
|
|
218
286
|
text: string,
|
|
219
287
|
) => string;
|
|
220
288
|
}
|
|
@@ -267,6 +335,8 @@ export interface TelegramBridgeStatusRuntimeDeps<
|
|
|
267
335
|
logs: string;
|
|
268
336
|
};
|
|
269
337
|
isPollingActive: () => boolean;
|
|
338
|
+
getPollingState?: () => TelegramBridgePollingState;
|
|
339
|
+
getInboundWorkerState?: () => TelegramBridgeInboundWorkerState | undefined;
|
|
270
340
|
getActiveSourceMessageIds: () => number[] | undefined;
|
|
271
341
|
hasActiveTurn: () => boolean;
|
|
272
342
|
hasDispatchPending: () => boolean;
|
|
@@ -278,6 +348,11 @@ export interface TelegramBridgeStatusRuntimeDeps<
|
|
|
278
348
|
getRecentRuntimeEvents: () => TelegramRuntimeEvent[];
|
|
279
349
|
getRuntimeLockState?: () => string;
|
|
280
350
|
getBusRole?: () => TelegramBridgeBusRole | undefined;
|
|
351
|
+
getBusProtocol?: () => {
|
|
352
|
+
protocolVersion: number;
|
|
353
|
+
runtimeBuild: string;
|
|
354
|
+
capabilities: string[];
|
|
355
|
+
};
|
|
281
356
|
getBusLifecyclePhase?: () => TelegramBridgeBusLifecyclePhase | undefined;
|
|
282
357
|
getBotThreadMode?: () =>
|
|
283
358
|
| {
|
|
@@ -404,15 +479,6 @@ export function recordStructuredTelegramRuntimeEvent(
|
|
|
404
479
|
}
|
|
405
480
|
}
|
|
406
481
|
|
|
407
|
-
export function recordTelegramRuntimeEvent(
|
|
408
|
-
events: TelegramRuntimeEvent[],
|
|
409
|
-
category: string,
|
|
410
|
-
error: unknown,
|
|
411
|
-
options: { botToken?: string; maxEvents: number; now?: number },
|
|
412
|
-
): void {
|
|
413
|
-
recordStructuredTelegramRuntimeEvent(events, { category, error }, options);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
482
|
function getOrCreateTelegramStatusLineProviderRegistry(): Map<
|
|
417
483
|
string,
|
|
418
484
|
TelegramStatusLineProvider
|
|
@@ -639,11 +705,18 @@ export function createTelegramBridgeStatusRuntime<
|
|
|
639
705
|
botThreadModeUpdatedAtMs: botThreadMode?.updatedAtMs,
|
|
640
706
|
botThreadModeAction: botThreadMode?.lastReconcileAction,
|
|
641
707
|
busRole: deps.getBusRole?.(),
|
|
708
|
+
busProtocol: deps.getBusProtocol?.(),
|
|
642
709
|
busLifecyclePhase: deps.getBusLifecyclePhase?.(),
|
|
643
710
|
instanceSlot: deps.getInstanceSlot?.(),
|
|
644
711
|
instanceThreadName: deps.getInstanceThreadName?.(),
|
|
645
712
|
lockState: deps.getRuntimeLockState?.(),
|
|
646
713
|
pollingActive: deps.isPollingActive(),
|
|
714
|
+
...(deps.getPollingState
|
|
715
|
+
? { polling: deps.getPollingState() }
|
|
716
|
+
: {}),
|
|
717
|
+
...(deps.getInboundWorkerState
|
|
718
|
+
? { inboundWorker: deps.getInboundWorkerState() }
|
|
719
|
+
: {}),
|
|
647
720
|
lastUpdateId: config.lastUpdateId,
|
|
648
721
|
activeSourceMessageIds: deps.getActiveSourceMessageIds(),
|
|
649
722
|
pendingDispatch: deps.hasDispatchPending(),
|
|
@@ -705,6 +778,7 @@ export function createTelegramStatusSnapshot(
|
|
|
705
778
|
instanceSlot: state.instanceSlot,
|
|
706
779
|
instanceThreadName: state.instanceThreadName,
|
|
707
780
|
pollingActive: state.pollingActive,
|
|
781
|
+
...(state.polling ? { polling: state.polling } : {}),
|
|
708
782
|
lockState: state.lockState,
|
|
709
783
|
},
|
|
710
784
|
liveRoster: {
|
|
@@ -735,14 +809,39 @@ export function createTelegramRuntimeDiagnosticsSnapshotScheduler(deps: {
|
|
|
735
809
|
}): () => void {
|
|
736
810
|
const setTimer = deps.setTimer ?? setTimeout;
|
|
737
811
|
let timer: { unref?: () => void } | number | undefined;
|
|
738
|
-
|
|
739
|
-
|
|
812
|
+
let persistPromise: Promise<void> | undefined;
|
|
813
|
+
let pending = false;
|
|
814
|
+
const recordError = (error: unknown): void => {
|
|
815
|
+
try {
|
|
816
|
+
deps.recordError(error);
|
|
817
|
+
} catch {
|
|
818
|
+
// Snapshot diagnostics cannot create an unhandled scheduler rejection.
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
const request = (): void => {
|
|
822
|
+
if (timer || persistPromise) {
|
|
823
|
+
pending = true;
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
740
826
|
timer = setTimer(() => {
|
|
741
827
|
timer = undefined;
|
|
742
|
-
void
|
|
828
|
+
let tracked: Promise<void>;
|
|
829
|
+
tracked = Promise.resolve()
|
|
830
|
+
.then(deps.persistSnapshot)
|
|
831
|
+
.catch(recordError)
|
|
832
|
+
.finally(() => {
|
|
833
|
+
if (persistPromise !== tracked) return;
|
|
834
|
+
persistPromise = undefined;
|
|
835
|
+
if (pending) {
|
|
836
|
+
pending = false;
|
|
837
|
+
request();
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
persistPromise = tracked;
|
|
743
841
|
}, TELEGRAM_DIAGNOSTICS_SNAPSHOT_COALESCE_MS);
|
|
744
842
|
if (typeof timer !== "number") timer?.unref?.();
|
|
745
843
|
};
|
|
844
|
+
return request;
|
|
746
845
|
}
|
|
747
846
|
|
|
748
847
|
export function getTelegramStatusBarProcessingStatus(state: {
|
|
@@ -785,7 +884,7 @@ export function buildTelegramStatusBarText(
|
|
|
785
884
|
if (state.busLifecyclePhase === "electing")
|
|
786
885
|
return `${label} ${theme.fg("warning", "electing")}${queued}`;
|
|
787
886
|
if (!state.pollingActive && state.busRole !== "follower")
|
|
788
|
-
return `${theme.fg("accent", "telegram")} ${theme.fg("
|
|
887
|
+
return `${theme.fg("accent", "telegram")} ${theme.fg("dim", "disconnected")}${queued}`;
|
|
789
888
|
if (state.processing) {
|
|
790
889
|
const processingStatus = state.queuedStatus
|
|
791
890
|
? "active"
|
|
@@ -826,6 +925,23 @@ function formatTelegramThreadStatusLabel(input: {
|
|
|
826
925
|
return input.slot ? `[${input.slot}]` : "";
|
|
827
926
|
}
|
|
828
927
|
|
|
928
|
+
function formatTelegramBusProtocolIdentity(
|
|
929
|
+
protocol:
|
|
930
|
+
| {
|
|
931
|
+
protocolVersion: number;
|
|
932
|
+
runtimeBuild: string;
|
|
933
|
+
capabilities: string[];
|
|
934
|
+
}
|
|
935
|
+
| undefined,
|
|
936
|
+
): string {
|
|
937
|
+
if (!protocol) return "";
|
|
938
|
+
const capabilities =
|
|
939
|
+
protocol.capabilities.length > 0
|
|
940
|
+
? ` capabilities=${protocol.capabilities.join(",")}`
|
|
941
|
+
: " capabilities=none";
|
|
942
|
+
return ` protocol=v${protocol.protocolVersion} build=${protocol.runtimeBuild}${capabilities}`;
|
|
943
|
+
}
|
|
944
|
+
|
|
829
945
|
function buildTelegramBusFollowerLines(
|
|
830
946
|
state: Pick<TelegramBridgeStatusLineState, "busFollowers" | "busNowMs">,
|
|
831
947
|
): string[] {
|
|
@@ -846,7 +962,8 @@ function buildTelegramBusFollowerLines(
|
|
|
846
962
|
const statusLabel = follower.status ? ` (${follower.status})` : "";
|
|
847
963
|
const cwd = follower.cwd ? ` ${follower.cwd}` : "";
|
|
848
964
|
const target = formatTelegramStatusTarget(follower.target);
|
|
849
|
-
|
|
965
|
+
const protocol = formatTelegramBusProtocolIdentity(follower.protocol);
|
|
966
|
+
return `- ${follower.instanceId}:${labelSuffix} heartbeat ${ageSeconds}s ago${statusLabel}${target}${cwd}${protocol}`;
|
|
850
967
|
}),
|
|
851
968
|
];
|
|
852
969
|
}
|
|
@@ -862,7 +979,10 @@ function buildTelegramLocalBusLines(
|
|
|
862
979
|
slot: localBus.followerSlot,
|
|
863
980
|
threadName: localBus.followerThreadName,
|
|
864
981
|
});
|
|
865
|
-
const
|
|
982
|
+
const protocol = formatTelegramBusProtocolIdentity(
|
|
983
|
+
localBus.leaderProtocol,
|
|
984
|
+
);
|
|
985
|
+
const followerLine = `- follower registered: ${localBus.followerRegistered ? "yes" : "no"}${label ? ` ${label}` : ""}${target}${protocol}`;
|
|
866
986
|
const lines = ["", "local bus:", followerLine];
|
|
867
987
|
if (options.verbose) {
|
|
868
988
|
if (localBus.leaderSocketPath) {
|
|
@@ -1050,6 +1170,109 @@ function buildTelegramBridgeCompactThreadLines(
|
|
|
1050
1170
|
return lines;
|
|
1051
1171
|
}
|
|
1052
1172
|
|
|
1173
|
+
function formatTelegramPollingLifecycle(
|
|
1174
|
+
state: Pick<TelegramBridgeStatusLineState, "pollingActive" | "polling">,
|
|
1175
|
+
): string {
|
|
1176
|
+
const lifecycle = state.pollingActive ? "running" : "stopped";
|
|
1177
|
+
if (!state.polling) return lifecycle;
|
|
1178
|
+
if (state.pollingActive) return `${lifecycle} (${state.polling.phase})`;
|
|
1179
|
+
return state.polling.stopReason
|
|
1180
|
+
? `${lifecycle} (${state.polling.stopReason})`
|
|
1181
|
+
: lifecycle;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
function buildTelegramPollingDiagnosticLines(
|
|
1185
|
+
polling: TelegramBridgePollingState | undefined,
|
|
1186
|
+
): string[] {
|
|
1187
|
+
if (!polling) return [];
|
|
1188
|
+
return [
|
|
1189
|
+
`- phase: ${polling.phase}`,
|
|
1190
|
+
...(polling.phaseStartedAtMs !== undefined
|
|
1191
|
+
? [
|
|
1192
|
+
`- phase started: ${new Date(polling.phaseStartedAtMs).toISOString()}`,
|
|
1193
|
+
]
|
|
1194
|
+
: []),
|
|
1195
|
+
...(polling.currentUpdateId !== undefined
|
|
1196
|
+
? [`- current update id: ${polling.currentUpdateId}`]
|
|
1197
|
+
: []),
|
|
1198
|
+
...(polling.lastSuccessfulResponseAtMs !== undefined
|
|
1199
|
+
? [
|
|
1200
|
+
`- last successful response: ${new Date(polling.lastSuccessfulResponseAtMs).toISOString()} (updates=${polling.lastSuccessfulResponseUpdateCount ?? "unknown"})`,
|
|
1201
|
+
]
|
|
1202
|
+
: []),
|
|
1203
|
+
...(polling.startedAtMs !== undefined
|
|
1204
|
+
? [`- started: ${new Date(polling.startedAtMs).toISOString()}`]
|
|
1205
|
+
: []),
|
|
1206
|
+
...(polling.stoppedAtMs !== undefined
|
|
1207
|
+
? [`- stopped: ${new Date(polling.stoppedAtMs).toISOString()}`]
|
|
1208
|
+
: []),
|
|
1209
|
+
...(polling.stopReason ? [`- stop reason: ${polling.stopReason}`] : []),
|
|
1210
|
+
];
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function formatTelegramInboundWorkerState(
|
|
1214
|
+
worker: TelegramBridgeInboundWorkerState | undefined,
|
|
1215
|
+
): string {
|
|
1216
|
+
if (!worker) return "not started";
|
|
1217
|
+
const depth = worker.journalEntryCount;
|
|
1218
|
+
return `${worker.phase} (depth=${depth}, queued=${worker.queuedClaimCount}, foreign=${worker.foreignQueuedCount}, deferred=${worker.deferredClaimCount}, retry=${worker.retryWaitCount}, failed=${worker.failedCount})`;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function buildTelegramInboundWorkerDiagnosticLines(
|
|
1222
|
+
worker: TelegramBridgeInboundWorkerState | undefined,
|
|
1223
|
+
): string[] {
|
|
1224
|
+
if (!worker) return ["- state: not started"];
|
|
1225
|
+
return [
|
|
1226
|
+
`- state: ${worker.phase}`,
|
|
1227
|
+
`- generation: ${worker.generation}`,
|
|
1228
|
+
...(worker.phaseStartedAtMs !== undefined
|
|
1229
|
+
? [`- phase started: ${new Date(worker.phaseStartedAtMs).toISOString()}`]
|
|
1230
|
+
: []),
|
|
1231
|
+
`- journal: entries=${worker.journalEntryCount}, bytes=${worker.journalSerializedBytes}`,
|
|
1232
|
+
`- claims: queued=${worker.queuedClaimCount}, foreign-queued=${worker.foreignQueuedCount}, deferred=${worker.deferredClaimCount}, unsettled=${worker.unsettledExecutionCount}`,
|
|
1233
|
+
...(worker.foreignQueuedOwner
|
|
1234
|
+
? [
|
|
1235
|
+
`- queued semantic owner: instance=${worker.foreignQueuedOwner.instanceId}, pid=${worker.foreignQueuedOwner.processId}, birth=${worker.foreignQueuedOwner.processBirthId}, session=${worker.foreignQueuedOwner.sessionGeneration}, acquisition=${worker.foreignQueuedOwner.acquisitionId}, liveness=${worker.foreignQueuedOwnerLiveness ?? "unknown"}`,
|
|
1236
|
+
]
|
|
1237
|
+
: []),
|
|
1238
|
+
`- failures: retry-wait=${worker.retryWaitCount}, terminal=${worker.failedCount}`,
|
|
1239
|
+
...(worker.currentUpdateId !== undefined
|
|
1240
|
+
? [`- current update id: ${worker.currentUpdateId}`]
|
|
1241
|
+
: []),
|
|
1242
|
+
...(worker.nextRetryUpdateId !== undefined
|
|
1243
|
+
? [
|
|
1244
|
+
`- next retry: update=${worker.nextRetryUpdateId}, attempt=${worker.nextRetryAttemptCount ?? "unknown"}, class=${worker.nextRetryFailureClass ?? "unknown"}${worker.nextRetryAtMs !== undefined ? ` at ${new Date(worker.nextRetryAtMs).toISOString()}` : ""}`,
|
|
1245
|
+
]
|
|
1246
|
+
: []),
|
|
1247
|
+
...(worker.failedUpdateId !== undefined
|
|
1248
|
+
? [
|
|
1249
|
+
`- terminal update: id=${worker.failedUpdateId}, failure=${worker.failedFailureId ?? "unknown"}, attempts=${worker.failedAttemptCount ?? "unknown"}, class=${worker.failedClass ?? "unknown"}${worker.terminalFailureAtMs !== undefined ? ` at ${new Date(worker.terminalFailureAtMs).toISOString()}` : ""}`,
|
|
1250
|
+
...(worker.failedSummary
|
|
1251
|
+
? [`- terminal summary: ${worker.failedSummary}`]
|
|
1252
|
+
: []),
|
|
1253
|
+
]
|
|
1254
|
+
: []),
|
|
1255
|
+
...(worker.oldestAdmittedAtMs !== undefined
|
|
1256
|
+
? [
|
|
1257
|
+
`- oldest admitted: ${new Date(worker.oldestAdmittedAtMs).toISOString()}`,
|
|
1258
|
+
]
|
|
1259
|
+
: []),
|
|
1260
|
+
...(worker.blockedReason
|
|
1261
|
+
? [`- blocked reason: ${worker.blockedReason}`]
|
|
1262
|
+
: []),
|
|
1263
|
+
...(worker.lastCompletedUpdateId !== undefined
|
|
1264
|
+
? [
|
|
1265
|
+
`- last completed: ${worker.lastCompletedUpdateId}${worker.lastCompletedAtMs !== undefined ? ` at ${new Date(worker.lastCompletedAtMs).toISOString()}` : ""}`,
|
|
1266
|
+
]
|
|
1267
|
+
: []),
|
|
1268
|
+
...(worker.lastFailurePhase
|
|
1269
|
+
? [
|
|
1270
|
+
`- last failure: ${worker.lastFailurePhase}${worker.lastFailureAtMs !== undefined ? ` at ${new Date(worker.lastFailureAtMs).toISOString()}` : ""}`,
|
|
1271
|
+
]
|
|
1272
|
+
: []),
|
|
1273
|
+
];
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1053
1276
|
function buildTelegramBridgeCompactStatusLines(
|
|
1054
1277
|
state: TelegramBridgeStatusLineState,
|
|
1055
1278
|
): string[] {
|
|
@@ -1101,7 +1324,12 @@ function buildTelegramBridgeCompactStatusLines(
|
|
|
1101
1324
|
...(state.lockState ? [`- owner: ${state.lockState}`] : []),
|
|
1102
1325
|
"",
|
|
1103
1326
|
"health:",
|
|
1104
|
-
`- polling: ${state
|
|
1327
|
+
`- polling: ${formatTelegramPollingLifecycle(state)}`,
|
|
1328
|
+
...(state.inboundWorker
|
|
1329
|
+
? [
|
|
1330
|
+
`- inbound worker: ${formatTelegramInboundWorkerState(state.inboundWorker)}`,
|
|
1331
|
+
]
|
|
1332
|
+
: []),
|
|
1105
1333
|
`- state: ${executionState}`,
|
|
1106
1334
|
queueLine,
|
|
1107
1335
|
...(state.activeToolExecutions > 0
|
|
@@ -1153,6 +1381,9 @@ export function buildTelegramBridgeDiagnosticStatusLines(
|
|
|
1153
1381
|
]
|
|
1154
1382
|
: []),
|
|
1155
1383
|
...(state.busRole ? [`- bus role: ${state.busRole}`] : []),
|
|
1384
|
+
...(state.busProtocol
|
|
1385
|
+
? [`- bus${formatTelegramBusProtocolIdentity(state.busProtocol)}`]
|
|
1386
|
+
: []),
|
|
1156
1387
|
...(state.busLifecyclePhase
|
|
1157
1388
|
? [`- bus lifecycle: ${state.busLifecyclePhase}`]
|
|
1158
1389
|
: []),
|
|
@@ -1162,9 +1393,17 @@ export function buildTelegramBridgeDiagnosticStatusLines(
|
|
|
1162
1393
|
...(state.lockState ? [`- owner: ${state.lockState}`] : []),
|
|
1163
1394
|
"",
|
|
1164
1395
|
"polling:",
|
|
1165
|
-
`- state: ${state
|
|
1396
|
+
`- state: ${formatTelegramPollingLifecycle(state)}`,
|
|
1397
|
+
...buildTelegramPollingDiagnosticLines(state.polling),
|
|
1166
1398
|
`- last update id: ${state.lastUpdateId ?? "none"}`,
|
|
1167
1399
|
"",
|
|
1400
|
+
...(state.inboundWorker
|
|
1401
|
+
? [
|
|
1402
|
+
"inbound worker:",
|
|
1403
|
+
...buildTelegramInboundWorkerDiagnosticLines(state.inboundWorker),
|
|
1404
|
+
"",
|
|
1405
|
+
]
|
|
1406
|
+
: []),
|
|
1168
1407
|
"execution:",
|
|
1169
1408
|
`- active turn: ${state.activeSourceMessageIds?.join(",") || "no"}`,
|
|
1170
1409
|
`- pending dispatch: ${state.pendingDispatch ? "yes" : "no"}`,
|