@llblab/pi-kit 0.27.6 → 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.
Files changed (182) hide show
  1. package/BACKLOG.md +2 -5
  2. package/CHANGELOG.md +5 -0
  3. package/README.md +2 -2
  4. package/node_modules/@llblab/pi-telegram/AGENTS.md +10 -8
  5. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -9
  6. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
  7. package/node_modules/@llblab/pi-telegram/LICENSE +21 -0
  8. package/node_modules/@llblab/pi-telegram/README.md +15 -5
  9. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  10. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +5 -7
  11. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +5 -1
  12. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +6 -1
  13. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +4 -2
  14. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +54 -56
  15. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +206 -139
  16. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +21 -2
  17. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +181 -25
  18. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +4 -1
  19. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +28 -4
  20. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +52 -56
  21. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +78 -246
  22. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +1 -3
  23. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +23 -26
  24. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +0 -5
  25. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +5 -5
  26. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +0 -23
  27. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +15 -15
  28. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +0 -17
  29. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +12 -14
  30. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +0 -4
  31. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +2 -5
  32. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +255 -61
  33. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -19
  34. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +0 -2
  35. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +2 -2
  36. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +177 -16
  37. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +1115 -249
  38. package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +0 -2
  39. package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +2 -2
  40. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +1 -1
  41. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +100 -12
  42. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +380 -14
  43. package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +30 -12
  44. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +129 -72
  45. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +28 -5
  46. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +26 -8
  47. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +0 -11
  48. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +8 -8
  49. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +0 -18
  50. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +18 -18
  51. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +4 -4
  52. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +12 -7
  53. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +1 -1
  54. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +1 -1
  55. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +1 -0
  56. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -3
  57. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +0 -1
  58. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +1 -1
  59. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +1 -8
  60. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +1 -6
  61. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +4 -21
  62. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +0 -8
  63. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +5 -5
  64. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +50 -7
  65. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +155 -17
  66. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +1 -0
  67. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +2 -1
  68. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +0 -5
  69. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -5
  70. package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +0 -3
  71. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +3 -3
  72. package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +0 -1
  73. package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +1 -1
  74. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +3 -15
  75. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +69 -8
  76. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +29 -9
  77. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +104 -33
  78. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +0 -4
  79. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -1
  80. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +73 -8
  81. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2206 -307
  82. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +0 -2
  83. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +1 -1
  84. package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +44 -9
  85. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +149 -27
  86. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +0 -5
  87. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +5 -4
  88. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +11 -3
  89. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +34 -23
  90. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +1 -0
  91. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +18 -21
  92. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +32 -5
  93. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +191 -7
  94. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +2 -0
  95. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +1 -1
  96. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +291 -59
  97. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +1647 -369
  98. package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +0 -1
  99. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -1
  100. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +184 -28
  101. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +1032 -114
  102. package/node_modules/@llblab/pi-telegram/dist/lib/wire.d.ts +12 -0
  103. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +21 -0
  104. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +25 -17
  105. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +95 -20
  106. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.d.ts +20 -0
  107. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +103 -0
  108. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +43 -8
  109. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +148 -52
  110. package/node_modules/@llblab/pi-telegram/dist/package.json +6 -6
  111. package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +3 -3
  112. package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
  113. package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
  114. package/node_modules/@llblab/pi-telegram/docs/activity.md +1 -1
  115. package/node_modules/@llblab/pi-telegram/docs/architecture.md +152 -34
  116. package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
  117. package/node_modules/@llblab/pi-telegram/docs/delivery.md +1 -1
  118. package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +2 -2
  119. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  120. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +125 -19
  121. package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -1
  122. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +22 -8
  123. package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
  124. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +5 -9
  125. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +6 -0
  126. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +5 -2
  127. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +243 -224
  128. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +178 -31
  129. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +30 -8
  130. package/node_modules/@llblab/pi-telegram/lib/bus.ts +110 -334
  131. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +29 -29
  132. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +5 -5
  133. package/node_modules/@llblab/pi-telegram/lib/commands.ts +15 -15
  134. package/node_modules/@llblab/pi-telegram/lib/config.ts +12 -17
  135. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +2 -8
  136. package/node_modules/@llblab/pi-telegram/lib/extension.ts +257 -72
  137. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +0 -22
  138. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +2 -2
  139. package/node_modules/@llblab/pi-telegram/lib/journal.ts +1182 -326
  140. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +2 -2
  141. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +1 -1
  142. package/node_modules/@llblab/pi-telegram/lib/locks.ts +402 -23
  143. package/node_modules/@llblab/pi-telegram/lib/logging.ts +151 -101
  144. package/node_modules/@llblab/pi-telegram/lib/media.ts +51 -9
  145. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +8 -8
  146. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +18 -18
  147. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +11 -0
  148. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +2 -2
  149. package/node_modules/@llblab/pi-telegram/lib/menu.ts +5 -1
  150. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +1 -1
  151. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +1 -10
  152. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +5 -24
  153. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +5 -5
  154. package/node_modules/@llblab/pi-telegram/lib/paths.ts +177 -17
  155. package/node_modules/@llblab/pi-telegram/lib/pi.ts +5 -2
  156. package/node_modules/@llblab/pi-telegram/lib/polling.ts +5 -5
  157. package/node_modules/@llblab/pi-telegram/lib/preview.ts +3 -3
  158. package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +1 -1
  159. package/node_modules/@llblab/pi-telegram/lib/queue.ts +67 -9
  160. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +104 -44
  161. package/node_modules/@llblab/pi-telegram/lib/replies.ts +1 -1
  162. package/node_modules/@llblab/pi-telegram/lib/routing.ts +1955 -375
  163. package/node_modules/@llblab/pi-telegram/lib/sections.ts +1 -1
  164. package/node_modules/@llblab/pi-telegram/lib/status.ts +156 -36
  165. package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
  166. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +48 -33
  167. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +24 -21
  168. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +267 -9
  169. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +3 -1
  170. package/node_modules/@llblab/pi-telegram/lib/threads.ts +1697 -488
  171. package/node_modules/@llblab/pi-telegram/lib/turns.ts +1 -1
  172. package/node_modules/@llblab/pi-telegram/lib/updates.ts +1072 -151
  173. package/node_modules/@llblab/pi-telegram/lib/wire.ts +28 -0
  174. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +97 -47
  175. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +147 -0
  176. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +188 -89
  177. package/node_modules/@llblab/pi-telegram/package.json +6 -6
  178. package/node_modules/@llblab/pi-telegram/scripts/audit-exports.mjs +100 -0
  179. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +80 -43
  180. package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +3 -3
  181. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +3 -3
  182. package/package.json +2 -2
