@llblab/pi-telegram 0.27.11 → 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/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
- options.recordRuntimeEvent?.(options.category, statusError, {
370
- phase: options.phase,
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
- deps.recordRuntimeEvent?.("typing", error, {
414
- chatId: targetChatId,
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
- deps.recordRuntimeEvent?.("typing", error, {
432
- chatId: targetChatId,
433
- aggregate: true,
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
- return () => {
739
- if (timer) return;
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 deps.persistSnapshot().catch(deps.recordError);
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("muted", "disconnected")}${queued}`;
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
- return `- ${follower.instanceId}:${labelSuffix} heartbeat ${ageSeconds}s ago${statusLabel}${target}${cwd}`;
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 followerLine = `- follower registered: ${localBus.followerRegistered ? "yes" : "no"}${label ? ` ${label}` : ""}${target}`;
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.pollingActive ? "running" : "stopped"}`,
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.pollingActive ? "running" : "stopped"}`,
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"}`,