@llblab/pi-kit 0.27.5 → 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 (194) hide show
  1. package/BACKLOG.md +3 -1
  2. package/CHANGELOG.md +9 -0
  3. package/README.md +4 -4
  4. package/node_modules/@llblab/pi-state-flow/AGENTS.md +1 -1
  5. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +6 -2
  6. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +4 -1
  7. package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +4 -3
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +26 -13
  9. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  10. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -1
  11. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +2 -2
  12. package/node_modules/@llblab/pi-state-flow/docs/usage.md +2 -1
  13. package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +4 -3
  14. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +25 -12
  15. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  16. package/node_modules/@llblab/pi-telegram/AGENTS.md +10 -8
  17. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -9
  18. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
  19. package/node_modules/@llblab/pi-telegram/LICENSE +21 -0
  20. package/node_modules/@llblab/pi-telegram/README.md +15 -5
  21. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  22. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +5 -7
  23. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +5 -1
  24. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +6 -1
  25. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +4 -2
  26. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +54 -56
  27. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +206 -139
  28. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +21 -2
  29. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +181 -25
  30. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +4 -1
  31. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +28 -4
  32. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +52 -56
  33. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +78 -246
  34. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +1 -3
  35. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +23 -26
  36. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +0 -5
  37. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +5 -5
  38. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +0 -23
  39. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +15 -15
  40. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +0 -17
  41. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +12 -14
  42. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +0 -4
  43. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +2 -5
  44. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +255 -61
  45. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -19
  46. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +0 -2
  47. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +2 -2
  48. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +177 -16
  49. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +1115 -249
  50. package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +0 -2
  51. package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +2 -2
  52. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +1 -1
  53. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +100 -12
  54. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +380 -14
  55. package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +30 -12
  56. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +129 -72
  57. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +28 -5
  58. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +26 -8
  59. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +0 -11
  60. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +8 -8
  61. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +0 -18
  62. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +18 -18
  63. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +4 -4
  64. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +12 -7
  65. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +1 -1
  66. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +1 -1
  67. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +1 -0
  68. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -3
  69. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +0 -1
  70. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +1 -1
  71. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +1 -8
  72. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +1 -6
  73. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +4 -21
  74. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +0 -8
  75. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +5 -5
  76. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +50 -7
  77. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +155 -17
  78. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +1 -0
  79. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +2 -1
  80. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +0 -5
  81. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -5
  82. package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +0 -3
  83. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +3 -3
  84. package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +0 -1
  85. package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +1 -1
  86. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +3 -15
  87. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +69 -8
  88. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +29 -9
  89. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +104 -33
  90. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +0 -4
  91. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -1
  92. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +73 -8
  93. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2206 -307
  94. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +0 -2
  95. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +1 -1
  96. package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +44 -9
  97. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +149 -27
  98. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +0 -5
  99. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +5 -4
  100. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +11 -3
  101. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +34 -23
  102. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +1 -0
  103. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +18 -21
  104. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +32 -5
  105. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +191 -7
  106. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +2 -0
  107. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +1 -1
  108. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +291 -59
  109. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +1647 -369
  110. package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +0 -1
  111. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -1
  112. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +184 -28
  113. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +1032 -114
  114. package/node_modules/@llblab/pi-telegram/dist/lib/wire.d.ts +12 -0
  115. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +21 -0
  116. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +25 -17
  117. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +95 -20
  118. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.d.ts +20 -0
  119. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +103 -0
  120. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +43 -8
  121. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +148 -52
  122. package/node_modules/@llblab/pi-telegram/dist/package.json +6 -6
  123. package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +3 -3
  124. package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
  125. package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
  126. package/node_modules/@llblab/pi-telegram/docs/activity.md +1 -1
  127. package/node_modules/@llblab/pi-telegram/docs/architecture.md +152 -34
  128. package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
  129. package/node_modules/@llblab/pi-telegram/docs/delivery.md +1 -1
  130. package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +2 -2
  131. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  132. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +125 -19
  133. package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -1
  134. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +22 -8
  135. package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
  136. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +5 -9
  137. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +6 -0
  138. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +5 -2
  139. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +243 -224
  140. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +178 -31
  141. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +30 -8
  142. package/node_modules/@llblab/pi-telegram/lib/bus.ts +110 -334
  143. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +29 -29
  144. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +5 -5
  145. package/node_modules/@llblab/pi-telegram/lib/commands.ts +15 -15
  146. package/node_modules/@llblab/pi-telegram/lib/config.ts +12 -17
  147. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +2 -8
  148. package/node_modules/@llblab/pi-telegram/lib/extension.ts +257 -72
  149. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +0 -22
  150. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +2 -2
  151. package/node_modules/@llblab/pi-telegram/lib/journal.ts +1182 -326
  152. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +2 -2
  153. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +1 -1
  154. package/node_modules/@llblab/pi-telegram/lib/locks.ts +402 -23
  155. package/node_modules/@llblab/pi-telegram/lib/logging.ts +151 -101
  156. package/node_modules/@llblab/pi-telegram/lib/media.ts +51 -9
  157. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +8 -8
  158. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +18 -18
  159. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +11 -0
  160. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +2 -2
  161. package/node_modules/@llblab/pi-telegram/lib/menu.ts +5 -1
  162. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +1 -1
  163. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +1 -10
  164. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +5 -24
  165. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +5 -5
  166. package/node_modules/@llblab/pi-telegram/lib/paths.ts +177 -17
  167. package/node_modules/@llblab/pi-telegram/lib/pi.ts +5 -2
  168. package/node_modules/@llblab/pi-telegram/lib/polling.ts +5 -5
  169. package/node_modules/@llblab/pi-telegram/lib/preview.ts +3 -3
  170. package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +1 -1
  171. package/node_modules/@llblab/pi-telegram/lib/queue.ts +67 -9
  172. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +104 -44
  173. package/node_modules/@llblab/pi-telegram/lib/replies.ts +1 -1
  174. package/node_modules/@llblab/pi-telegram/lib/routing.ts +1955 -375
  175. package/node_modules/@llblab/pi-telegram/lib/sections.ts +1 -1
  176. package/node_modules/@llblab/pi-telegram/lib/status.ts +156 -36
  177. package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
  178. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +48 -33
  179. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +24 -21
  180. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +267 -9
  181. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +3 -1
  182. package/node_modules/@llblab/pi-telegram/lib/threads.ts +1697 -488
  183. package/node_modules/@llblab/pi-telegram/lib/turns.ts +1 -1
  184. package/node_modules/@llblab/pi-telegram/lib/updates.ts +1072 -151
  185. package/node_modules/@llblab/pi-telegram/lib/wire.ts +28 -0
  186. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +97 -47
  187. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +147 -0
  188. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +188 -89
  189. package/node_modules/@llblab/pi-telegram/package.json +6 -6
  190. package/node_modules/@llblab/pi-telegram/scripts/audit-exports.mjs +100 -0
  191. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +80 -43
  192. package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +3 -3
  193. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +3 -3
  194. package/package.json +3 -3
