@llblab/pi-telegram 0.51.5 → 0.52.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.
Files changed (181) hide show
  1. package/AGENTS.md +10 -8
  2. package/BACKLOG.md +1 -8
  3. package/CHANGELOG.md +23 -0
  4. package/LICENSE +21 -0
  5. package/README.md +16 -6
  6. package/dist/lib/activity-verbosity.d.ts +1 -5
  7. package/dist/lib/activity-verbosity.js +5 -7
  8. package/dist/lib/bindings.d.ts +9 -2
  9. package/dist/lib/bindings.js +22 -13
  10. package/dist/lib/bus-api.js +4 -2
  11. package/dist/lib/bus-follower.d.ts +55 -57
  12. package/dist/lib/bus-follower.js +213 -140
  13. package/dist/lib/bus-leader.d.ts +21 -2
  14. package/dist/lib/bus-leader.js +181 -25
  15. package/dist/lib/bus-transport.d.ts +4 -1
  16. package/dist/lib/bus-transport.js +28 -4
  17. package/dist/lib/bus.d.ts +52 -56
  18. package/dist/lib/bus.js +78 -246
  19. package/dist/lib/channel-posts.d.ts +1 -3
  20. package/dist/lib/channel-posts.js +23 -26
  21. package/dist/lib/command-templates.d.ts +0 -5
  22. package/dist/lib/command-templates.js +5 -5
  23. package/dist/lib/commands.d.ts +12 -26
  24. package/dist/lib/commands.js +152 -98
  25. package/dist/lib/config.d.ts +0 -17
  26. package/dist/lib/config.js +12 -14
  27. package/dist/lib/delivery.d.ts +0 -4
  28. package/dist/lib/delivery.js +2 -5
  29. package/dist/lib/extension.js +281 -61
  30. package/dist/lib/generative-apps.d.ts +0 -19
  31. package/dist/lib/inbound.d.ts +0 -2
  32. package/dist/lib/inbound.js +2 -2
  33. package/dist/lib/journal.d.ts +177 -16
  34. package/dist/lib/journal.js +1115 -249
  35. package/dist/lib/keyboard.d.ts +0 -2
  36. package/dist/lib/keyboard.js +2 -2
  37. package/dist/lib/lifecycle.d.ts +57 -2
  38. package/dist/lib/lifecycle.js +110 -5
  39. package/dist/lib/locks.d.ts +100 -12
  40. package/dist/lib/locks.js +380 -14
  41. package/dist/lib/logging.d.ts +30 -12
  42. package/dist/lib/logging.js +129 -72
  43. package/dist/lib/media.d.ts +28 -5
  44. package/dist/lib/media.js +26 -8
  45. package/dist/lib/menu-model.d.ts +0 -11
  46. package/dist/lib/menu-model.js +8 -8
  47. package/dist/lib/menu-settings.d.ts +0 -18
  48. package/dist/lib/menu-settings.js +18 -18
  49. package/dist/lib/menu-status.d.ts +4 -4
  50. package/dist/lib/menu-status.js +12 -7
  51. package/dist/lib/menu-thinking.d.ts +1 -1
  52. package/dist/lib/menu-thinking.js +1 -1
  53. package/dist/lib/menu.d.ts +1 -0
  54. package/dist/lib/menu.js +4 -3
  55. package/dist/lib/model.js +2 -4
  56. package/dist/lib/outbound-attachments.d.ts +0 -1
  57. package/dist/lib/outbound-attachments.js +1 -1
  58. package/dist/lib/outbound-buttons.js +1 -8
  59. package/dist/lib/outbound-markup.d.ts +1 -6
  60. package/dist/lib/outbound-markup.js +4 -21
  61. package/dist/lib/outbound.d.ts +0 -8
  62. package/dist/lib/outbound.js +5 -5
  63. package/dist/lib/paths.d.ts +50 -7
  64. package/dist/lib/paths.js +155 -17
  65. package/dist/lib/pi.d.ts +1 -0
  66. package/dist/lib/pi.js +2 -1
  67. package/dist/lib/polling.d.ts +0 -5
  68. package/dist/lib/polling.js +5 -5
  69. package/dist/lib/preview.d.ts +0 -3
  70. package/dist/lib/preview.js +3 -3
  71. package/dist/lib/prompt-templates.d.ts +0 -1
  72. package/dist/lib/prompt-templates.js +1 -1
  73. package/dist/lib/queue.d.ts +3 -15
  74. package/dist/lib/queue.js +69 -8
  75. package/dist/lib/recovery.d.ts +29 -9
  76. package/dist/lib/recovery.js +104 -33
  77. package/dist/lib/replies.d.ts +0 -4
  78. package/dist/lib/replies.js +1 -1
  79. package/dist/lib/routing.d.ts +73 -8
  80. package/dist/lib/routing.js +2206 -307
  81. package/dist/lib/sections.d.ts +0 -2
  82. package/dist/lib/sections.js +1 -1
  83. package/dist/lib/status.d.ts +46 -9
  84. package/dist/lib/status.js +179 -27
  85. package/dist/lib/sync.d.ts +0 -5
  86. package/dist/lib/sync.js +9 -8
  87. package/dist/lib/telegram-api.d.ts +11 -3
  88. package/dist/lib/telegram-api.js +34 -23
  89. package/dist/lib/thread-cleanup-manager.d.ts +1 -0
  90. package/dist/lib/thread-cleanup-manager.js +18 -21
  91. package/dist/lib/thread-naming.d.ts +32 -5
  92. package/dist/lib/thread-naming.js +191 -7
  93. package/dist/lib/thread-reconciler.d.ts +2 -0
  94. package/dist/lib/thread-reconciler.js +1 -1
  95. package/dist/lib/threads.d.ts +292 -59
  96. package/dist/lib/threads.js +1664 -373
  97. package/dist/lib/turns.d.ts +0 -1
  98. package/dist/lib/turns.js +1 -1
  99. package/dist/lib/updates.d.ts +184 -28
  100. package/dist/lib/updates.js +1032 -114
  101. package/dist/lib/wire.d.ts +12 -0
  102. package/dist/lib/wire.js +21 -0
  103. package/dist/lib/workspace-admission.d.ts +25 -17
  104. package/dist/lib/workspace-admission.js +95 -20
  105. package/dist/lib/workspace-identity.d.ts +20 -0
  106. package/dist/lib/workspace-identity.js +103 -0
  107. package/dist/lib/workspace-retirement.d.ts +45 -10
  108. package/dist/lib/workspace-retirement.js +196 -64
  109. package/dist/package.json +6 -6
  110. package/dist/skills/generated-control-surface/SKILL.md +3 -3
  111. package/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
  112. package/docs/README.md +1 -1
  113. package/docs/activity.md +1 -1
  114. package/docs/architecture.md +158 -39
  115. package/docs/callback-namespaces.md +1 -1
  116. package/docs/delivery.md +1 -1
  117. package/docs/generative-apps.md +2 -2
  118. package/docs/inbound.md +1 -1
  119. package/docs/multi-instance-bus.md +127 -19
  120. package/docs/public-api.md +5 -3
  121. package/docs/ui-style.md +26 -8
  122. package/docs/updates.md +1 -1
  123. package/lib/activity-verbosity.ts +5 -9
  124. package/lib/bindings.ts +23 -10
  125. package/lib/bus-api.ts +5 -2
  126. package/lib/bus-follower.ts +251 -226
  127. package/lib/bus-leader.ts +178 -31
  128. package/lib/bus-transport.ts +30 -8
  129. package/lib/bus.ts +110 -334
  130. package/lib/channel-posts.ts +29 -29
  131. package/lib/command-templates.ts +5 -5
  132. package/lib/commands.ts +150 -112
  133. package/lib/config.ts +12 -17
  134. package/lib/delivery.ts +2 -8
  135. package/lib/extension.ts +282 -72
  136. package/lib/generative-apps.ts +0 -22
  137. package/lib/inbound.ts +2 -2
  138. package/lib/journal.ts +1182 -326
  139. package/lib/keyboard.ts +2 -2
  140. package/lib/lifecycle.ts +141 -5
  141. package/lib/locks.ts +402 -23
  142. package/lib/logging.ts +151 -101
  143. package/lib/media.ts +51 -9
  144. package/lib/menu-model.ts +8 -8
  145. package/lib/menu-settings.ts +18 -18
  146. package/lib/menu-status.ts +11 -0
  147. package/lib/menu-thinking.ts +2 -2
  148. package/lib/menu.ts +5 -1
  149. package/lib/model.ts +2 -4
  150. package/lib/outbound-attachments.ts +1 -1
  151. package/lib/outbound-buttons.ts +1 -10
  152. package/lib/outbound-markup.ts +5 -24
  153. package/lib/outbound.ts +5 -5
  154. package/lib/paths.ts +177 -17
  155. package/lib/pi.ts +5 -2
  156. package/lib/polling.ts +5 -5
  157. package/lib/preview.ts +3 -3
  158. package/lib/prompt-templates.ts +1 -1
  159. package/lib/queue.ts +67 -9
  160. package/lib/recovery.ts +104 -44
  161. package/lib/replies.ts +1 -1
  162. package/lib/routing.ts +1955 -375
  163. package/lib/sections.ts +1 -1
  164. package/lib/status.ts +185 -36
  165. package/lib/sync.ts +8 -8
  166. package/lib/telegram-api.ts +48 -33
  167. package/lib/thread-cleanup-manager.ts +24 -21
  168. package/lib/thread-naming.ts +267 -9
  169. package/lib/thread-reconciler.ts +3 -1
  170. package/lib/threads.ts +1718 -491
  171. package/lib/turns.ts +1 -1
  172. package/lib/updates.ts +1072 -151
  173. package/lib/wire.ts +28 -0
  174. package/lib/workspace-admission.ts +97 -47
  175. package/lib/workspace-identity.ts +147 -0
  176. package/lib/workspace-retirement.ts +234 -102
  177. package/package.json +6 -6
  178. package/scripts/audit-exports.mjs +100 -0
  179. package/scripts/check-downgrade.mjs +80 -43
  180. package/skills/generated-control-surface/SKILL.md +3 -3
  181. package/skills/telegram-bridge/references/diagnosis.md +3 -3