@@ -4,12 +4,15 @@
4
4
  * Owns leader-only runtime orchestration: follower registration envelopes, follower API proxying,
5
5
  * leader activation hot-switching, local bus server startup, and stale follower pruning.
6
6
  */
7
+ import { isDeepStrictEqual } from "node:util";
7
8
  import * as Sync from "./sync.js";
8
9
  import { createTelegramThreadDisplayReconciler, resolveTelegramInitialWorkspaceDisplayName, resolveTelegramLiveWorkspaceBindingKeys, } from "./thread-display.js";
10
+ import * as ThreadNaming from "./thread-naming.js";
9
11
  import * as ThreadReconciler from "./thread-reconciler.js";
10
12
  import { getTelegramApiErrorRequestTarget, isTelegramApiCommitUnknownError, } from "./telegram-api.js";
11
13
  import * as Threads from "./threads.js";
12
- import { createTelegramBusLocalServer, createUnauthorizedBusAck, getTelegramBusEnvelopeTrafficClass, getTelegramBusProtocolCompatibility, hasTelegramBusCapability, isTelegramBusEnvelopeAuthorized, getTelegramBusFollowerSocketPath, sendTelegramBusLocalEnvelope, stripTelegramBusApiMetadata, TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION, TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF, TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT, TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME, TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE, TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT, TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT, } from "./bus.js";
14
+ import * as WorkspaceIdentity from "./workspace-identity.js";
15
+ import { createTelegramBusLocalServer, createUnauthorizedBusAck, getTelegramBusEnvelopeTrafficClass, getTelegramBusProtocolCompatibility, hasTelegramBusCapability, isTelegramBusEnvelopeAuthorized, getTelegramBusFollowerSocketPath, sendTelegramBusLocalEnvelope, stripTelegramBusApiMetadata, TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION, TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF, TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE, TELEGRAM_BUS_CAPABILITY_WORKSPACE_THREAD_RENAME, TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE, TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT, TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT, } from "./bus.js";
13
16
  import { getTelegramBusTransportRetryPolicy } from "./bus-transport.js";
14
17
  import { createTelegramWorkspaceOperationRuntime, createTelegramWorkspaceSlotRotation, } from "./workspace-retirement.js";
15
18
  import { createTelegramWorkspaceAdmissionOperationId, runWithTelegramWorkspaceAdmissionsAsync, } from "./workspace-admission.js";