@@ -37,6 +37,8 @@ import {
37
37
  getTelegramBusLeaderEndpoint,
38
38
  getTelegramBusPipePath,
39
39
  getTelegramBusTransportRetryPolicy,
40
+ type TelegramBusEndpointLayout,
41
+ TELEGRAM_BUS_MAX_DIRECT_UNIX_ENDPOINT_BYTES,
40
42
  isTelegramBusPipePath,
41
43
  isRetryableTelegramBusTransportError,
42
44
  probeTelegramBusEndpoint,
@@ -44,20 +46,21 @@ import {
44
46
  type TelegramBusTransportRetryPolicy,
45
47
  } from "./bus-transport.ts";
46
48
  import {
47
- TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS,
49
+ parseTelegramQueueHandoffPayload,
48
50
  TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES,
49
51
  type TelegramQueueHandoffPayload,
50
52
  } from "./queue.ts";
51
53
  import type { TelegramTarget } from "./target.ts";
52
54
  import type { TelegramThreadDisplayMode } from "./config.ts";
53
55
  import { isProcessAlive } from "./locks.ts";
56
+ import { isWireRecord as isRecord } from "./wire.ts";
54
57
  import { resolveAgentDir } from "./paths.ts";
55
58
  import {
56
59
  normalizeTelegramSessionReplacementIntent,
57
60
  type TelegramSessionReplacementIntent,
58
61
  } from "./threads.ts";
59
62
 
60
- export interface TelegramBusProcessRuntime {
63
+ interface TelegramBusProcessRuntime {
61
64
  instanceId: string;
62
65
  processId: number;
63
66
  processBirthId: string;
@@ -66,19 +69,19 @@ export interface TelegramBusProcessRuntime {
66
69
  getFollowerSocketPath: () => string;
67
70
  }
68
71
 
69
- export interface TelegramProcessBirthIdentityOptions {
72
+ interface TelegramProcessBirthIdentityOptions {
70
73
  platform?: NodeJS.Platform;
71
74
  readProcStat?: (pid: number) => string;
72
75
  readDarwinProcessStart?: (pid: number) => string;
73
76
  }
74
77
 
75
- export type TelegramProcessBirthProof =
78
+ type TelegramProcessBirthProof =
76
79
  | { status: "proven"; identity: string }
77
80
  | { status: "unverifiable" };
78
81
 
79
82
  export type TelegramProcessLiveness = "alive" | "dead" | "unverifiable";
80
83
 
81
- export interface TelegramProcessLivenessOptions
84
+ interface TelegramProcessLivenessOptions
82
85
  extends TelegramProcessBirthIdentityOptions {
83
86
  isProcessAlive?: (pid: number) => boolean;
84
87
  }
@@ -94,7 +97,7 @@ function readDarwinProcessStart(pid: number): string {
94
97
  ).trim();
95
98
  }
96
99
 
97
- export function getTelegramProcessBirthProof(
100
+ function getTelegramProcessBirthProof(
98
101
  pid: number,
99
102
  options: TelegramProcessBirthIdentityOptions = {},
100
103
  ): TelegramProcessBirthProof {
@@ -175,12 +178,14 @@ export function getTelegramProcessBirthIdentityLiveness(
175
178
 
176
179
  export function createCurrentTelegramBusProcessRuntime(input: {
177
180
  getActiveProfileName: () => string | undefined;
181
+ endpointLayout?: TelegramBusEndpointLayout;
178
182
  pid?: number;
179
183
  parentPid?: number;
180
184
  createdAtMs?: number;
181
185
  }): TelegramBusProcessRuntime {
182
186
  return createTelegramBusProcessRuntime({
183
187
  getActiveProfileName: input.getActiveProfileName,
188
+ endpointLayout: input.endpointLayout,
184
189
  pid: input.pid ?? process.pid,
185
190
  parentPid: input.parentPid ?? process.ppid,
186
191
  createdAtMs: input.createdAtMs ?? Date.now(),
@@ -189,11 +194,13 @@ export function createCurrentTelegramBusProcessRuntime(input: {
189
194
 
190
195
  export function createTelegramBusProcessRuntime(input: {
191
196
  getActiveProfileName: () => string | undefined;
197
+ endpointLayout?: TelegramBusEndpointLayout;
192
198
  pid: number;
193
199
  parentPid: number;
194
200
  parentProcessIdentity?: string;
195
201
  createdAtMs: number;
196
202
  }): TelegramBusProcessRuntime {
203
+ const getActiveProfileName = input.getActiveProfileName, endpointLayout = input.endpointLayout;
197
204
  const instanceId = `${input.pid}:${input.createdAtMs}`;
198
205
  const ownerPid = input.parentPid || input.pid;
199
206
  const manualFollowerOwnerId =
@@ -208,14 +215,16 @@ export function createTelegramBusProcessRuntime(input: {
208
215
  getTelegramBusSocketPath(
209
216
  undefined,
210
217
  undefined,
211
- input.getActiveProfileName(),
218
+ getActiveProfileName(),
219
+ endpointLayout,
212
220
  ),
213
221
  getFollowerSocketPath: () =>
214
222
  getTelegramBusFollowerSocketPath(
215
223
  instanceId,
216
224
  undefined,
217
225
  undefined,
218
- input.getActiveProfileName(),
226
+ getActiveProfileName(),
227
+ endpointLayout,
219
228
  ),
220
229
  };
221
230
  }
@@ -224,7 +233,7 @@ export function createTelegramBusAuthSecret(): string {
224
233
  return randomBytes(32).toString("base64url");
225
234
  }
226
235
 
227
- export const TELEGRAM_BUS_PROTOCOL_VERSION = 2 as const;
236
+ const TELEGRAM_BUS_PROTOCOL_VERSION = 2 as const;
228
237
  export const TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION =
229
238
  "durable-follower-admission-v1" as const;
230
239
  export const TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF =
@@ -240,6 +249,15 @@ export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT =
240
249
  "workspace-follower-auto-connect-v1" as const;
241
250
  export const TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT =
242
251
  "session-replacement-intent-v1" as const;
252
+ export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE = "workspace-restore-v1" as const;
253
+
254
+ export interface TelegramBusWorkspaceRestoreObservation {
255
+ operationId: string;
256
+ recipient: { kind: "follower"; instanceId: string; sessionId: string; generation: string };
257
+ target: TelegramTarget & { threadId: number };
258
+ slot: string;
259
+ ready: boolean;
260
+ }
243
261
 
244
262
  export interface TelegramBusProtocolIdentity {
245
263
  protocolVersion: number;
@@ -247,7 +265,7 @@ export interface TelegramBusProtocolIdentity {
247
265
  capabilities: string[];
248
266
  }
249
267
 
250
- export interface TelegramBusProtocolCompatibility {
268
+ interface TelegramBusProtocolCompatibility {
251
269
  compatible: boolean;
252
270
  reason?: "missing-identity" | "version-mismatch" | "missing-capability";
253
271
  missingCapabilities: string[];
@@ -352,8 +370,9 @@ export function getTelegramBusSocketPath(
352
370
  agentDir = resolveAgentDir(),
353
371
  platform = getPlatform(),
354
372
  profileName?: string,
373
+ layout?: TelegramBusEndpointLayout,
355
374
  ): string {
356
- return getTelegramBusLeaderEndpoint({ agentDir, platform, profileName });
375
+ return getTelegramBusLeaderEndpoint({ agentDir, platform, profileName, layout });
357
376
  }
358
377
 
359
378
  export function getTelegramBusFollowerSocketPath(
@@ -361,12 +380,14 @@ export function getTelegramBusFollowerSocketPath(
361
380
  agentDir = resolveAgentDir(),
362
381
  platform = getPlatform(),
363
382
  profileName?: string,
383
+ layout?: TelegramBusEndpointLayout,
364
384
  ): string {
365
385
  return getTelegramBusFollowerEndpoint({
366
386
  agentDir,
367
387
  platform,
368
388
  instanceId,
369
389
  profileName,
390
+ layout,
370
391
  });
371
392
  }
372
393
 
@@ -447,7 +468,7 @@ export function markTelegramBusAggregateDelivery<
447
468
  };
448
469
  }
449
470
 
450
- export function isTelegramBusAggregateDelivery(body: unknown): boolean {
471
+ function isTelegramBusAggregateDelivery(body: unknown): boolean {
451
472
  return Boolean(
452
473
  body &&
453
474
  typeof body === "object" &&
@@ -466,7 +487,7 @@ export function markTelegramBusCrossTargetDelivery<
466
487
  };
467
488
  }
468
489
 
469
- export function isTelegramBusCrossTargetDelivery(body: unknown): boolean {
490
+ function isTelegramBusCrossTargetDelivery(body: unknown): boolean {
470
491
  return Boolean(
471
492
  body &&
472
493
  typeof body === "object" &&
@@ -628,7 +649,7 @@ export function isTelegramFollowerApiCallAllowed(input: {
628
649
  return false;
629
650
  }
630
651
 
631
- export interface TelegramFollowerApiCallAuthorizationInput {
652
+ interface TelegramFollowerApiCallAuthorizationInput {
632
653
  follower: TelegramBusFollowerView;
633
654
  method: string;
634
655
  args: unknown[];
@@ -677,7 +698,7 @@ export interface TelegramBusFollowerDeliveryIdentity {
677
698
  };
678
699
  }
679
700
 
680
- export type TelegramBusForeignUpdateFailureClass =
701
+ type TelegramBusForeignUpdateFailureClass =
681
702
  | "source-update-identity-missing"
682
703
  | "recipient-binding-missing"
683
704
  | "recipient-generation-missing"
@@ -889,13 +910,12 @@ export type TelegramBusEnvelope = (
889
910
  sentAtMs: number;
890
911
  }
891
912
  | {
892
- kind: "leader.replaceFollowerTarget";
913
+ kind: "leader.workspaceRestore";
893
914
  requestId: string;
894
915
  recipientInstanceId: string;
895
- recipientRegistrationGeneration?: string;
896
- target: TelegramTarget & { threadId: number };
897
- oldTarget?: TelegramTarget & { threadId: number };
898
- reason: "thread-restore";
916
+ recipientRegistrationGeneration: string;
917
+ operationId: string;
918
+ mode: "apply" | "inspect";
899
919
  sentAtMs: number;
900
920
  }
901
921
  | {
@@ -976,7 +996,7 @@ export type TelegramBusEnvelope = (
976
996
  }
977
997
  ) & { auth?: string };
978
998
 
979
- export type TelegramBusEnvelopeTrafficClass =
999
+ type TelegramBusEnvelopeTrafficClass =
980
1000
  | "bootstrap"
981
1001
  | "generation-fenced"
982
1002
  | "response";
@@ -1114,8 +1134,16 @@ export function parseTelegramBusEnvelope(
1114
1134
  "leader.forwardEditedMessage",
1115
1135
  );
1116
1136
  break;
1117
- case "leader.replaceFollowerTarget":
1118
- envelope = parseReplaceFollowerTargetEnvelope(value, requestId);
1137
+ case "leader.workspaceRestore":
1138
+ if (typeof value.recipientInstanceId === "string" && value.recipientInstanceId &&
1139
+ typeof value.recipientRegistrationGeneration === "string" && value.recipientRegistrationGeneration &&
1140
+ typeof value.operationId === "string" && value.operationId.length > 0 && value.operationId.length <= 128 &&
1141
+ (value.mode === "apply" || value.mode === "inspect") &&
1142
+ typeof value.sentAtMs === "number" && Number.isFinite(value.sentAtMs)) {
1143
+ envelope = { kind: "leader.workspaceRestore", requestId, recipientInstanceId: value.recipientInstanceId,
1144
+ recipientRegistrationGeneration: value.recipientRegistrationGeneration, operationId: value.operationId,
1145
+ mode: value.mode, sentAtMs: value.sentAtMs };
1146
+ }
1119
1147
  break;
1120
1148
  case "leader.offerQueueHandoff":
1121
1149
  envelope = parseQueueHandoffEnvelope(
@@ -1151,7 +1179,7 @@ export function parseTelegramBusEnvelope(
1151
1179
  return envelope;
1152
1180
  }
1153
1181
 
1154
- export interface TelegramBusLocalServer {
1182
+ interface TelegramBusLocalServer {
1155
1183
  start: () => Promise<void>;
1156
1184
  stop: () => Promise<void>;
1157
1185
  ensureEndpoint: () => Promise<boolean>;
@@ -1171,8 +1199,6 @@ function getActiveTelegramBusLocalServers(): Map<string, TelegramBusLocalServer>
1171
1199
 
1172
1200
  export type TelegramBusSocketPathSource = string | (() => string);
1173
1201
 
1174
- const TELEGRAM_BUS_MAX_DIRECT_UNIX_ENDPOINT_BYTES = 80;
1175
-
1176
1202
  export function resolveTelegramBusSocketPath(
1177
1203
  source: TelegramBusSocketPathSource,
1178
1204
  platform: NodeJS.Platform | string = getPlatform(),
@@ -1205,7 +1231,7 @@ export function resolveTelegramBusSocketPath(
1205
1231
  return join(fallbackDir, `${digest}.sock`);
1206
1232
  }
1207
1233
 
1208
- export interface TelegramBusLocalServerDeps {
1234
+ interface TelegramBusLocalServerDeps {
1209
1235
  socketPath: TelegramBusSocketPathSource;
1210
1236
  handleEnvelope: (
1211
1237
  envelope: TelegramBusEnvelope,
@@ -1221,7 +1247,7 @@ export interface TelegramBusLocalServerDeps {
1221
1247
  ) => boolean;
1222
1248
  }
1223
1249
 
1224
- export interface TelegramBusLocalClientOptions {
1250
+ interface TelegramBusLocalClientOptions {
1225
1251
  socketPath: string;
1226
1252
  envelope: TelegramBusEnvelope;
1227
1253
  timeoutMs?: number;
@@ -1229,7 +1255,7 @@ export interface TelegramBusLocalClientOptions {
1229
1255
  recordTransportEvent?: TelegramBusTransportEventRecorder;
1230
1256
  }
1231
1257
 
1232
- export interface TelegramBusForeignOwnedForwarderDeps<TMessage = unknown> {
1258
+ interface TelegramBusForeignOwnedForwarderDeps<TMessage = unknown> {
1233
1259
  socketPath: TelegramBusSocketPathSource;
1234
1260
  createRequestId: () => string;
1235
1261
  getNowMs?: () => number;
@@ -1635,14 +1661,6 @@ export function createTelegramBusForeignOwnedUpdateForwarder<
1635
1661
  };
1636
1662
  }
1637
1663
 
1638
- export interface TelegramBusFollowerThreadRestoreHandlerDeps {
1639
- followerRegistry: Pick<TelegramBusFollowerRegistry, "get" | "register">;
1640
- followerTargetController: ReturnType<
1641
- typeof createTelegramBusFollowerTargetController
1642
- >;
1643
- onRestored?: () => void;
1644
- }
1645
-
1646
1664
  export function listTelegramBusLiveThreadTargets(input: {
1647
1665
  leaderTarget?: TelegramTarget;
1648
1666
  followers: readonly TelegramBusFollowerView[];
@@ -1657,79 +1675,58 @@ export function listTelegramBusLiveThreadTargets(input: {
1657
1675
  return targets;
1658
1676
  }
1659
1677
 
1660
- export function createTelegramBusFollowerTargetController(
1661
- deps: TelegramBusForeignOwnedForwarderDeps,
1662
- ): {
1663
- replaceTarget: (input: {
1664
- follower: TelegramBusFollowerView;
1665
- target: TelegramTarget & { threadId: number };
1666
- oldTarget?: TelegramTarget & { threadId: number };
1667
- reason: "thread-restore";
1668
- }) => Promise<boolean>;
1669
- } {
1670
- const getNowMs = deps.getNowMs ?? Date.now;
1671
- return {
1672
- async replaceTarget({ follower, target, oldTarget, reason }) {
1673
- if (!follower.busSocketPath || !follower.registrationGeneration) {
1674
- return false;
1675
- }
1676
- const envelope: TelegramBusEnvelope = {
1677
- kind: "leader.replaceFollowerTarget",
1678
- requestId: deps.createRequestId(),
1679
- recipientInstanceId: follower.instanceId,
1680
- recipientRegistrationGeneration: follower.registrationGeneration,
1681
- target,
1682
- ...(oldTarget ? { oldTarget } : {}),
1683
- reason,
1684
- sentAtMs: getNowMs(),
1685
- };
1686
- if (deps.getAuthSecret) envelope.auth = deps.getAuthSecret();
1687
- const response = await sendTelegramBusLocalEnvelope({
1688
- socketPath: follower.busSocketPath,
1689
- envelope,
1690
- timeoutMs: deps.timeoutMs,
1691
- retry: getTelegramBusTransportRetryPolicy({
1692
- endpoint: follower.busSocketPath,
1693
- operation: "operation",
1694
- }),
1695
- });
1696
- return response?.kind === "bus.ack" && response.ok;
1697
- },
1698
- };
1699
- }
1700
-
1701
- export function createTelegramBusFollowerThreadRestoreHandler(
1702
- deps: TelegramBusFollowerThreadRestoreHandlerDeps,
1703
- ): (input: {
1704
- record: { instanceId?: string };
1705
- target: TelegramTarget & { threadId: number };
1706
- oldTarget?: TelegramTarget & { threadId: number };
1707
- }) => Promise<boolean> {
1708
- return async ({ record, target, oldTarget }) => {
1709
- if (!record.instanceId) return false;
1710
- const follower = deps.followerRegistry.get(record.instanceId);
1711
- if (!follower?.registrationGeneration || !oldTarget ||
1712
- follower.target?.chatId !== oldTarget.chatId ||
1713
- follower.target.threadId !== oldTarget.threadId ||
1714
- target.chatId !== oldTarget.chatId || target.threadId === oldTarget.threadId) return false;
1715
- const replaced = await deps.followerTargetController.replaceTarget({
1716
- follower,
1717
- target,
1718
- oldTarget,
1719
- reason: "thread-restore",
1720
- });
1721
- const current = deps.followerRegistry.get(record.instanceId);
1722
- if (!replaced || !current ||
1723
- current.registrationGeneration !== follower.registrationGeneration ||
1724
- current.target?.chatId !== oldTarget.chatId ||
1725
- current.target.threadId !== oldTarget.threadId) return false;
1726
- deps.followerRegistry.register({
1727
- ...current,
1728
- target,
1729
- connectedAtMs: current.connectedAtMs,
1730
- });
1731
- deps.onRestored?.();
1732
- return true;
1678
+ /** Caller owns the durable one-shot apply grant. Inspections never grant another apply. */
1679
+ export function createTelegramBusWorkspaceRestoreController(deps: {
1680
+ getFollower: (instanceId: string) => TelegramBusFollowerView | undefined;
1681
+ localProtocolIdentity: TelegramBusProtocolIdentity;
1682
+ createRequestId: () => string;
1683
+ getAuthSecret: () => string | undefined;
1684
+ timeoutMs?: number;
1685
+ }) {
1686
+ return async (input: { operationId: string; instanceId: string; sessionId: string; slot: string;
1687
+ target: TelegramTarget & { threadId: number }; oldTarget: TelegramTarget & { threadId: number };
1688
+ mode: "apply" | "inspect"; isCurrent: () => boolean }): Promise<TelegramBusWorkspaceRestoreObservation | undefined> => {
1689
+ const isCurrent = input.isCurrent.bind(input);
1690
+ if (!isCurrent()) return undefined;
1691
+ const follower = deps.getFollower(input.instanceId);
1692
+ const secret = deps.getAuthSecret();
1693
+ if (!secret || !follower?.registrationGeneration || !follower.busSocketPath ||
1694
+ follower.sessionId !== input.sessionId || follower.slot !== input.slot ||
1695
+ !hasTelegramBusCapability(deps.localProtocolIdentity, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) ||
1696
+ !hasTelegramBusCapability(follower.protocol, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) ||
1697
+ !getTelegramBusProtocolCompatibility({ local: deps.localProtocolIdentity, remote: follower.protocol }).compatible) return undefined;
1698
+ const captured = structuredClone(follower);
1699
+ const expected = { operationId: input.operationId, sessionId: input.sessionId, slot: input.slot,
1700
+ target: { ...input.target }, oldTarget: { ...input.oldTarget }, mode: input.mode };
1701
+ const current = (): boolean => {
1702
+ if (!isCurrent() || deps.getAuthSecret() !== secret ||
1703
+ !hasTelegramBusCapability(deps.localProtocolIdentity, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE)) return false;
1704
+ const live = deps.getFollower(captured.instanceId);
1705
+ return !!live && live.registrationGeneration === captured.registrationGeneration &&
1706
+ live.sessionId === captured.sessionId && live.cwd === captured.cwd && live.slot === captured.slot &&
1707
+ live.busSocketPath === captured.busSocketPath && live.target?.chatId === captured.target?.chatId &&
1708
+ live.target?.threadId === captured.target?.threadId &&
1709
+ hasTelegramBusCapability(live.protocol, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) &&
1710
+ getTelegramBusProtocolCompatibility({ local: deps.localProtocolIdentity, remote: live.protocol }).compatible;
1711
+ };
1712
+ if (!current()) return undefined;
1713
+ const requestId = deps.createRequestId();
1714
+ const response = await sendTelegramBusLocalEnvelope({ socketPath: captured.busSocketPath!, timeoutMs: deps.timeoutMs,
1715
+ retry: { attempts: 1, delayMs: 0 }, envelope: { kind: "leader.workspaceRestore", requestId,
1716
+ recipientInstanceId: captured.instanceId, recipientRegistrationGeneration: captured.registrationGeneration!,
1717
+ operationId: expected.operationId, mode: expected.mode, auth: secret, sentAtMs: Date.now() } });
1718
+ if (!current() || response?.kind !== "bus.ack" || response.requestId !== requestId || response.ok !== true ||
1719
+ !response.result || typeof response.result !== "object") return undefined;
1720
+ const result = response.result as Record<string, unknown>;
1721
+ const recipient = result.recipient as Record<string, unknown> | undefined;
1722
+ const target = parseThreadTarget(result.target);
1723
+ const wanted = result.ready === true ? expected.target : expected.oldTarget;
1724
+ if (result.operationId !== expected.operationId || typeof result.ready !== "boolean" || result.slot !== expected.slot ||
1725
+ !target || target.chatId !== wanted.chatId || target.threadId !== wanted.threadId ||
1726
+ recipient?.kind !== "follower" || recipient.instanceId !== captured.instanceId ||
1727
+ recipient.sessionId !== expected.sessionId || recipient.generation !== captured.registrationGeneration) return undefined;
1728
+ return { operationId: expected.operationId, recipient: { kind: "follower", instanceId: captured.instanceId,
1729
+ sessionId: expected.sessionId, generation: captured.registrationGeneration! }, target, slot: expected.slot, ready: result.ready };
1733
1730
  };
1734
1731
  }
1735
1732
 
@@ -1817,14 +1814,8 @@ export function createTelegramBusLocalServer(
1817
1814
  result: Promise.resolve().then(() => deps.handleEnvelope(envelope)),
1818
1815
  };
1819
1816
  requestLedger.set(key, entry);
1820
- void entry.result.then(
1821
- () => {
1822
- entry.settled = true;
1823
- },
1824
- () => {
1825
- entry.settled = true;
1826
- },
1827
- );
1817
+ const settled = () => { entry.settled = true; };
1818
+ void entry.result.then(settled, settled);
1828
1819
  return entry.result;
1829
1820
  };
1830
1821
  let server: Server | undefined;
@@ -2572,217 +2563,6 @@ function parseWakeInputCustodyEnvelope(
2572
2563
  : undefined;
2573
2564
  }
2574
2565
 
2575
- function parseReplaceFollowerTargetEnvelope(
2576
- value: Record<string, unknown>,
2577
- requestId: string,
2578
- ): TelegramBusEnvelope | undefined {
2579
- const target = parseThreadTarget(value.target);
2580
- const oldTarget = parseThreadTarget(value.oldTarget);
2581
- if (
2582
- typeof value.recipientInstanceId !== "string" ||
2583
- !target ||
2584
- (value.oldTarget !== undefined && !oldTarget) ||
2585
- value.reason !== "thread-restore" ||
2586
- typeof value.sentAtMs !== "number"
2587
- ) {
2588
- return undefined;
2589
- }
2590
- return {
2591
- kind: "leader.replaceFollowerTarget",
2592
- requestId,
2593
- recipientInstanceId: value.recipientInstanceId,
2594
- ...(typeof value.recipientRegistrationGeneration === "string"
2595
- ? {
2596
- recipientRegistrationGeneration:
2597
- value.recipientRegistrationGeneration,
2598
- }
2599
- : {}),
2600
- target,
2601
- ...(oldTarget ? { oldTarget } : {}),
2602
- reason: value.reason,
2603
- sentAtMs: value.sentAtMs,
2604
- };
2605
- }
2606
-
2607
- function parseQueueAdmissionReceipt(
2608
- value: unknown,
2609
- queueKind: "prompt" | "control",
2610
- ):
2611
- | {
2612
- queueKind: "prompt" | "control";
2613
- receiptId: string;
2614
- sourceUpdateIds: number[];
2615
- }
2616
- | undefined {
2617
- if (!isRecord(value) || value.queueKind !== queueKind) return undefined;
2618
- const sourceUpdateIds = Array.isArray(value.sourceUpdateIds)
2619
- ? value.sourceUpdateIds
2620
- : undefined;
2621
- if (
2622
- typeof value.receiptId !== "string" ||
2623
- !value.receiptId ||
2624
- (value.journalBindingKey !== undefined &&
2625
- (typeof value.journalBindingKey !== "string" ||
2626
- !value.journalBindingKey.trim())) ||
2627
- !sourceUpdateIds ||
2628
- sourceUpdateIds.length === 0 ||
2629
- sourceUpdateIds.some(
2630
- (updateId, index) =>
2631
- !Number.isSafeInteger(updateId) ||
2632
- (updateId as number) < 0 ||
2633
- (index > 0 &&
2634
- (updateId as number) <=
2635
- (sourceUpdateIds[index - 1] as number)),
2636
- )
2637
- ) {
2638
- return undefined;
2639
- }
2640
- return {
2641
- queueKind,
2642
- receiptId: value.receiptId,
2643
- sourceUpdateIds: sourceUpdateIds as number[],
2644
- ...(typeof value.journalBindingKey === "string"
2645
- ? { journalBindingKey: value.journalBindingKey }
2646
- : {}),
2647
- };
2648
- }
2649
-
2650
- function parseQueueHandoffPayload(
2651
- value: unknown,
2652
- ): TelegramQueueHandoffPayload | undefined {
2653
- if (!isRecord(value) || (value.kind !== "prompt" && value.kind !== "control")) {
2654
- return undefined;
2655
- }
2656
- const queueKind = value.kind;
2657
- const target = parseTarget(value.target);
2658
- const transportStamp = isRecord(value.transportStamp) &&
2659
- typeof value.transportStamp.profile === "string" &&
2660
- typeof value.transportStamp.generation === "string"
2661
- ? {
2662
- profile: value.transportStamp.profile,
2663
- generation: value.transportStamp.generation,
2664
- }
2665
- : undefined;
2666
- if (
2667
- !Number.isSafeInteger(value.chatId) ||
2668
- (value.target !== undefined && !target) ||
2669
- (value.transportStamp !== undefined && !transportStamp) ||
2670
- !Number.isSafeInteger(value.replyToMessageId) ||
2671
- (value.guestQueryId !== undefined &&
2672
- typeof value.guestQueryId !== "string") ||
2673
- (value.guestInlineMessageId !== undefined &&
2674
- typeof value.guestInlineMessageId !== "string") ||
2675
- !Number.isSafeInteger(value.queueOrder) ||
2676
- (value.queueLane !== "control" &&
2677
- value.queueLane !== "priority" &&
2678
- value.queueLane !== "default") ||
2679
- !Number.isSafeInteger(value.laneOrder) ||
2680
- typeof value.statusSummary !== "string" ||
2681
- !Array.isArray(value.admissionReceipts)
2682
- ) {
2683
- return undefined;
2684
- }
2685
- const admissionReceipts = value.admissionReceipts.map((receipt) =>
2686
- parseQueueAdmissionReceipt(receipt, queueKind),
2687
- );
2688
- if (
2689
- admissionReceipts.length === 0 ||
2690
- admissionReceipts.length > TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS ||
2691
- admissionReceipts.some((receipt) => receipt === undefined)
2692
- ) {
2693
- return undefined;
2694
- }
2695
- const queueLane = value.queueLane as "control" | "priority" | "default";
2696
- const base = {
2697
- chatId: value.chatId as number,
2698
- ...(target ? { target } : {}),
2699
- ...(transportStamp ? { transportStamp } : {}),
2700
- replyToMessageId: value.replyToMessageId as number,
2701
- ...(typeof value.guestQueryId === "string"
2702
- ? { guestQueryId: value.guestQueryId }
2703
- : {}),
2704
- ...(typeof value.guestInlineMessageId === "string"
2705
- ? { guestInlineMessageId: value.guestInlineMessageId }
2706
- : {}),
2707
- queueOrder: value.queueOrder as number,
2708
- queueLane,
2709
- laneOrder: value.laneOrder as number,
2710
- statusSummary: value.statusSummary,
2711
- admissionReceipts: admissionReceipts as NonNullable<
2712
- (typeof admissionReceipts)[number]
2713
- >[],
2714
- };
2715
- if (queueKind === "control") {
2716
- if (
2717
- value.queueLane !== "control" ||
2718
- (value.controlType !== "status" && value.controlType !== "model")
2719
- ) {
2720
- return undefined;
2721
- }
2722
- return { kind: "control", controlType: value.controlType, ...base };
2723
- }
2724
- if (
2725
- value.queueLane === "control" ||
2726
- !Array.isArray(value.sourceMessageIds) ||
2727
- value.sourceMessageIds.some((id) => !Number.isSafeInteger(id)) ||
2728
- !Array.isArray(value.queuedAttachments) ||
2729
- value.queuedAttachments.some(
2730
- (attachment) =>
2731
- !isRecord(attachment) ||
2732
- typeof attachment.path !== "string" ||
2733
- typeof attachment.fileName !== "string",
2734
- ) ||
2735
- !Array.isArray(value.content) ||
2736
- value.content.some(
2737
- (content) =>
2738
- !isRecord(content) ||
2739
- (content.type === "text"
2740
- ? typeof content.text !== "string"
2741
- : content.type === "image"
2742
- ? typeof content.data !== "string" ||
2743
- typeof content.mimeType !== "string"
2744
- : true),
2745
- ) ||
2746
- typeof value.historyText !== "string" ||
2747
- (value.priorityEmoji !== undefined &&
2748
- typeof value.priorityEmoji !== "string") ||
2749
- (value.reactionSuppressionEmoji !== undefined &&
2750
- typeof value.reactionSuppressionEmoji !== "string") ||
2751
- (value.voiceReplyPreferred !== undefined &&
2752
- typeof value.voiceReplyPreferred !== "boolean") ||
2753
- (value.voiceReplyRequired !== undefined &&
2754
- typeof value.voiceReplyRequired !== "boolean")
2755
- ) {
2756
- return undefined;
2757
- }
2758
- return {
2759
- kind: "prompt",
2760
- ...base,
2761
- sourceMessageIds: value.sourceMessageIds as number[],
2762
- queuedAttachments: value.queuedAttachments as Array<{
2763
- path: string;
2764
- fileName: string;
2765
- }>,
2766
- content: value.content as Array<
2767
- | { type: "text"; text: string }
2768
- | { type: "image"; data: string; mimeType: string }
2769
- >,
2770
- historyText: value.historyText,
2771
- ...(typeof value.priorityEmoji === "string"
2772
- ? { priorityEmoji: value.priorityEmoji }
2773
- : {}),
2774
- ...(typeof value.reactionSuppressionEmoji === "string"
2775
- ? { reactionSuppressionEmoji: value.reactionSuppressionEmoji }
2776
- : {}),
2777
- ...(typeof value.voiceReplyPreferred === "boolean"
2778
- ? { voiceReplyPreferred: value.voiceReplyPreferred }
2779
- : {}),
2780
- ...(typeof value.voiceReplyRequired === "boolean"
2781
- ? { voiceReplyRequired: value.voiceReplyRequired }
2782
- : {}),
2783
- };
2784
- }
2785
-
2786
2566
  function parseQueueHandoffEnvelope(
2787
2567
  value: Record<string, unknown>,
2788
2568
  requestId: string,
@@ -2797,7 +2577,7 @@ function parseQueueHandoffEnvelope(
2797
2577
  if (serializedPayloadBytes > TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES) {
2798
2578
  return undefined;
2799
2579
  }
2800
- const payload = parseQueueHandoffPayload(value.payload);
2580
+ const payload = parseTelegramQueueHandoffPayload(value.payload);
2801
2581
  if (
2802
2582
  !payload ||
2803
2583
  typeof value.recipientInstanceId !== "string" ||
@@ -3102,7 +2882,3 @@ function parseThreadTarget(
3102
2882
  ? { chatId: target.chatId, threadId: target.threadId }
3103
2883
  : undefined;
3104
2884
  }
3105
-
3106
- function isRecord(value: unknown): value is Record<string, unknown> {
3107
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3108
- }