package/lib/extension.ts CHANGED
@@ -47,7 +47,9 @@ import * as Sync from "./sync.ts";
47
47
  import * as TelegramApi from "./telegram-api.ts";
48
48
  import * as TextGroups from "./text-groups.ts";
49
49
  import * as ThreadReconciler from "./thread-reconciler.ts";
50
+ import * as Targets from "./target.ts";
50
51
  import * as ThreadDisplay from "./thread-display.ts";
52
+ import * as ThreadNaming from "./thread-naming.ts";
51
53
  import * as Threads from "./threads.ts";
52
54
  import * as TimeInjection from "./time-injection.ts";
53
55
  import * as Updates from "./updates.ts";
@@ -66,6 +68,7 @@ const telegramBusProtocolIdentity =
66
68
  Bus.TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE,
67
69
  Bus.TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT,
68
70
  Bus.TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT,
71
+ Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE,
69
72
  ]);
70
73
 
71
74
  // --- Extension Runtime ---
@@ -85,12 +88,13 @@ export default function (pi: Pi.ExtensionAPI) {
85
88
  } = piRuntime;
86
89
  const bridgeRuntime = Runtime.createTelegramBridgeRuntime();
87
90
  const runtimeDiagnostics =
88
- Logging.createTelegramRuntimeDiagnosticsRuntime<Pi.ExtensionContext>();
91
+ Logging.createTelegramRuntimeDiagnosticsRuntime<Pi.ExtensionContext>({ sharedFile: true });
89
92
  const runtimeEvents = runtimeDiagnostics.events;
90
93
  const recordRuntimeEvent = runtimeDiagnostics.recordRuntimeEvent;
91
94
  const configStore = Config.createTelegramConfigStore({ recordRuntimeEvent });
92
95
  const busProcessRuntime = Bus.createCurrentTelegramBusProcessRuntime({
93
96
  getActiveProfileName: configStore.getActiveProfileName,
97
+ endpointLayout: "consolidated",
94
98
  });