@@ -39,10 +42,10 @@ export function createTelegramBusFollowerSessionReplacementAuthority(deps) {
39
42
  const ttlMs = deps.ttlMs ?? Threads.TELEGRAM_LEADER_SESSION_HANDOFF_TTL_MS;
40
43
  const assertCommon = (follower, intent) => {
41
44
  const cwd = follower.cwd
42
- ? Threads.normalizeTelegramWorkspacePath(follower.cwd)
45
+ ? WorkspaceIdentity.normalizeTelegramWorkspacePath(follower.cwd)
43
46
  : undefined;
44
47
  const sessionId = follower.sessionId
45
- ? Threads.normalizeTelegramSessionId(follower.sessionId)
48
+ ? WorkspaceIdentity.normalizeTelegramSessionId(follower.sessionId)
46
49
  : undefined;
47
50
  const nowMs = getNowMs();
48
51
  if (intent.continuity !== "workspace-thread" ||
@@ -324,7 +327,7 @@ export function createTelegramBusLeaderRuntimeAssembly(deps) {
324
327
  slot: record.slot,
325
328
  });
326
329
  if (!renamed) {
327
- throw new Error(Threads.getTelegramTopicThreadNameValidationError(threadName, record.slot) ?? "Telegram Workspace Thread name is already reserved.");
330
+ throw new Error(ThreadNaming.getTelegramTopicThreadNameValidationError(threadName, record.slot) ?? "Telegram Workspace Thread name is already reserved.");
328
331
  }
329
332
  await deps.topicTargetStore.persist();
330
333
  return renamed;
@@ -474,7 +477,7 @@ export function createTelegramBusLeaderRuntimeAssembly(deps) {
474
477
  throw new Error("Telegram Workspace Thread rename lost leader ownership.");
475
478
  }
476
479
  if (!renamed) {
477
- throw new Error(Threads.getTelegramTopicThreadNameValidationError(threadName, follower.slot) ?? "Telegram Workspace Thread name is already reserved.");
480
+ throw new Error(ThreadNaming.getTelegramTopicThreadNameValidationError(threadName, follower.slot) ?? "Telegram Workspace Thread name is already reserved.");
478
481
  }
479
482
  await deps.topicTargetStore.persist();
480
483
  return { threadName: renamed.manualThreadName ?? threadName };
@@ -559,8 +562,26 @@ export function createTelegramBusLeaderRuntimeAssembly(deps) {
559
562
  operationKind: "workspace.provision-follower",
560
563
  scopes: [{ kind: "profile" }],
561
564
  }, () => provisionFollowerTarget(registration, options)),
565
+ commitFollowerRegistration: (input, publish) => runWorkspaceOperation({
566
+ operationId: createTelegramWorkspaceAdmissionOperationId(),
567
+ operationKind: "workspace.publish-follower-registration", scopes: [{ kind: "profile" }],
568
+ }, async () => {
569
+ if (!input.target) {
570
+ publish();
571
+ return;
572
+ }
573
+ await deps.topicTargetStore.load();
574
+ const binding = input.registration.cwd && input.registration.sessionId
575
+ ? deps.topicTargetStore.getWorkspaceBindingByTarget(input.target, input.registration.sessionId) : undefined;
576
+ const bindingKey = binding && input.registration.cwd && binding.cwd === WorkspaceIdentity.normalizeTelegramWorkspacePath(input.registration.cwd)
577
+ ? binding.bindingKey : undefined;
578
+ if (input.target.threadId !== undefined && input.registration.cwd && input.registration.sessionId && !bindingKey)
579
+ throw new Error("Telegram follower Workspace binding changed before registration publication.");
580
+ deps.topicTargetStore.commitWorkspaceRestoreRegistration({ target: input.target, slot: input.slot, bindingKey }, publish);
581
+ }),
562
582
  getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
563
583
  getTelegramProfile: deps.getTelegramProfile,
584
+ getAllowedUserId: deps.getAllowedUserId,
564
585
  runWorkspaceAdmission,
565
586
  runWithWorkspaceCapacity,
566
587
  callApi: createTelegramBusLeaderApiProxy({
@@ -751,10 +772,10 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
751
772
  const nowMs = getNowMs();
752
773
  const carriedThreadName = workspaceBinding?.threadName ?? registration.threadName;
753
774
  const requestedThreadName = carriedThreadName &&
754
- Threads.isTelegramTopicThreadNameValidForSlot(carriedThreadName, registration.slot)
775
+ ThreadNaming.isTelegramTopicThreadNameValidForSlot(carriedThreadName, registration.slot)
755
776
  ? carriedThreadName
756
777
  : recoveryHint?.threadName &&
757
- Threads.isTelegramTopicThreadNameValidForSlot(recoveryHint.threadName, recoveryHint.slot)
778
+ ThreadNaming.isTelegramTopicThreadNameValidForSlot(recoveryHint.threadName, recoveryHint.slot)
758
779
  ? recoveryHint.threadName
759
780
  : undefined;
760
781
  const requestedSlot = workspaceIdentity?.slot ?? workspaceBinding?.slot ?? registration.slot ?? recoveryHint?.slot;
@@ -790,6 +811,11 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
790
811
  };
791
812
  };
792
813
  const runRegistration = async () => {
814
+ if (requestedTarget)
815
+ deps.topicTargetStore.assertWorkspaceRestoreRegistration({
816
+ target: requestedTarget, bindingKey: workspaceIdentity?.bindingKey,
817
+ slot: workspaceIdentity?.slot ?? workspaceBinding?.slot ?? registration.slot,
818
+ });
793
819
  const recoveryTarget = reconnectRecord?.target ?? recoverableTarget;
794
820
  if (recoveryTarget) {
795
821
  Threads.assertTelegramPendingTopicRecoveryAllowed(deps.topicTargetStore, recoveryTarget);
@@ -824,6 +850,10 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
824
850
  };
825
851
  };
826
852
  alignResultWithWorkspaceSlot();
853
+ const assertRestoreRegistration = () => deps.topicTargetStore.assertWorkspaceRestoreRegistration({
854
+ target: result.target, slot: result.record.slot, bindingKey: workspaceIdentity?.bindingKey,
855
+ });
856
+ assertRestoreRegistration();
827
857
  const crossSessionReuse = !!reconnectRecord &&
828
858
  reconnectRecord.instanceId !== registration.instanceId;
829
859
  if (reconnectRecord && !crossSessionReuse) {
@@ -864,6 +894,7 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
864
894
  : undefined;
865
895
  if (requiresVisibilityProbe && connectedAnnouncement) {
866
896
  try {
897
+ assertRestoreRegistration();
867
898
  await deps.callApi(exactSessionHandoff ? "sendChatAction" : "sendMessage", exactSessionHandoff
868
899
  ? {
869
900
  chat_id: connectedAnnouncement.target.chatId,
@@ -876,6 +907,7 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
876
907
  text: connectedAnnouncement.text,
877
908
  parse_mode: connectedAnnouncement.parseMode,
878
909
  });
910
+ assertRestoreRegistration();
879
911
  if (recoverableTarget || probeRequiredRecord) {
880
912
  const activatedRecord = deps.topicTargetStore.upsert({
881
913
  ...result.record,
@@ -929,6 +961,8 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
929
961
  connectedAnnouncement = undefined;
930
962
  }
931
963
  catch (error) {
964
+ // Restore protection failures are not evidence that a Telegram topic is missing.
965
+ assertRestoreRegistration();
932
966
  if (Threads.isTelegramTopicTargetStaleError(error)) {
933
967
  deps.topicTargetStore.markStaleByTarget(result.target, "deleted", error instanceof Error ? error.message : String(error));
934
968
  await deps.topicTargetStore.persist();
@@ -976,7 +1010,10 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
976
1010
  ? { threadName: result.record.threadName }
977
1011
  : {}),
978
1012
  slot: workspaceIdentity.slot,
979
- journalBindingKeys: [followerProfileKey],
1013
+ ...(workspaceIdentity.sessionId
1014
+ ? { journalSources: [{ sessionId: workspaceIdentity.sessionId, recipientBindingKey: followerProfileKey }],
1015
+ journalBindingKeys: [] }
1016
+ : { journalBindingKeys: [followerProfileKey] }),
980
1017
  journalBindingsComplete: true,
981
1018
  updatedAtMs: getNowMs(),
982
1019
  },
@@ -990,6 +1027,7 @@ export function createTelegramBusFollowerTargetProvisioner(deps) {
990
1027
  deps.topicTargetStore.markWorkspaceBindingActiveByTarget(result.target);
991
1028
  await deps.topicTargetStore.persist();
992
1029
  }
1030
+ assertRestoreRegistration();
993
1031
  deps.setSyncState(Sync.markTelegramSyncSliceFresh(deps.getSyncState(), "target-bindings", {
994
1032
  nowMs: getNowMs(),
995
1033
  action: "follower-register",
@@ -1183,7 +1221,7 @@ export function createTelegramBusLeaderTargetProvisioner(deps) {
1183
1221
  await deps.topicTargetStore.load();
1184
1222
  const cwd = deps.getCwd?.(ctx);
1185
1223
  const normalizedCwd = cwd
1186
- ? Threads.normalizeTelegramWorkspacePath(cwd)
1224
+ ? WorkspaceIdentity.normalizeTelegramWorkspacePath(cwd)
1187
1225
  : undefined;
1188
1226
  const profileKey = Threads.getTelegramThreadOwnerKey({
1189
1227
  kind: "leader",
@@ -1319,6 +1357,26 @@ export function createTelegramBusLeaderTargetProvisioner(deps) {
1319
1357
  }
1320
1358
  };
1321
1359
  }
1360
+ /** Strict copy of a follower-supplied attachment source; anything malformed falls back to the plain generated name. */
1361
+ function parseTelegramBusAttachmentSource(value) {
1362
+ if (!value || typeof value !== "object" || Array.isArray(value))
1363
+ return undefined;
1364
+ const raw = value, chat = raw.chat;
1365
+ const text = (field) => typeof field === "string" && field.length > 0 && field.length <= 256;
1366
+ if (!text(raw.kind) || !Number.isSafeInteger(raw.messageId) ||
1367
+ (raw.index !== undefined && !Number.isSafeInteger(raw.index)) ||
1368
+ (raw.userFileName !== undefined && !text(raw.userFileName)) || (raw.scope !== undefined && !text(raw.scope)) ||
1369
+ (chat !== undefined && (!chat || typeof chat !== "object" || Array.isArray(chat) || !Number.isSafeInteger(chat.id) ||
1370
+ (chat.type !== undefined && !text(chat.type)) || (chat.username !== undefined && !text(chat.username)))))
1371
+ return undefined;
1372
+ return { kind: raw.kind, messageId: raw.messageId,
1373
+ ...(raw.index !== undefined ? { index: raw.index } : {}),
1374
+ ...(raw.userFileName !== undefined ? { userFileName: raw.userFileName } : {}),
1375
+ ...(raw.scope !== undefined ? { scope: raw.scope } : {}),
1376
+ ...(chat ? { chat: { id: chat.id,
1377
+ ...(chat.type !== undefined ? { type: chat.type } : {}),
1378
+ ...(chat.username !== undefined ? { username: chat.username } : {}) } } : {}) };
1379
+ }
1322
1380
  export function createTelegramBusLeaderApiProxy(deps) {
1323
1381
  return async (method, args) => {
1324
1382
  if (method === "call") {
@@ -1342,7 +1400,9 @@ export function createTelegramBusLeaderApiProxy(deps) {
1342
1400
  }
1343
1401
  }
1344
1402
  if (method === "downloadFile") {
1345
- return deps.downloadFile(args[0], args[1]);
1403
+ // Follower IPC is a trust boundary: only an exact attachment-source shape reaches the file-name builder.
1404
+ const source = parseTelegramBusAttachmentSource(args[2]);
1405
+ return deps.downloadFile(args[0], args[1], ...(source ? [source] : []));
1346
1406
  }
1347
1407
  throw new Error(`Unsupported Telegram bus API method: ${method}`);
1348
1408
  };
@@ -1374,6 +1434,54 @@ function createTelegramBusFollowerMutationRunner() {
1374
1434
  export function createTelegramBusLeaderEnvelopeHandler(deps) {
1375
1435
  const getNowMs = deps.getNowMs ?? Date.now;
1376
1436
  const runFollowerMutation = deps.runFollowerMutation ?? createTelegramBusFollowerMutationRunner();
1437
+ const recipientObservations = new Map();
1438
+ const observeRestoreRecipient = (follower) => {
1439
+ if (!deps.authSecret || !deps.onWorkspaceRestoreRecipientObserved || !deps.getTelegramProfile ||
1440
+ !hasTelegramBusCapability(deps.protocolIdentity, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) ||
1441
+ !hasTelegramBusCapability(follower.protocol, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE))
1442
+ return;
1443
+ const instanceId = follower.instanceId;
1444
+ const report = (error) => {
1445
+ try {
1446
+ deps.recordRuntimeEvent?.("bus", error, { phase: "workspace-restore-recipient-observation", instanceId });
1447
+ }
1448
+ catch { /* Optional observation diagnostics must not break transport liveness. */ }
1449
+ };
1450
+ try {
1451
+ const epoch = deps.getCurrentLeaderEpoch?.(), operator = deps.getAllowedUserId?.();
1452
+ const runtimeGeneration = deps.getWorkspaceRestoreObservationGeneration?.();
1453
+ if (epoch === undefined || operator === undefined ||
1454
+ (deps.getWorkspaceRestoreObservationGeneration && runtimeGeneration === undefined))
1455
+ return;
1456
+ const snapshot = structuredClone(follower);
1457
+ const observed = { epoch, operator, profile: deps.getTelegramProfile(), runtimeGeneration,
1458
+ follower: { ...snapshot, lastHeartbeatMs: undefined, connectedAtMs: undefined, threadName: undefined } };
1459
+ if (isDeepStrictEqual(recipientObservations.get(instanceId), observed))
1460
+ return;
1461
+ recipientObservations.set(instanceId, observed);
1462
+ const isCurrent = () => {
1463
+ const live = deps.followerRegistry.get(instanceId);
1464
+ return recipientObservations.get(instanceId) === observed &&
1465
+ deps.getWorkspaceRestoreObservationGeneration?.() === observed.runtimeGeneration &&
1466
+ hasTelegramBusCapability(deps.protocolIdentity, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) &&
1467
+ deps.getCurrentLeaderEpoch?.() === observed.epoch && deps.getAllowedUserId?.() === observed.operator &&
1468
+ deps.getTelegramProfile?.() === observed.profile && !!live &&
1469
+ hasTelegramBusCapability(live.protocol, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) &&
1470
+ isDeepStrictEqual({ ...live, lastHeartbeatMs: undefined, connectedAtMs: undefined, threadName: undefined }, observed.follower);
1471
+ };
1472
+ // A heartbeat is a hint, not completion proof. Never await the listener before acknowledging it.
1473
+ void Promise.resolve().then(() => {
1474
+ if (isCurrent())
1475
+ return deps.onWorkspaceRestoreRecipientObserved?.(structuredClone(snapshot), isCurrent);
1476
+ }).catch(report).finally(() => {
1477
+ if (recipientObservations.get(instanceId) === observed)
1478
+ recipientObservations.delete(instanceId);
1479
+ });
1480
+ }
1481
+ catch (error) {
1482
+ report(error);
1483
+ }
1484
+ };
1377
1485
  const handleAgentRequest = async (envelope) => {
1378
1486
  const follower = deps.followerRegistry.get(envelope.instanceId);
1379
1487
  if (!follower) {
@@ -1636,9 +1744,16 @@ export function createTelegramBusLeaderEnvelopeHandler(deps) {
1636
1744
  const registrationOperation = () => runFollowerMutation(envelope.registration, async () => {
1637
1745
  try {
1638
1746
  const leaderEpoch = deps.getCurrentLeaderEpoch?.();
1747
+ const telegramProfile = deps.getTelegramProfile?.();
1748
+ const operatorUserId = deps.getAllowedUserId?.();
1749
+ const registrationCurrent = () => (!deps.getCurrentLeaderEpoch || deps.getCurrentLeaderEpoch() === leaderEpoch) &&
1750
+ (!deps.getTelegramProfile || deps.getTelegramProfile() === telegramProfile) &&
1751
+ (!deps.getAllowedUserId || deps.getAllowedUserId() === operatorUserId);
1639
1752
  if (deps.getCurrentLeaderEpoch && leaderEpoch === undefined) {
1640
1753
  throw new Error("Telegram follower registration requires leader ownership.");
1641
1754
  }
1755
+ if (hasTelegramBusCapability(deps.protocolIdentity, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) && !deps.commitFollowerRegistration)
1756
+ throw new Error("Workspace Restore registration publication protection is unavailable.");
1642
1757
  const target = await deps.provisionFollowerTarget?.(envelope.registration, { existingWorkspaceBindingOnly: restoringWorkspace });
1643
1758
  if (restoringWorkspace && !target) {
1644
1759
  return {
@@ -1650,10 +1765,8 @@ export function createTelegramBusLeaderEnvelopeHandler(deps) {
1650
1765
  message: "No remembered Telegram Workspace Thread is available.",
1651
1766
  };
1652
1767
  }
1653
- if (deps.getCurrentLeaderEpoch &&
1654
- deps.getCurrentLeaderEpoch() !== leaderEpoch) {
1768
+ if (!registrationCurrent())
1655
1769
  throw new Error("Telegram follower registration lost leader ownership.");
1656
- }
1657
1770
  if (!displayCompatible()) {
1658
1771
  throw new Error("Thread display mode changed; update or restart this follower before connecting.");
1659
1772
  }
@@ -1664,19 +1777,50 @@ export function createTelegramBusLeaderEnvelopeHandler(deps) {
1664
1777
  (!registeredSlot || !/^[A-Z]$/u.test(registeredSlot))) {
1665
1778
  throw new Error("Telegram Thread slot authority is unavailable.");
1666
1779
  }
1667
- deps.followerRegistry.register({
1668
- ...envelope.registration,
1669
- connectedAtMs: getNowMs(),
1670
- target: registeredTarget,
1671
- ...(registeredSlot ? { slot: registeredSlot } : {}),
1672
- ...((target?.threadName ?? envelope.registration.threadName)
1673
- ? {
1674
- threadName: target?.threadName ?? envelope.registration.threadName,
1675
- }
1676
- : {}),
1677
- });
1780
+ let publicationOpen = true;
1781
+ let published;
1782
+ const publish = () => {
1783
+ if (!publicationOpen)
1784
+ throw new Error("Telegram follower registration publication is no longer available.");
1785
+ publicationOpen = false;
1786
+ if (!registrationCurrent())
1787
+ throw new Error("Telegram follower registration lost leader ownership.");
1788
+ if (!displayCompatible())
1789
+ throw new Error("Thread display mode changed; update or restart this follower before connecting.");
1790
+ published = deps.followerRegistry.register({
1791
+ ...envelope.registration,
1792
+ connectedAtMs: getNowMs(),
1793
+ target: registeredTarget,
1794
+ ...(registeredSlot ? { slot: registeredSlot } : {}),
1795
+ ...((target?.threadName ?? envelope.registration.threadName)
1796
+ ? { threadName: target?.threadName ?? envelope.registration.threadName } : {}),
1797
+ });
1798
+ };
1799
+ try {
1800
+ if (deps.commitFollowerRegistration)
1801
+ await deps.commitFollowerRegistration({
1802
+ registration: envelope.registration, target: registeredTarget, slot: registeredSlot,
1803
+ }, publish);
1804
+ else
1805
+ publish();
1806
+ }
1807
+ finally {
1808
+ publicationOpen = false;
1809
+ }
1810
+ if (!published)
1811
+ throw new Error("Telegram follower registration publication was not confirmed.");
1812
+ if (!registrationCurrent())
1813
+ throw new Error("Telegram follower registration lost leader ownership.");
1814
+ if (!displayCompatible())
1815
+ throw new Error("Thread display mode changed; update or restart this follower before connecting.");
1678
1816
  const follower = deps.followerRegistry.get(envelope.registration.instanceId);
1679
- const displayTitle = follower ? deps.getFollowerDisplayTitle?.(follower) : undefined;
1817
+ if (!follower)
1818
+ throw new Error("Telegram follower registration changed before acknowledgement.");
1819
+ const { lastHeartbeatMs: _publishedHeartbeat, connectedAtMs: _publishedAt, threadName: _publishedName, ...publishedIdentity } = published;
1820
+ const { lastHeartbeatMs: _heartbeat, connectedAtMs: _connectedAt, threadName: _threadName, ...identity } = follower;
1821
+ if (!isDeepStrictEqual(publishedIdentity, identity))
1822
+ throw new Error("Telegram follower registration changed before acknowledgement.");
1823
+ const displayTitle = deps.getFollowerDisplayTitle?.(follower);
1680
1824
  deps.onFollowerRegistered?.();
1681
1825
  return {
1682
1826
  kind: "bus.ack",
@@ -1997,6 +2141,8 @@ export function createTelegramBusLeaderEnvelopeHandler(deps) {
1997
2141
  };
1998
2142
  }
1999
2143
  const follower = deps.followerRegistry.heartbeat(envelope.instanceId, getNowMs());
2144
+ if (follower)
2145
+ observeRestoreRecipient(follower);
2000
2146
  const displayTitle = follower ? deps.getFollowerDisplayTitle?.(follower) : undefined;
2001
2147
  return follower
2002
2148
  ? {
@@ -2406,6 +2552,10 @@ export function createTelegramBusLeaderRuntime(deps) {
2406
2552
  const expectedGeneration = pruneGeneration;
2407
2553
  let tracked;
2408
2554
  tracked = pruneFollowers(expectedGeneration)
2555
+ .then(() => {
2556
+ if (pruneGeneration === expectedGeneration)
2557
+ deps.afterFollowerPrune?.();
2558
+ })
2409
2559
  .catch((error) => {
2410
2560
  if (pruneGeneration === expectedGeneration) {
2411
2561
  recordPruneEvent(error, { phase: "follower-prune-owner" });
@@ -2441,6 +2591,7 @@ export function createTelegramBusLeaderRuntime(deps) {
2441
2591
  forgetPreservation(pendingPreservations.get(registration.instanceId));
2442
2592
  return deps.provisionFollowerTarget(registration, options);
2443
2593
  } : undefined,
2594
+ commitFollowerRegistration: deps.commitFollowerRegistration,
2444
2595
  onFollowerDisconnected: deps.onFollowerDisconnected,
2445
2596
  renameFollowerThread: deps.renameFollowerThread,
2446
2597
  resetFollowerThreadName: deps.resetFollowerThreadName,
@@ -2448,9 +2599,14 @@ export function createTelegramBusLeaderRuntime(deps) {
2448
2599
  settleFollowerSessionReplacement: deps.settleFollowerSessionReplacement,
2449
2600
  getFollowerDisplayTitle: deps.getFollowerDisplayTitle,
2450
2601
  onFollowerRegistered: deps.onFollowerRegistered,
2602
+ onWorkspaceRestoreRecipientObserved: deps.onWorkspaceRestoreRecipientObserved,
2603
+ getWorkspaceRestoreObservationGeneration: () => pruneInterval ? pruneGeneration : undefined,
2604
+ recordRuntimeEvent: deps.recordRuntimeEvent,
2451
2605
  applyThreadDisplayMode: deps.applyThreadDisplayMode,
2452
2606
  getThreadDisplayMode: deps.getThreadDisplayMode,
2453
2607
  getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
2608
+ getTelegramProfile: deps.getTelegramProfile,
2609
+ getAllowedUserId: deps.getAllowedUserId,
2454
2610
  runFollowerMutation,
2455
2611
  runWorkspaceAdmission: deps.runWorkspaceAdmission,
2456
2612
  runWithWorkspaceCapacity: deps.runWithWorkspaceCapacity,
@@ -3,6 +3,8 @@
3
3
  * Zones: multi-instance bus, IPC transport, Windows named pipes, Unix sockets
4
4
  * Owns endpoint derivation, transport-kind detection, retry/error classification, and small timing policy.
5
5
  */
6
+ export type TelegramBusEndpointLayout = "consolidated";
7
+ export declare const TELEGRAM_BUS_MAX_DIRECT_UNIX_ENDPOINT_BYTES = 80;
6
8
  export type TelegramBusTransportKind = "pipe" | "socket";
7
9
  export type TelegramBusTransportEventRecorder = (phase: string, details: Record<string, unknown>) => void;
8
10
  export type TelegramBusTransportEndpointDiagnostics = Record<string, unknown> & {
@@ -19,7 +21,6 @@ export interface TelegramBusTransportRetryPolicyOverrides {
19
21
  }
20
22
  export type TelegramBusTransportOperation = "registration" | "operation";
21
23
  export declare const TELEGRAM_BUS_REGISTRATION_RETRY: TelegramBusTransportRetryPolicy;
22
- export declare const TELEGRAM_BUS_OPERATION_RETRY: TelegramBusTransportRetryPolicy;
23
24
  export declare function getTelegramBusPipePath(input: {
24
25
  agentDir: string;
25
26
  scope: string;
@@ -36,12 +37,14 @@ export declare function getTelegramBusLeaderEndpoint(input: {
36
37
  agentDir: string;
37
38
  platform: NodeJS.Platform | string;
38
39
  profileName?: string;
40
+ layout?: TelegramBusEndpointLayout;
39
41
  }): string;
40
42
  export declare function getTelegramBusFollowerEndpoint(input: {
41
43
  agentDir: string;
42
44
  platform: NodeJS.Platform | string;
43
45
  instanceId: string;
44
46
  profileName?: string;
47
+ layout?: TelegramBusEndpointLayout;
45
48
  }): string;
46
49
  export interface TelegramBusTransportErrorInfo {
47
50
  message: string;
@@ -5,12 +5,20 @@
5
5
  */
6
6
  import { createHash } from "node:crypto";
7
7
  import { createConnection } from "node:net";
8
- import { join, resolve } from "node:path";
8
+ import { isAbsolute, join, resolve } from "node:path";
9
+ import { resolveTelegramTempDir, resolveTelegramRuntimeDir } from "./paths.js";
10
+ export const TELEGRAM_BUS_MAX_DIRECT_UNIX_ENDPOINT_BYTES = 80;
11
+ function requireConsolidatedUnixEndpoint(path) {
12
+ if (!isAbsolute(path) || resolve(path) !== path)
13
+ throw new Error("Consolidated Telegram IPC requires an exact absolute runtime path.");
14
+ // Over-budget logical paths keep their identity; socket resolution applies the bounded private external fallback.
15
+ return path;
16
+ }
9
17
  export const TELEGRAM_BUS_REGISTRATION_RETRY = {
10
18
  attempts: 10,
11
19
  delayMs: 150,
12
20
  };
13
- export const TELEGRAM_BUS_OPERATION_RETRY = {
21
+ const TELEGRAM_BUS_OPERATION_RETRY = {
14
22
  attempts: 3,
15
23
  delayMs: 100,
16
24
  };
@@ -51,6 +59,14 @@ function normalizeTelegramBusEndpointScope(value) {
51
59
  return value.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 80);
52
60
  }
53
61
  export function getTelegramBusLeaderEndpoint(input) {
62
+ if (input.layout !== undefined && input.layout !== "consolidated")
63
+ throw new Error("Telegram IPC layout is invalid.");
64
+ if (input.layout === "consolidated") {
65
+ const scope = createHash("sha256").update(JSON.stringify([input.profileName ?? "default"])).digest("hex").slice(0, 16);
66
+ return input.platform === "win32"
67
+ ? getTelegramBusPipePath({ agentDir: input.agentDir, scope: `bus-${scope}` })
68
+ : requireConsolidatedUnixEndpoint(join(resolveTelegramRuntimeDir(input.agentDir), `bus.${scope}.sock`));
69
+ }
54
70
  const profileScope = input.profileName
55
71
  ? normalizeTelegramBusEndpointScope(input.profileName)
56
72
  : undefined;
@@ -59,9 +75,17 @@ export function getTelegramBusLeaderEndpoint(input) {
59
75
  agentDir: input.agentDir,
60
76
  scope: profileScope ? `bus-${profileScope}` : "bus",
61
77
  })
62
- : join(input.agentDir, "tmp", "telegram", profileScope ? `bus.${profileScope}.sock` : "bus.sock");
78
+ : join(resolveTelegramTempDir(input.agentDir), profileScope ? `bus.${profileScope}.sock` : "bus.sock");
63
79
  }
64
80
  export function getTelegramBusFollowerEndpoint(input) {
81
+ if (input.layout !== undefined && input.layout !== "consolidated")
82
+ throw new Error("Telegram IPC layout is invalid.");
83
+ if (input.layout === "consolidated") {
84
+ const scope = createHash("sha256").update(JSON.stringify([input.profileName ?? "default", input.instanceId])).digest("hex").slice(0, 16);
85
+ return input.platform === "win32"
86
+ ? getTelegramBusPipePath({ agentDir: input.agentDir, scope: `follower-${scope}` })
87
+ : requireConsolidatedUnixEndpoint(join(resolveTelegramRuntimeDir(input.agentDir), `f.${scope}.sock`));
88
+ }
65
89
  const instanceScope = normalizeTelegramBusEndpointScope(input.instanceId);
66
90
  const profileScope = input.profileName
67
91
  ? normalizeTelegramBusEndpointScope(input.profileName)
@@ -73,7 +97,7 @@ export function getTelegramBusFollowerEndpoint(input) {
73
97
  ? `follower-${profileScope}-${instanceScope}`
74
98
  : `follower-${instanceScope}`,
75
99
  })
76
- : join(input.agentDir, "tmp", "telegram", "followers", ...(profileScope ? [profileScope] : []), `${instanceScope}.sock`);
100
+ : join(resolveTelegramTempDir(input.agentDir), "followers", ...(profileScope ? [profileScope] : []), `${instanceScope}.sock`);
77
101
  }
78
102
  export function classifyTelegramBusTransportError(error) {
79
103
  const message = error instanceof Error ? error.message : String(error);