95
99
  const {
96
100
  instanceId: telegramInstanceId,
@@ -105,7 +109,7 @@ export default function (pi: Pi.ExtensionAPI) {
105
109
  WorkspaceAdmission.createTelegramWorkspaceAdmissionRuntimeBinding({
106
110
  getProfileName: configStore.getActiveProfileName,
107
111
  getBotToken: configStore.getBotToken,
108
- getPath: Paths.resolveTelegramWorkspaceAdmissionPathForProfile,
112
+ getStatePath: Paths.resolveTelegramStatePath,
109
113
  owner: {
110
114
  processId: telegramProcessId,
111
115
  processBirthId: telegramQueueProcessBirthId,
@@ -157,24 +161,30 @@ export default function (pi: Pi.ExtensionAPI) {
157
161
  getBotToken: configStore.getBotToken,
158
162
  getBotId: getTelegramBotId,
159
163
  });
164
+ const telegramLeaderJournalPath = Locks.createTelegramLeaderJournalPathResolver({
165
+ getNamedJournalPath() { return lockRuntime.getJournalPath(); },
166
+ getSessionId() { return Pi.getExtensionContextSessionId(telegramSessionContextStore.get()); },
167
+ getProfileName: configStore.getActiveProfileName,
168
+ });
160
169
  const telegramJournalBindingRuntime =
161
170
  Journal.createTelegramUpdateJournalBindingRuntime({
162
171
  base: {
163
172
  getProfileName: configStore.getActiveProfileName,
164
173
  getBotToken: configStore.getBotToken,
165
174
  getBotId: getTelegramBotId,
175
+ withSourceSerialization: configStore.withSourceSerialization,
166
176
  onRecovery(event) {
167
177
  recordRuntimeEvent(
168
178
  "recovery",
169
179
  event.kind === "repaired"
170
180
  ? "Telegram update journal was repaired automatically."
171
- : "Telegram update journal was reset after its damaged files were quarantined.",
181
+ : "Telegram update journal was reset after its damaged files were deleted.",
172
182
  {
173
183
  phase: "journal-auto-recovery",
174
184
  recoveryKind: event.kind,
175
185
  journalPath: event.path,
176
186
  revision: event.revision,
177
- quarantinePath: event.quarantinePath,
187
+ deletedPaths: event.deletedPaths,
178
188
  reason: event.reason,
179
189
  },
180
190
  );
@@ -188,8 +198,12 @@ export default function (pi: Pi.ExtensionAPI) {
188
198
  },
189
199
  getWorkspaceAdmission: workspaceAdmissionRuntime.resolve,
190
200
  },
191
- getLeaderJournalPath: Paths.resolveTelegramUpdateJournalPathForProfile,
192
- getFollowerJournalPath(bindingKey, profileName) {
201
+ getLeaderJournalPath: telegramLeaderJournalPath.resolve,
202
+ getRuntimeDir: Paths.resolveTelegramTempDir,
203
+ getFollowerJournalPath(bindingKey, profileName, sessionId) {
204
+ if (sessionId !== undefined) return Paths.resolveTelegramSessionJournalPath(
205
+ sessionId, bindingKey, undefined, profileName,
206
+ );
193
207
  return Paths.resolveTelegramFollowerJournalPath(
194
208
  bindingKey,
195
209
  undefined,
@@ -197,6 +211,11 @@ export default function (pi: Pi.ExtensionAPI) {
197
211
  );
198
212
  },
199
213
  getActiveFollowerBindingKey: getTelegramManualFollowerProfileKey,
214
+ getActiveFollowerSessionId() {
215
+ return telegramBusFollowerRegistrationState.getSessionId(
216
+ Pi.getExtensionContextSessionId(telegramSessionContextStore.get()),
217
+ );
218
+ },
200
219
  isFollowerRegistered: telegramBusFollowerRegistrationState.isRegistered,
201
220
  });
202
221
  const telegramJournalReferenceRegistry =
@@ -213,31 +232,46 @@ export default function (pi: Pi.ExtensionAPI) {
213
232
  const configControls = Config.createTelegramConfigControls(configStore);
214
233
  const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>({
215
234
  key: Locks.createTelegramLockKeyResolver(configStore),
235
+ statePath: Paths.resolveTelegramStatePath(),
216
236
  instanceId: telegramInstanceId,
217
237
  busSecret: telegramBusAuthSecret,
218
238
  staleHeartbeatMs: Locks.TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS,
239
+ legacyLocksPath: Paths.resolveLegacyTelegramOwnersPath(),
240
+ createJournalPath() { return telegramLeaderJournalPath.createJournalPath(); },
219
241
  });
242
+ const telegramSessionContextStore =
243
+ Lifecycle.createTelegramSessionContextStore<Pi.ExtensionContext>({
244
+ getIdentity(ctx) {
245
+ return ctx.sessionManager ?? ctx.cwd;
246
+ },
247
+ });
248
+ const captureTelegramStateAuthority = Locks.createTelegramOwnedStateAuthorityCapture(
249
+ lockRuntime, telegramSessionContextStore);
220
250
  const threadStore = Threads.createTelegramTopicTargetStore({
221
- path: function () {
222
- return Threads.getTelegramTopicTargetsPath(
223
- undefined,
224
- configStore.getActiveProfileName(),
225
- );
226
- },
251
+ path: Paths.resolveTelegramStatePath,
227
252
  telegramProfile: function () {
228
253
  return configStore.getActiveProfileName();
229
254
  },
230
255
  canPersist: lockRuntime.owns,
231
- commitPersist: lockRuntime.commitIfOwned,
256
+ consolidated: {
257
+ captureAuthority: captureTelegramStateAuthority,
258
+ publishIfOwned: lockRuntime.publishStateSectionIfOwned!,
259
+ },
232
260
  getExternalReservedSlots: function () {
233
261
  return workspaceAdmissionRuntime.resolve()?.listReservedSlots() ?? [];
234
262
  },
235
263
  });
264
+ const resolveWorkspaceRestoreStore = Threads.createTelegramWorkspaceRestoreResolver({
265
+ getProfileName: configStore.getActiveProfileName,
266
+ getBotToken: configStore.getBotToken,
267
+ threadStore,
268
+ });
236
269
  runtimeDiagnostics.bindStorage({
237
270
  getBotToken: configStore.getBotToken,
238
271
  getProfileName: configStore.getActiveProfileName,
239
272
  canReset: lockRuntime.owns,
240
273
  commitReset: lockRuntime.commitIfOwned,
274
+ captureAuthority: captureTelegramStateAuthority,
241
275
  });
242
276
  const sessionActionAssembly = Commands.createTelegramSessionActionAssembly({
243
277
  registerCommand,
@@ -276,12 +310,6 @@ export default function (pi: Pi.ExtensionAPI) {
276
310
  const lockOwnershipGuard =
277
311
  Locks.createTelegramLockOwnershipGuard(lockRuntime);
278
312
  const getCurrentLeaderEpoch = lockRuntime.getOwnedLeaderEpoch;
279
- const telegramSessionContextStore =
280
- Lifecycle.createTelegramSessionContextStore<Pi.ExtensionContext>({
281
- getIdentity(ctx) {
282
- return ctx.sessionManager ?? ctx.cwd;
283
- },
284
- });
285
313
  const ownsTelegramDirectDelivery =
286
314
  Locks.createTelegramDirectDeliveryOwnershipChecker({
287
315
  lock: lockRuntime,
@@ -360,13 +388,15 @@ export default function (pi: Pi.ExtensionAPI) {
360
388
  );
361
389
  const telegramApiTargetActivityRuntime =
362
390
  TelegramApi.createTelegramApiTargetActivityRuntime();
363
- const captureWorkspaceExternalProtection =
364
- WorkspaceRetirement.createTelegramWorkspaceExternalProtectionCapture({
391
+ const workspaceProtectionObserver =
392
+ WorkspaceRetirement.createTelegramWorkspaceProtectionObserver({
365
393
  listFollowers: telegramBusFollowerRegistry.list,
366
394
  getActiveTurnTarget: activeTurnRuntime.getTarget,
367
395
  getQueuedItems: telegramQueueStore.getQueuedItems,
368
396
  resolveLeaderJournal: resolveTelegramUpdateJournalBinding,
369
397
  createFollowerJournalResolver:
398
+ telegramJournalBindingRuntime.createLegacyRecipientResolver,
399
+ createSessionJournalResolver:
370
400
  telegramJournalBindingRuntime.createRecipientResolver,
371
401
  withJournalReference(binding, operation) {
372
402
  if (!binding.recoveryKey) throw new Error(
@@ -378,12 +408,24 @@ export default function (pi: Pi.ExtensionAPI) {
378
408
  }, operation);
379
409
  },
380
410
  discoverFollowerJournals() {
381
- return Journal.discoverTelegramFollowerJournalPaths({
411
+ return Journal.discoverTelegramRecipientJournalPaths({
382
412
  directory: Paths.resolveTelegramTempDir(),
383
413
  profileName: configStore.getActiveProfileName(),
384
414
  });
385
415
  },
386
416
  createJournalPathResolver: telegramJournalBindingRuntime.createPathResolver,
417
+ inspectJournalNamespace() {
418
+ const botToken = configStore.getBotToken();
419
+ if (!botToken) throw new Error("Telegram namespace inspection requires current bot identity.");
420
+ return Journal.inspectTelegramSessionJournalNamespace({
421
+ directory: Paths.resolveTelegramTempDir(),
422
+ profile: configStore.getActiveProfileName() ?? "default",
423
+ pollingPath: telegramLeaderJournalPath.resolve(configStore.getActiveProfileName()),
424
+ botIdentity: Journal.createTelegramUpdateJournalBotIdentity({ botToken, botId: getTelegramBotId() }),
425
+ limits: { maxDirectoryEntries: 10_000, maxFiles: 4096, maxBytes: 64 * 1024 * 1024,
426
+ maxEntries: 10_000, maxWork: 100_000 },
427
+ });
428
+ },
387
429
  getJournalWriterProtection(journalBindingKey) {
388
430
  const owner = Threads.getTelegramThreadOwnerFromProfileKey(
389
431
  journalBindingKey,
@@ -397,11 +439,24 @@ export default function (pi: Pi.ExtensionAPI) {
397
439
  : liveness === "dead" ? "clear" : "unknown";
398
440
  },
399
441
  getDeliveryAuthorityProtection(binding) {
400
- return telegramApiTargetActivityRuntime.hasPendingTarget(binding.target)
442
+ const retainedRestore = resolveWorkspaceRestoreStore()?.list().some(function (intent) {
443
+ return intent.request.binding.bindingKey === binding.bindingKey ||
444
+ Targets.areTelegramTargetsEqual(intent.request.binding.target, binding.target) ||
445
+ Targets.areTelegramTargetsEqual(intent.request.target, binding.target);
446
+ });
447
+ return retainedRestore || telegramApiTargetActivityRuntime.hasPendingTarget(binding.target)
401
448
  ? "protected"
402
449
  : "clear";
403
450
  },
404
451
  });
452
+ const captureWorkspaceExternalProtection = workspaceProtectionObserver.capture;
453
+ const pruneWorkspaceJournalEvidence = WorkspaceRetirement.createTelegramWorkspaceJournalEvidencePruner({
454
+ store: threadStore,
455
+ getAdmission: workspaceAdmissionRuntime.resolve,
456
+ getLeaderEpoch: getCurrentLeaderEpoch,
457
+ runExclusive: telegramWorkspaceOperationRuntime.runExclusive,
458
+ protection: workspaceProtectionObserver,
459
+ });
405
460
  const reclaimTelegramWorkspaceDeadOwnerQueue =
406
461
  WorkspaceRetirement.createTelegramWorkspaceDeadOwnerQueueReclaimer({
407
462
  getExternalProtection: captureWorkspaceExternalProtection,
@@ -409,9 +464,11 @@ export default function (pi: Pi.ExtensionAPI) {
409
464
  getQueuedItems: telegramQueueStore.getQueuedItems,
410
465
  resolveLeaderJournal: resolveTelegramUpdateJournalBinding,
411
466
  createFollowerJournalResolver:
467
+ telegramJournalBindingRuntime.createLegacyRecipientResolver,
468
+ createSessionJournalResolver:
412
469
  telegramJournalBindingRuntime.createRecipientResolver,
413
470
  discoverFollowerJournals() {
414
- return Journal.discoverTelegramFollowerJournalPaths({
471
+ return Journal.discoverTelegramRecipientJournalPaths({
415
472
  directory: Paths.resolveTelegramTempDir(),
416
473
  profileName: configStore.getActiveProfileName(),
417
474
  });
@@ -460,7 +517,7 @@ export default function (pi: Pi.ExtensionAPI) {
460
517
  const botToken = configStore.getBotToken();
461
518
  if (!botToken) throw new Error("Telegram Thread cleanup review requires an active bot token.");
462
519
  return ThreadCleanupManager.createTelegramThreadCleanupWorkStore({
463
- path: Paths.resolveTelegramThreadCleanupWorkPath(undefined, profileName),
520
+ ...Paths.resolveTelegramServiceJournalStorage("thread-cleanup", undefined, profileName),
464
521
  profileName,
465
522
  tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
466
523
  });
@@ -573,9 +630,10 @@ export default function (pi: Pi.ExtensionAPI) {
573
630
  instanceId: telegramInstanceId,
574
631
  updateStatus: statusRuntime.updateStatus,
575
632
  getStatusState: statusRuntime.getStatusState,
633
+ session: telegramSessionContextStore,
576
634
  async persistSnapshot(snapshot) {
577
635
  threadStore.setStatusSnapshot(snapshot);
578
- await threadStore.persist();
636
+ await threadStore.persistStatus();
579
637
  },
580
638
  });
581
639
  const updateStatus = runtimeDiagnostics.updateStatus;
@@ -592,6 +650,10 @@ export default function (pi: Pi.ExtensionAPI) {
592
650
  const directTelegramApiRuntime =
593
651
  TelegramApi.createDefaultTelegramBridgeApiRuntime({
594
652
  getBotToken: configStore.getBotToken,
653
+ getBotScope() {
654
+ const config = configStore.get();
655
+ return config.botUsername ?? (config.botId === undefined ? undefined : String(config.botId));
656
+ },
595
657
  recordRuntimeEvent,
596
658
  targetActivity: telegramApiTargetActivityRuntime,
597
659
  workspaceAdmission: workspaceAdmissionRuntime.resolve,
@@ -896,6 +958,10 @@ export default function (pi: Pi.ExtensionAPI) {
896
958
  getActiveModel: currentModelRuntime.get,
897
959
  getThinkingLevel,
898
960
  getQueueItemCount,
961
+ getPendingCancellationCount() {
962
+ return getCurrentLeaderEpoch() === undefined ? 0 :
963
+ updateAdmissionRuntimeBinding.getActive()?.getState()?.abandoningClaimCount ?? 0;
964
+ },
899
965
  buildStatusHtml: Commands.createTelegramAppMenuHtmlBuilder({
900
966
  buildStatusHtml: Status.createTelegramStatusHtmlBuilder({
901
967
  getActiveModel: currentModelRuntime.get,
@@ -988,21 +1054,12 @@ export default function (pi: Pi.ExtensionAPI) {
988
1054
 
989
1055
  const foreignOwnedUpdateForwarder =
990
1056
  telegramBusFollowerClients.foreignOwnedUpdateForwarder;
991
- const followerTargetController = telegramBusFollowerClients.targetController;
992
- const restoreFollowerThreadTarget =
993
- Bus.createTelegramBusFollowerThreadRestoreHandler({
994
- followerRegistry: telegramBusFollowerRegistry,
995
- followerTargetController,
996
- onRestored() {
997
- telegramSyncStateRuntime.markSliceFresh(
998
- Sync.TELEGRAM_SYNC_SLICE_TARGET_BINDINGS,
999
- {
1000
- nowMs: Date.now(),
1001
- action: "follower-thread-restore",
1002
- },
1003
- );
1004
- },
1005
- });
1057
+ const workspaceRestoreFollowerController = Bus.createTelegramBusWorkspaceRestoreController({
1058
+ getFollower: telegramBusFollowerRegistry.get,
1059
+ localProtocolIdentity: telegramBusProtocolIdentity,
1060
+ createRequestId: telegramBusFollowerClients.createRequestId,
1061
+ getAuthSecret() { return telegramBusAuthSecret; },
1062
+ });
1006
1063
  const observedThreadTargetBinding =
1007
1064
  Polling.createTelegramThreadTargetObservationBinding<Pi.ExtensionContext>();
1008
1065
  const topicLifecycleSync =
@@ -1057,7 +1114,62 @@ export default function (pi: Pi.ExtensionAPI) {
1057
1114
  return observedThreadTargetBinding.handle(ctx);
1058
1115
  },
1059
1116
  foreignOwnedUpdateForwarder,
1060
- replaceFollowerThreadTarget: restoreFollowerThreadTarget,
1117
+ workspaceRestoreRecipient: {
1118
+ getSessionId: Pi.getExtensionContextSessionId,
1119
+ getCwd: Pi.getExtensionContextCwd,
1120
+ getLeaderIdentity: telegramBusLeaderState.getIdentity,
1121
+ followerRegistry: telegramBusFollowerRegistry,
1122
+ runFollower: workspaceRestoreFollowerController,
1123
+ },
1124
+ getWorkspaceRestoreStore: resolveWorkspaceRestoreStore,
1125
+ captureWorkspaceExternalProtection,
1126
+ inspectRestoreSourceCompletion(expected) {
1127
+ return telegramJournalReferenceRegistry.withReference({ referenceClass: "operator-disposition",
1128
+ recoveryKey: expected.journalBindingKey }, function () {
1129
+ const observed = telegramJournalBindingRuntime.inspectSourceCompletion(expected.journalBindingKey, { updateId: expected.updateId,
1130
+ sourceSha256: expected.sourceSha256, completionSha256: expected.completionSha256 });
1131
+ return observed && { journalBindingKey: expected.journalBindingKey, ...observed };
1132
+ });
1133
+ },
1134
+ inspectRestoreQueuedReceipt(expected) {
1135
+ return telegramJournalReferenceRegistry.withReference({ referenceClass: "operator-disposition",
1136
+ recoveryKey: expected.journalBindingKey }, function () {
1137
+ return telegramJournalBindingRuntime.inspectQueuedReceipt(expected.journalBindingKey, { queueKind: expected.queueKind, receiptId: expected.receiptId,
1138
+ sourceUpdateIds: [...expected.sourceUpdateIds], queueOwner: { ...expected.queueOwner } });
1139
+ });
1140
+ },
1141
+ inspectRestoreSourceAbandonment(updateId, journalBindingKey) {
1142
+ return telegramJournalReferenceRegistry.withReference({
1143
+ referenceClass: "operator-disposition",
1144
+ recoveryKey: journalBindingKey,
1145
+ }, function () {
1146
+ return telegramJournalBindingRuntime.inspectSourceAbandonment(journalBindingKey, updateId);
1147
+ });
1148
+ },
1149
+ inspectTemporaryThreadSources(_target, requiredJournalBindingKeys) {
1150
+ const binding = resolveTelegramUpdateJournalBinding();
1151
+ const botToken = configStore.getBotToken();
1152
+ if (!binding || !botToken) return undefined;
1153
+ const profile = configStore.getActiveProfileName() ?? "default";
1154
+ const botIdentity = Journal.createTelegramUpdateJournalBotIdentity({ botToken, botId: getTelegramBotId() });
1155
+ return configStore.withSourceSerialization(function () {
1156
+ return Journal.isTelegramThreadCleanupJournalNamespaceClear({
1157
+ directory: Paths.resolveTelegramTempDir(), profile, botIdentity,
1158
+ pollingPath: telegramLeaderJournalPath.resolve(configStore.getActiveProfileName()),
1159
+ requiredJournalBindingKeys: [binding.recoveryKey, ...requiredJournalBindingKeys],
1160
+ limits: { maxDirectoryEntries: 10_000, maxFiles: 4096, maxBytes: 64 * 1024 * 1024,
1161
+ maxEntries: 10_000, maxWork: 100_000 },
1162
+ withSourceReference(path, operation) {
1163
+ return telegramJournalReferenceRegistry.withReference({ referenceClass: "operator-disposition",
1164
+ recoveryKey: Journal.createTelegramUpdateJournalBindingKey({ path, profileName: profile, botIdentity }) }, operation);
1165
+ },
1166
+ }) ? [] : undefined;
1167
+ });
1168
+ },
1169
+ hasWorkspaceRestoreAuthority() {
1170
+ return lockRuntime.owns() && Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE);
1171
+ },
1172
+ getSessionGeneration: telegramSessionContextStore.getGeneration,
1061
1173
  bridgeRuntime,
1062
1174
  requestNewSession(source) {
1063
1175
  const updateId = Updates.getTelegramUpdateExecutionFence(source)?.updateId;
@@ -1131,7 +1243,7 @@ export default function (pi: Pi.ExtensionAPI) {
1131
1243
  sendTextReply,
1132
1244
  setMyCommands,
1133
1245
  validateThreadName(threadName) {
1134
- return Threads.getTelegramManualThreadDisplayNameValidationError(
1246
+ return ThreadNaming.getTelegramManualThreadDisplayNameValidationError(
1135
1247
  threadName,
1136
1248
  );
1137
1249
  },
@@ -1197,10 +1309,43 @@ export default function (pi: Pi.ExtensionAPI) {
1197
1309
  dispatchNext: dispatchNextQueuedTelegramTurn,
1198
1310
  requestQueueHandoffReconciliation:
1199
1311
  queueHandoffReconciliationBinding.request,
1200
- afterUpdateCompleted: sessionActionsRuntime.onUpdateCompleted,
1312
+ afterQueueReceiptCommitted: inboundRouteRuntime.onQueueReceiptCommitted,
1313
+ afterUpdateCompleted(updateId, ctx, journalBindingKey) {
1314
+ sessionActionsRuntime.onUpdateCompleted(updateId);
1315
+ inboundRouteRuntime.onUpdateCompleted(updateId, ctx, journalBindingKey);
1316
+ },
1201
1317
  },
1202
1318
  worker: {
1319
+ expireRoutingInput: inboundRouteRuntime.expireRoutingInput,
1320
+ beforeQueueReceiptPublished: inboundRouteRuntime.beforeQueueReceiptPublished,
1321
+ onQueueReceiptCompleted: inboundRouteRuntime.onQueueReceiptCompleted,
1203
1322
  defaultHandle: inboundRouteRuntime.handleUpdate,
1323
+ async shouldReviewHistoricalInput(entry, ctx, signal) {
1324
+ if (signal.aborted || !telegramSessionContextStore.isCurrent(ctx)) {
1325
+ throw new Error("Workspace Restore source generation ended.");
1326
+ }
1327
+ const verdict = await inboundRouteRuntime.shouldReviewHistoricalInput(entry, ctx, signal);
1328
+ if (signal.aborted || !telegramSessionContextStore.isCurrent(ctx)) {
1329
+ throw new Error("Workspace Restore source generation ended.");
1330
+ }
1331
+ if (verdict === "retain") return verdict;
1332
+ const journalBindingKey = getTelegramQueueJournalBinding();
1333
+ if (journalBindingKey && resolveWorkspaceRestoreStore()?.list().some(function (intent) {
1334
+ return intent.request.source.journalBindingKey === journalBindingKey &&
1335
+ intent.request.source.updateIds.includes(entry.updateId);
1336
+ })) return true;
1337
+ return verdict;
1338
+ },
1339
+ spendHistoricalInput: true,
1340
+ onHeldSourcesPrepared(input): Promise<void> {
1341
+ return inboundRouteRuntime.forgetPreviousWorld(input, lockedPollingRuntime.captureTransportAuthority).then(function () {});
1342
+ },
1343
+ async shouldHoldPendingInput(entry, ctx, signal) {
1344
+ if (signal.aborted || !telegramSessionContextStore.isCurrent(ctx)) {
1345
+ throw new Error("Temporary Thread source generation ended.");
1346
+ }
1347
+ return inboundRouteRuntime.shouldHoldPendingInput(entry, ctx, signal);
1348
+ },
1204
1349
  onStateChange: runtimeDiagnostics.scheduleSnapshotPersist,
1205
1350
  settleTerminalExecutionFailure(error) {
1206
1351
  return Sync.settleStaleTelegramTopicExecutionFailure(
@@ -1217,6 +1362,11 @@ export default function (pi: Pi.ExtensionAPI) {
1217
1362
  resolveBinding: resolveTelegramFollowerJournalBinding,
1218
1363
  isRegistered: telegramBusFollowerRegistrationState.isRegistered,
1219
1364
  getGeneration: telegramBusFollowerRegistrationState.getGeneration,
1365
+ prepareBinding(binding) {
1366
+ telegramJournalBindingRuntime.prepareActiveFollowerSuccession(function isCurrent() {
1367
+ return binding.hasAuthority?.() === true;
1368
+ });
1369
+ },
1220
1370
  prepareUpdateForExecution(update) {
1221
1371
  return BusFollower.prepareTelegramBusFollowerJournaledUpdateForExecution(
1222
1372
  update,
@@ -1296,6 +1446,32 @@ export default function (pi: Pi.ExtensionAPI) {
1296
1446
  local: agentMessageRuntime,
1297
1447
  follower: telegramBusFollowerClients.agentMessages,
1298
1448
  });
1449
+ const followerWorkspaceRestore = BusFollower.createTelegramBusFollowerWorkspaceRestoreHandler({
1450
+ instanceId: telegramInstanceId,
1451
+ getContextAuthority: BusFollower.createTelegramBusFollowerRestoreContextGetter({
1452
+ isContextCurrent: telegramSessionContextStore.isCurrent,
1453
+ getSessionId: Pi.getExtensionContextSessionId,
1454
+ getCwd: Pi.getExtensionContextCwd,
1455
+ getGeneration: telegramSessionContextStore.getGeneration,
1456
+ getProfileBindingKey() {
1457
+ return workspaceAdmissionRuntime.resolve()?.getProfileKey();
1458
+ },
1459
+ getOperatorUserId: configStore.getAllowedUserId,
1460
+ getLeaderState: lockRuntime.getState,
1461
+ getAuthenticatedSecret: telegramBusFollowerControlState.getActiveAuthSecret,
1462
+ getLeaderProtocol: telegramBusFollowerRegistrationState.getLeaderProtocol,
1463
+ }),
1464
+ readRestoreIntent(operationId, profileBindingKey) {
1465
+ if (profileBindingKey !== workspaceAdmissionRuntime.resolve()?.getProfileKey()) return undefined;
1466
+ return resolveWorkspaceRestoreStore()?.list().find(function (intent) {
1467
+ return intent.request.operationId === operationId;
1468
+ });
1469
+ },
1470
+ topicTargetStore: threadStore,
1471
+ registrationState: telegramBusFollowerRegistrationState,
1472
+ getWorkspaceAdmission: workspaceAdmissionRuntime.resolve,
1473
+ recordRuntimeEvent,
1474
+ });
1299
1475
  const telegramBusFollowerAssembly: BusFollower.TelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext> =
1300
1476
  BusFollower.createTelegramBusFollowerRuntimeAssembly<Pi.ExtensionContext>({
1301
1477
  instanceId: telegramInstanceId,
@@ -1308,15 +1484,11 @@ export default function (pi: Pi.ExtensionAPI) {
1308
1484
  getRecipientBindingKey: getTelegramManualFollowerProfileKey,
1309
1485
  durableAdmission: followerDurableAdmissionRuntime,
1310
1486
  handleQueueHandoff: acceptStagedQueueHandoff,
1311
- },
1312
- targetReplacement: {
1313
- topicTargetStore: threadStore,
1314
- getManualFollowerProfileKey: getTelegramManualFollowerProfileKey,
1315
- manualFollowerOwnerId: telegramManualFollowerOwnerId,
1316
- getWorkspaceAdmission: workspaceAdmissionRuntime.resolve,
1317
- getSyncState: telegramSyncStateRuntime.getState,
1318
- setSyncState: telegramSyncStateRuntime.setState,
1319
- updateStatus,
1487
+ handleWorkspaceRestore: followerWorkspaceRestore,
1488
+ isWorkspaceRestoreEnabled() {
1489
+ return Bus.hasTelegramBusCapability(telegramBusProtocolIdentity, Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) &&
1490
+ Bus.hasTelegramBusCapability(telegramBusFollowerRegistrationState.getLeaderProtocol(), Bus.TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE);
1491
+ },
1320
1492
  },
1321
1493
  recovery: {
1322
1494
  getLeaderState: lockRuntime.getState,
@@ -1424,6 +1596,17 @@ export default function (pi: Pi.ExtensionAPI) {
1424
1596
  const authorizeFollowerApiCall = Bus.createTelegramFollowerApiCallAuthorizer({
1425
1597
  isMessageOwned: messageOwnershipRuntime.isOwnedByFollower,
1426
1598
  });
1599
+ const telegramSessionFolderSweeper = Recovery.createTelegramSessionFolderSweeper({
1600
+ getSessionsDir: Paths.resolveTelegramSessionsDir,
1601
+ getProfileName: configStore.getActiveProfileName,
1602
+ getKeptSessionIds() {
1603
+ return [
1604
+ ...threadStore.listWorkspaceBindings().map(function bindingSession(binding) { return binding.sessionId; }),
1605
+ ...telegramBusFollowerRegistry.list().map(function followerSession(follower) { return follower.sessionId; }),
1606
+ Pi.getExtensionContextSessionId(telegramSessionContextStore.get()),
1607
+ ];
1608
+ },
1609
+ });
1427
1610
  const telegramBusLeaderRuntime =
1428
1611
  BusLeader.createTelegramBusLeaderRuntimeAssembly<Pi.ExtensionContext>({
1429
1612
  runtime: {
@@ -1448,11 +1631,15 @@ export default function (pi: Pi.ExtensionAPI) {
1448
1631
  });
1449
1632
  },
1450
1633
  isFollowerProcessAlive: Locks.isProcessAlive,
1634
+ afterFollowerPrune() { telegramSessionFolderSweeper.sweep(); },
1451
1635
  shouldCleanupConfirmedDeadFollower:
1452
1636
  configControls.resolveAutomaticThreadCleanupEnabled,
1453
1637
  recordFollowerMessageOwnership(record) {
1454
1638
  messageOwnershipRuntime.recordFollower(record);
1455
1639
  },
1640
+ onWorkspaceRestoreRecipientObserved(follower, isCurrent) {
1641
+ return inboundRouteRuntime.onWorkspaceRestoreRecipientObserved(follower, isCurrent, telegramSessionContextStore.get());
1642
+ },
1456
1643
  },
1457
1644
  getAllowedUserId: configStore.getAllowedUserId,
1458
1645
  instanceId: telegramInstanceId,
@@ -1481,6 +1668,7 @@ export default function (pi: Pi.ExtensionAPI) {
1481
1668
  getAdmission: workspaceAdmissionRuntime.resolve,
1482
1669
  runExclusive: telegramWorkspaceOperationRuntime.runExclusive,
1483
1670
  deleteThread: directTelegramApiRuntime.deleteWorkspaceThread,
1671
+ pruneJournalEvidence: pruneWorkspaceJournalEvidence,
1484
1672
  reclaimDeadOwnerQueuedWork(binding, isCurrent) {
1485
1673
  return telegramWorkspaceOperationRuntime.run({
1486
1674
  operationId: WorkspaceAdmission.createTelegramWorkspaceAdmissionOperationId(),
@@ -1583,6 +1771,12 @@ export default function (pi: Pi.ExtensionAPI) {
1583
1771
  const threadAwarePollingPorts = telegramThreadCapabilityRuntime.pollingPorts;
1584
1772
  const lockedPollingRuntime = Locks.createTelegramLockedPollingRuntime({
1585
1773
  lock: lockRuntime,
1774
+ resetDamagedState() {
1775
+ return Locks.resetDamagedTelegramRuntimeState(Paths.resolveTelegramStatePath(), function (profile, sections) {
1776
+ Threads.parseTelegramWorkspaceStateSection(sections.workspace, profile);
1777
+ WorkspaceAdmission.assertTelegramConsolidatedAdmissionSection(sections.admission, profile);
1778
+ });
1779
+ },
1586
1780
  transportMonitor: telegramThreadCapabilityMonitor,
1587
1781
  hasBotToken: configStore.hasBotToken,
1588
1782
  getBotTokenDiagnostic: configStore.getBotTokenDiagnostic,
@@ -1633,6 +1827,27 @@ export default function (pi: Pi.ExtensionAPI) {
1633
1827
  resolveAutomaticThreadCleanupEnabled: configControls.resolveAutomaticThreadCleanupEnabled,
1634
1828
  runWorkspaceOperation: telegramWorkspaceOperationRuntime.run,
1635
1829
  });
1830
+ const connectionIntent = Lifecycle.createTelegramConnectionIntentRuntime();
1831
+ const connectionLifecycle = Lifecycle.createTelegramConnectionLifecycle({
1832
+ intent: connectionIntent,
1833
+ getGeneration: telegramSessionContextStore.getGeneration,
1834
+ isCurrent: telegramSessionContextStore.isCurrent,
1835
+ getProfileName: configStore.getActiveProfileName,
1836
+ isConnected() {
1837
+ return lockRuntime.owns() || telegramBusFollowerRegistrationState.isRegistered();
1838
+ },
1839
+ async activateProfile(profileName, isCurrent) {
1840
+ await configStore.load();
1841
+ if (!isCurrent()) return false;
1842
+ return configStore.activateProfile(profileName) && configStore.hasBotToken();
1843
+ },
1844
+ start(ctx) {
1845
+ return lockedPollingRuntime.start(ctx);
1846
+ },
1847
+ recordError(error) {
1848
+ recordRuntimeEvent("connection", error, { phase: "resume-connect" });
1849
+ },
1850
+ });
1636
1851
  const telegramBridgeSessionLifecycleDeps =
1637
1852
  Lifecycle.createTelegramBridgeSessionLifecycleDeps({
1638
1853
  contextStore: telegramSessionContextStore,
@@ -1645,7 +1860,10 @@ export default function (pi: Pi.ExtensionAPI) {
1645
1860
  syncCounters: queue.syncCounters,
1646
1861
  syncFlags: lifecycle.syncFlags,
1647
1862
  bindDeferredDispatchContext: deferredQueueDispatchRuntime.bind,
1648
- prepareTempDir,
1863
+ prepareTempDir() {
1864
+ Recovery.removeTelegramLegacyRecoveryStorage(Paths.resolveTelegramTempDir());
1865
+ return prepareTempDir();
1866
+ },
1649
1867
  updateStatus,
1650
1868
  unbindDeferredDispatchContext: deferredQueueDispatchRuntime.unbind,
1651
1869
  discardQueuedItems: queueMutationRuntime.clear,
@@ -1681,6 +1899,7 @@ export default function (pi: Pi.ExtensionAPI) {
1681
1899
  },
1682
1900
  delivery: deliveryLifecycleRuntime,
1683
1901
  polling: lockedPollingRuntime,
1902
+ connection: connectionLifecycle,
1684
1903
  inboundWorker: {
1685
1904
  onSessionShutdown: updateAdmissionRuntimeBinding.onSessionShutdown,
1686
1905
  },
@@ -1802,20 +2021,7 @@ export default function (pi: Pi.ExtensionAPI) {
1802
2021
  activeTurnRuntime,
1803
2022
  lockedPollingRuntime,
1804
2023
  stopPolling: disconnectTelegramAndDeleteCurrentThread,
1805
- recoverPollingStart: Recovery.createTelegramPollingStartRecoveryHandler({
1806
- getOwnersPath: Paths.resolveTelegramOwnersPath,
1807
- getStatePaths() {
1808
- return [
1809
- Threads.getTelegramTopicTargetsPath(
1810
- undefined,
1811
- configStore.getActiveProfileName(),
1812
- ),
1813
- ];
1814
- },
1815
- suspendPolling: lockedPollingRuntime.suspend,
1816
- releaseOwnership: lockRuntime.release,
1817
- recordRuntimeEvent,
1818
- }),
2024
+ // Consolidated envelope failures remain protective; legacy whole-file recovery cannot reset sibling authority.
1819
2025
  getDisconnectThreadName() {
1820
2026
  const record = findCurrentThreadRecord();
1821
2027
  if (!record?.target.threadId) return undefined;
@@ -1827,6 +2033,9 @@ export default function (pi: Pi.ExtensionAPI) {
1827
2033
  modelContextAvailabilityRuntime.reconcile();
1828
2034
  },
1829
2035
  getStatusLines,
2036
+ isContextCurrent: telegramSessionContextStore.isCurrent,
2037
+ getSessionGeneration: telegramSessionContextStore.getGeneration,
2038
+ connectionIntent,
1830
2039
  buttonActionStore,
1831
2040
  sendMarkdownReply,
1832
2041
  async sendChannelMarkdownMessage(channel, markdown, options) {
@@ -1837,7 +2046,7 @@ export default function (pi: Pi.ExtensionAPI) {
1837
2046
  const botToken = configStore.getBotToken();
1838
2047
  if (!botToken) throw new Error("Telegram channel delivery requires an active bot token.");
1839
2048
  const store = ChannelPosts.createTelegramChannelPostJournalStore({
1840
- path: Paths.resolveTelegramChannelPostJournalPath(undefined, profileName),
2049
+ ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName),
1841
2050
  profileName,
1842
2051
  tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
1843
2052
  });
@@ -1868,7 +2077,7 @@ export default function (pi: Pi.ExtensionAPI) {
1868
2077
  const caption = Replies.renderTelegramMarkdownToHtmlDraft(markdown);
1869
2078
  ChannelPosts.assertTelegramChannelPostCaptionWithinLimit(caption);
1870
2079
  const store = ChannelPosts.createTelegramChannelPostJournalStore({
1871
- path: Paths.resolveTelegramChannelPostJournalPath(undefined, profileName),
2080
+ ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName),
1872
2081
  profileName,
1873
2082
  tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
1874
2083
  });
@@ -1897,7 +2106,7 @@ export default function (pi: Pi.ExtensionAPI) {
1897
2106
  const botToken = configStore.getBotToken();
1898
2107
  if (!botToken) throw new Error("Telegram channel posts require an active bot token.");
1899
2108
  return ChannelPosts.createTelegramChannelPostJournalStore({
1900
- path: Paths.resolveTelegramChannelPostJournalPath(undefined, profileName),
2109
+ ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName),
1901
2110
  profileName,
1902
2111
  tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
1903
2112
  }).list(input);
@@ -1908,7 +2117,7 @@ export default function (pi: Pi.ExtensionAPI) {
1908
2117
  const botToken = configStore.getBotToken();
1909
2118
  if (!botToken) throw new Error("Telegram channel post mutation requires an active bot token.");
1910
2119
  const store = ChannelPosts.createTelegramChannelPostJournalStore({
1911
- path: Paths.resolveTelegramChannelPostJournalPath(undefined, profileName), profileName,
2120
+ ...Paths.resolveTelegramServiceJournalStorage("channel-posts", undefined, profileName), profileName,
1912
2121
  tokenSha256: Journal.createTelegramUpdateJournalBotIdentity({ botToken }).tokenSha256,
1913
2122
  });
1914
2123
  if (input.action === "edit") {
@@ -1970,6 +2179,7 @@ export default function (pi: Pi.ExtensionAPI) {
1970
2179
  },
1971
2180
  activityRuntime,
1972
2181
  activityVerbosityRuntime,
2182
+ diagnostics: runtimeDiagnostics,
1973
2183
  assistantOutputRuntime,
1974
2184
  publicationRuntime,
1975
2185
  configStore,