@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
@@ -11,9 +11,10 @@ import { createConnection, createServer, } from "node:net";
11
11
  import { createRequire } from "node:module";
12
12
  import { platform as getPlatform, tmpdir } from "node:os";
13
13
  import { basename, dirname, join } from "node:path";
14
- import { classifyTelegramBusTransportError, createTelegramBusTransportTimeoutError, delayTelegramBusTransportRetry, getTelegramBusEndpointDiagnostics, getTelegramBusFollowerEndpoint, getTelegramBusLeaderEndpoint, getTelegramBusPipePath, getTelegramBusTransportRetryPolicy, isTelegramBusPipePath, isRetryableTelegramBusTransportError, probeTelegramBusEndpoint, } from "./bus-transport.js";
15
- import { TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS, TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES, } from "./queue.js";
14
+ import { classifyTelegramBusTransportError, createTelegramBusTransportTimeoutError, delayTelegramBusTransportRetry, getTelegramBusEndpointDiagnostics, getTelegramBusFollowerEndpoint, getTelegramBusLeaderEndpoint, getTelegramBusPipePath, getTelegramBusTransportRetryPolicy, TELEGRAM_BUS_MAX_DIRECT_UNIX_ENDPOINT_BYTES, isTelegramBusPipePath, isRetryableTelegramBusTransportError, probeTelegramBusEndpoint, } from "./bus-transport.js";
15
+ import { parseTelegramQueueHandoffPayload, TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES, } from "./queue.js";
16
16
  import { isProcessAlive } from "./locks.js";
17
+ import { isWireRecord as isRecord } from "./wire.js";
17
18
  import { resolveAgentDir } from "./paths.js";
18
19
  import { normalizeTelegramSessionReplacementIntent, } from "./threads.js";
19
20
  function readDarwinProcessStart(pid) {
@@ -22,7 +23,7 @@ function readDarwinProcessStart(pid) {
22
23
  stdio: ["ignore", "pipe", "ignore"],
23
24
  }).trim();
24
25
  }
25
- export function getTelegramProcessBirthProof(pid, options = {}) {
26
+ function getTelegramProcessBirthProof(pid, options = {}) {
26
27
  if (pid <= 0)
27
28
  return { status: "unverifiable" };
28
29
  const platform = options.platform ?? getPlatform();
@@ -95,12 +96,14 @@ export function getTelegramProcessBirthIdentityLiveness(processBirthId, options
95
96
  export function createCurrentTelegramBusProcessRuntime(input) {
96
97
  return createTelegramBusProcessRuntime({
97
98
  getActiveProfileName: input.getActiveProfileName,
99
+ endpointLayout: input.endpointLayout,
98
100
  pid: input.pid ?? process.pid,
99
101
  parentPid: input.parentPid ?? process.ppid,
100
102
  createdAtMs: input.createdAtMs ?? Date.now(),
101
103
  });
102
104
  }
103
105
  export function createTelegramBusProcessRuntime(input) {
106
+ const getActiveProfileName = input.getActiveProfileName, endpointLayout = input.endpointLayout;
104
107
  const instanceId = `${input.pid}:${input.createdAtMs}`;
105
108
  const ownerPid = input.parentPid || input.pid;
106
109
  const manualFollowerOwnerId = input.parentProcessIdentity ??
@@ -110,14 +113,14 @@ export function createTelegramBusProcessRuntime(input) {
110
113
  processId: input.pid,
111
114
  processBirthId: getTelegramProcessBirthIdentity(input.pid, instanceId),
112
115
  manualFollowerOwnerId,
113
- getLeaderSocketPath: () => getTelegramBusSocketPath(undefined, undefined, input.getActiveProfileName()),
114
- getFollowerSocketPath: () => getTelegramBusFollowerSocketPath(instanceId, undefined, undefined, input.getActiveProfileName()),
116
+ getLeaderSocketPath: () => getTelegramBusSocketPath(undefined, undefined, getActiveProfileName(), endpointLayout),
117
+ getFollowerSocketPath: () => getTelegramBusFollowerSocketPath(instanceId, undefined, undefined, getActiveProfileName(), endpointLayout),
115
118
  };
116
119
  }
117
120
  export function createTelegramBusAuthSecret() {
118
121
  return randomBytes(32).toString("base64url");
119
122
  }
120
- export const TELEGRAM_BUS_PROTOCOL_VERSION = 2;
123
+ const TELEGRAM_BUS_PROTOCOL_VERSION = 2;
121
124
  export const TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION = "durable-follower-admission-v1";
122
125
  export const TELEGRAM_BUS_CAPABILITY_QUEUE_HANDOFF = "queue-handoff-v1";
123
126
  export const TELEGRAM_BUS_CAPABILITY_INPUT_CUSTODY_REFERENCE = "input-custody-reference-v1";
@@ -126,6 +129,7 @@ export const TELEGRAM_BUS_CAPABILITY_THREAD_DISPLAY_MODE = "thread-display-mode-
126
129
  export const TELEGRAM_BUS_CAPABILITY_DIRECTORY_DISPLAY_FORMAT = "directory-display-format-v1";
127
130
  export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_FOLLOWER_AUTO_CONNECT = "workspace-follower-auto-connect-v1";
128
131
  export const TELEGRAM_BUS_CAPABILITY_SESSION_REPLACEMENT_INTENT = "session-replacement-intent-v1";
132
+ export const TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE = "workspace-restore-v1";
129
133
  export function createTelegramBusProtocolIdentity(input) {
130
134
  const runtimeBuild = input.runtimeBuild.trim();
131
135
  if (!runtimeBuild || runtimeBuild.length > 128) {
@@ -191,15 +195,16 @@ export function getTelegramBusProtocolCompatibility(input) {
191
195
  }
192
196
  : { compatible: true, missingCapabilities: [] };
193
197
  }
194
- export function getTelegramBusSocketPath(agentDir = resolveAgentDir(), platform = getPlatform(), profileName) {
195
- return getTelegramBusLeaderEndpoint({ agentDir, platform, profileName });
198
+ export function getTelegramBusSocketPath(agentDir = resolveAgentDir(), platform = getPlatform(), profileName, layout) {
199
+ return getTelegramBusLeaderEndpoint({ agentDir, platform, profileName, layout });
196
200
  }
197
- export function getTelegramBusFollowerSocketPath(instanceId, agentDir = resolveAgentDir(), platform = getPlatform(), profileName) {
201
+ export function getTelegramBusFollowerSocketPath(instanceId, agentDir = resolveAgentDir(), platform = getPlatform(), profileName, layout) {
198
202
  return getTelegramBusFollowerEndpoint({
199
203
  agentDir,
200
204
  platform,
201
205
  instanceId,
202
206
  profileName,
207
+ layout,
203
208
  });
204
209
  }
205
210
  export function getTelegramFollowerTargetOwnership(input) {
@@ -229,7 +234,7 @@ export function markTelegramBusAggregateDelivery(body) {
229
234
  [TELEGRAM_BUS_AGGREGATE_DELIVERY_FIELD]: true,
230
235
  };
231
236
  }
232
- export function isTelegramBusAggregateDelivery(body) {
237
+ function isTelegramBusAggregateDelivery(body) {
233
238
  return Boolean(body &&
234
239
  typeof body === "object" &&
235
240
  !Array.isArray(body) &&
@@ -242,7 +247,7 @@ export function markTelegramBusCrossTargetDelivery(body) {
242
247
  [TELEGRAM_BUS_CROSS_TARGET_DELIVERY_FIELD]: true,
243
248
  };
244
249
  }
245
- export function isTelegramBusCrossTargetDelivery(body) {
250
+ function isTelegramBusCrossTargetDelivery(body) {
246
251
  return Boolean(body &&
247
252
  typeof body === "object" &&
248
253
  !Array.isArray(body) &&
@@ -542,8 +547,16 @@ export function parseTelegramBusEnvelope(line) {
542
547
  case "leader.forwardEditedMessage":
543
548
  envelope = parseForwardMessageEnvelope(value, requestId, "leader.forwardEditedMessage");
544
549
  break;
545
- case "leader.replaceFollowerTarget":
546
- envelope = parseReplaceFollowerTargetEnvelope(value, requestId);
550
+ case "leader.workspaceRestore":
551
+ if (typeof value.recipientInstanceId === "string" && value.recipientInstanceId &&
552
+ typeof value.recipientRegistrationGeneration === "string" && value.recipientRegistrationGeneration &&
553
+ typeof value.operationId === "string" && value.operationId.length > 0 && value.operationId.length <= 128 &&
554
+ (value.mode === "apply" || value.mode === "inspect") &&
555
+ typeof value.sentAtMs === "number" && Number.isFinite(value.sentAtMs)) {
556
+ envelope = { kind: "leader.workspaceRestore", requestId, recipientInstanceId: value.recipientInstanceId,
557
+ recipientRegistrationGeneration: value.recipientRegistrationGeneration, operationId: value.operationId,
558
+ mode: value.mode, sentAtMs: value.sentAtMs };
559
+ }
547
560
  break;
548
561
  case "leader.offerQueueHandoff":
549
562
  envelope = parseQueueHandoffEnvelope(value, requestId, "leader.offerQueueHandoff");
@@ -576,7 +589,6 @@ function getActiveTelegramBusLocalServers() {
576
589
  const root = globalThis;
577
590
  return (root[TELEGRAM_ACTIVE_LOCAL_SERVERS] ??= new Map());
578
591
  }
579
- const TELEGRAM_BUS_MAX_DIRECT_UNIX_ENDPOINT_BYTES = 80;
580
592
  export function resolveTelegramBusSocketPath(source, platform = getPlatform()) {
581
593
  const endpoint = typeof source === "function" ? source() : source;
582
594
  if (platform === "win32") {
@@ -892,67 +904,56 @@ export function listTelegramBusLiveThreadTargets(input) {
892
904
  }
893
905
  return targets;
894
906
  }
895
- export function createTelegramBusFollowerTargetController(deps) {
896
- const getNowMs = deps.getNowMs ?? Date.now;
897
- return {
898
- async replaceTarget({ follower, target, oldTarget, reason }) {
899
- if (!follower.busSocketPath || !follower.registrationGeneration) {
907
+ /** Caller owns the durable one-shot apply grant. Inspections never grant another apply. */
908
+ export function createTelegramBusWorkspaceRestoreController(deps) {
909
+ return async (input) => {
910
+ const isCurrent = input.isCurrent.bind(input);
911
+ if (!isCurrent())
912
+ return undefined;
913
+ const follower = deps.getFollower(input.instanceId);
914
+ const secret = deps.getAuthSecret();
915
+ if (!secret || !follower?.registrationGeneration || !follower.busSocketPath ||
916
+ follower.sessionId !== input.sessionId || follower.slot !== input.slot ||
917
+ !hasTelegramBusCapability(deps.localProtocolIdentity, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) ||
918
+ !hasTelegramBusCapability(follower.protocol, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) ||
919
+ !getTelegramBusProtocolCompatibility({ local: deps.localProtocolIdentity, remote: follower.protocol }).compatible)
920
+ return undefined;
921
+ const captured = structuredClone(follower);
922
+ const expected = { operationId: input.operationId, sessionId: input.sessionId, slot: input.slot,
923
+ target: { ...input.target }, oldTarget: { ...input.oldTarget }, mode: input.mode };
924
+ const current = () => {
925
+ if (!isCurrent() || deps.getAuthSecret() !== secret ||
926
+ !hasTelegramBusCapability(deps.localProtocolIdentity, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE))
900
927
  return false;
901
- }
902
- const envelope = {
903
- kind: "leader.replaceFollowerTarget",
904
- requestId: deps.createRequestId(),
905
- recipientInstanceId: follower.instanceId,
906
- recipientRegistrationGeneration: follower.registrationGeneration,
907
- target,
908
- ...(oldTarget ? { oldTarget } : {}),
909
- reason,
910
- sentAtMs: getNowMs(),
911
- };
912
- if (deps.getAuthSecret)
913
- envelope.auth = deps.getAuthSecret();
914
- const response = await sendTelegramBusLocalEnvelope({
915
- socketPath: follower.busSocketPath,
916
- envelope,
917
- timeoutMs: deps.timeoutMs,
918
- retry: getTelegramBusTransportRetryPolicy({
919
- endpoint: follower.busSocketPath,
920
- operation: "operation",
921
- }),
922
- });
923
- return response?.kind === "bus.ack" && response.ok;
924
- },
925
- };
926
- }
927
- export function createTelegramBusFollowerThreadRestoreHandler(deps) {
928
- return async ({ record, target, oldTarget }) => {
929
- if (!record.instanceId)
930
- return false;
931
- const follower = deps.followerRegistry.get(record.instanceId);
932
- if (!follower?.registrationGeneration || !oldTarget ||
933
- follower.target?.chatId !== oldTarget.chatId ||
934
- follower.target.threadId !== oldTarget.threadId ||
935
- target.chatId !== oldTarget.chatId || target.threadId === oldTarget.threadId)
936
- return false;
937
- const replaced = await deps.followerTargetController.replaceTarget({
938
- follower,
939
- target,
940
- oldTarget,
941
- reason: "thread-restore",
942
- });
943
- const current = deps.followerRegistry.get(record.instanceId);
944
- if (!replaced || !current ||
945
- current.registrationGeneration !== follower.registrationGeneration ||
946
- current.target?.chatId !== oldTarget.chatId ||
947
- current.target.threadId !== oldTarget.threadId)
948
- return false;
949
- deps.followerRegistry.register({
950
- ...current,
951
- target,
952
- connectedAtMs: current.connectedAtMs,
953
- });
954
- deps.onRestored?.();
955
- return true;
928
+ const live = deps.getFollower(captured.instanceId);
929
+ return !!live && live.registrationGeneration === captured.registrationGeneration &&
930
+ live.sessionId === captured.sessionId && live.cwd === captured.cwd && live.slot === captured.slot &&
931
+ live.busSocketPath === captured.busSocketPath && live.target?.chatId === captured.target?.chatId &&
932
+ live.target?.threadId === captured.target?.threadId &&
933
+ hasTelegramBusCapability(live.protocol, TELEGRAM_BUS_CAPABILITY_WORKSPACE_RESTORE) &&
934
+ getTelegramBusProtocolCompatibility({ local: deps.localProtocolIdentity, remote: live.protocol }).compatible;
935
+ };
936
+ if (!current())
937
+ return undefined;
938
+ const requestId = deps.createRequestId();
939
+ const response = await sendTelegramBusLocalEnvelope({ socketPath: captured.busSocketPath, timeoutMs: deps.timeoutMs,
940
+ retry: { attempts: 1, delayMs: 0 }, envelope: { kind: "leader.workspaceRestore", requestId,
941
+ recipientInstanceId: captured.instanceId, recipientRegistrationGeneration: captured.registrationGeneration,
942
+ operationId: expected.operationId, mode: expected.mode, auth: secret, sentAtMs: Date.now() } });
943
+ if (!current() || response?.kind !== "bus.ack" || response.requestId !== requestId || response.ok !== true ||
944
+ !response.result || typeof response.result !== "object")
945
+ return undefined;
946
+ const result = response.result;
947
+ const recipient = result.recipient;
948
+ const target = parseThreadTarget(result.target);
949
+ const wanted = result.ready === true ? expected.target : expected.oldTarget;
950
+ if (result.operationId !== expected.operationId || typeof result.ready !== "boolean" || result.slot !== expected.slot ||
951
+ !target || target.chatId !== wanted.chatId || target.threadId !== wanted.threadId ||
952
+ recipient?.kind !== "follower" || recipient.instanceId !== captured.instanceId ||
953
+ recipient.sessionId !== expected.sessionId || recipient.generation !== captured.registrationGeneration)
954
+ return undefined;
955
+ return { operationId: expected.operationId, recipient: { kind: "follower", instanceId: captured.instanceId,
956
+ sessionId: expected.sessionId, generation: captured.registrationGeneration }, target, slot: expected.slot, ready: result.ready };
956
957
  };
957
958
  }
958
959
  export function isTelegramBusEnvelopeAuthorized(envelope, secret) {
@@ -1021,11 +1022,8 @@ export function createTelegramBusLocalServer(deps) {
1021
1022
  result: Promise.resolve().then(() => deps.handleEnvelope(envelope)),
1022
1023
  };
1023
1024
  requestLedger.set(key, entry);
1024
- void entry.result.then(() => {
1025
- entry.settled = true;
1026
- }, () => {
1027
- entry.settled = true;
1028
- });
1025
+ const settled = () => { entry.settled = true; };
1026
+ void entry.result.then(settled, settled);
1029
1027
  return entry.result;
1030
1028
  };
1031
1029
  let server;
@@ -1652,169 +1650,6 @@ function parseWakeInputCustodyEnvelope(value, requestId) {
1652
1650
  delivery, sentAtMs: value.sentAtMs }
1653
1651
  : undefined;
1654
1652
  }
1655
- function parseReplaceFollowerTargetEnvelope(value, requestId) {
1656
- const target = parseThreadTarget(value.target);
1657
- const oldTarget = parseThreadTarget(value.oldTarget);
1658
- if (typeof value.recipientInstanceId !== "string" ||
1659
- !target ||
1660
- (value.oldTarget !== undefined && !oldTarget) ||
1661
- value.reason !== "thread-restore" ||
1662
- typeof value.sentAtMs !== "number") {
1663
- return undefined;
1664
- }
1665
- return {
1666
- kind: "leader.replaceFollowerTarget",
1667
- requestId,
1668
- recipientInstanceId: value.recipientInstanceId,
1669
- ...(typeof value.recipientRegistrationGeneration === "string"
1670
- ? {
1671
- recipientRegistrationGeneration: value.recipientRegistrationGeneration,
1672
- }
1673
- : {}),
1674
- target,
1675
- ...(oldTarget ? { oldTarget } : {}),
1676
- reason: value.reason,
1677
- sentAtMs: value.sentAtMs,
1678
- };
1679
- }
1680
- function parseQueueAdmissionReceipt(value, queueKind) {
1681
- if (!isRecord(value) || value.queueKind !== queueKind)
1682
- return undefined;
1683
- const sourceUpdateIds = Array.isArray(value.sourceUpdateIds)
1684
- ? value.sourceUpdateIds
1685
- : undefined;
1686
- if (typeof value.receiptId !== "string" ||
1687
- !value.receiptId ||
1688
- (value.journalBindingKey !== undefined &&
1689
- (typeof value.journalBindingKey !== "string" ||
1690
- !value.journalBindingKey.trim())) ||
1691
- !sourceUpdateIds ||
1692
- sourceUpdateIds.length === 0 ||
1693
- sourceUpdateIds.some((updateId, index) => !Number.isSafeInteger(updateId) ||
1694
- updateId < 0 ||
1695
- (index > 0 &&
1696
- updateId <=
1697
- sourceUpdateIds[index - 1]))) {
1698
- return undefined;
1699
- }
1700
- return {
1701
- queueKind,
1702
- receiptId: value.receiptId,
1703
- sourceUpdateIds: sourceUpdateIds,
1704
- ...(typeof value.journalBindingKey === "string"
1705
- ? { journalBindingKey: value.journalBindingKey }
1706
- : {}),
1707
- };
1708
- }
1709
- function parseQueueHandoffPayload(value) {
1710
- if (!isRecord(value) || (value.kind !== "prompt" && value.kind !== "control")) {
1711
- return undefined;
1712
- }
1713
- const queueKind = value.kind;
1714
- const target = parseTarget(value.target);
1715
- const transportStamp = isRecord(value.transportStamp) &&
1716
- typeof value.transportStamp.profile === "string" &&
1717
- typeof value.transportStamp.generation === "string"
1718
- ? {
1719
- profile: value.transportStamp.profile,
1720
- generation: value.transportStamp.generation,
1721
- }
1722
- : undefined;
1723
- if (!Number.isSafeInteger(value.chatId) ||
1724
- (value.target !== undefined && !target) ||
1725
- (value.transportStamp !== undefined && !transportStamp) ||
1726
- !Number.isSafeInteger(value.replyToMessageId) ||
1727
- (value.guestQueryId !== undefined &&
1728
- typeof value.guestQueryId !== "string") ||
1729
- (value.guestInlineMessageId !== undefined &&
1730
- typeof value.guestInlineMessageId !== "string") ||
1731
- !Number.isSafeInteger(value.queueOrder) ||
1732
- (value.queueLane !== "control" &&
1733
- value.queueLane !== "priority" &&
1734
- value.queueLane !== "default") ||
1735
- !Number.isSafeInteger(value.laneOrder) ||
1736
- typeof value.statusSummary !== "string" ||
1737
- !Array.isArray(value.admissionReceipts)) {
1738
- return undefined;
1739
- }
1740
- const admissionReceipts = value.admissionReceipts.map((receipt) => parseQueueAdmissionReceipt(receipt, queueKind));
1741
- if (admissionReceipts.length === 0 ||
1742
- admissionReceipts.length > TELEGRAM_QUEUE_HANDOFF_MAX_RECEIPTS ||
1743
- admissionReceipts.some((receipt) => receipt === undefined)) {
1744
- return undefined;
1745
- }
1746
- const queueLane = value.queueLane;
1747
- const base = {
1748
- chatId: value.chatId,
1749
- ...(target ? { target } : {}),
1750
- ...(transportStamp ? { transportStamp } : {}),
1751
- replyToMessageId: value.replyToMessageId,
1752
- ...(typeof value.guestQueryId === "string"
1753
- ? { guestQueryId: value.guestQueryId }
1754
- : {}),
1755
- ...(typeof value.guestInlineMessageId === "string"
1756
- ? { guestInlineMessageId: value.guestInlineMessageId }
1757
- : {}),
1758
- queueOrder: value.queueOrder,
1759
- queueLane,
1760
- laneOrder: value.laneOrder,
1761
- statusSummary: value.statusSummary,
1762
- admissionReceipts: admissionReceipts,
1763
- };
1764
- if (queueKind === "control") {
1765
- if (value.queueLane !== "control" ||
1766
- (value.controlType !== "status" && value.controlType !== "model")) {
1767
- return undefined;
1768
- }
1769
- return { kind: "control", controlType: value.controlType, ...base };
1770
- }
1771
- if (value.queueLane === "control" ||
1772
- !Array.isArray(value.sourceMessageIds) ||
1773
- value.sourceMessageIds.some((id) => !Number.isSafeInteger(id)) ||
1774
- !Array.isArray(value.queuedAttachments) ||
1775
- value.queuedAttachments.some((attachment) => !isRecord(attachment) ||
1776
- typeof attachment.path !== "string" ||
1777
- typeof attachment.fileName !== "string") ||
1778
- !Array.isArray(value.content) ||
1779
- value.content.some((content) => !isRecord(content) ||
1780
- (content.type === "text"
1781
- ? typeof content.text !== "string"
1782
- : content.type === "image"
1783
- ? typeof content.data !== "string" ||
1784
- typeof content.mimeType !== "string"
1785
- : true)) ||
1786
- typeof value.historyText !== "string" ||
1787
- (value.priorityEmoji !== undefined &&
1788
- typeof value.priorityEmoji !== "string") ||
1789
- (value.reactionSuppressionEmoji !== undefined &&
1790
- typeof value.reactionSuppressionEmoji !== "string") ||
1791
- (value.voiceReplyPreferred !== undefined &&
1792
- typeof value.voiceReplyPreferred !== "boolean") ||
1793
- (value.voiceReplyRequired !== undefined &&
1794
- typeof value.voiceReplyRequired !== "boolean")) {
1795
- return undefined;
1796
- }
1797
- return {
1798
- kind: "prompt",
1799
- ...base,
1800
- sourceMessageIds: value.sourceMessageIds,
1801
- queuedAttachments: value.queuedAttachments,
1802
- content: value.content,
1803
- historyText: value.historyText,
1804
- ...(typeof value.priorityEmoji === "string"
1805
- ? { priorityEmoji: value.priorityEmoji }
1806
- : {}),
1807
- ...(typeof value.reactionSuppressionEmoji === "string"
1808
- ? { reactionSuppressionEmoji: value.reactionSuppressionEmoji }
1809
- : {}),
1810
- ...(typeof value.voiceReplyPreferred === "boolean"
1811
- ? { voiceReplyPreferred: value.voiceReplyPreferred }
1812
- : {}),
1813
- ...(typeof value.voiceReplyRequired === "boolean"
1814
- ? { voiceReplyRequired: value.voiceReplyRequired }
1815
- : {}),
1816
- };
1817
- }
1818
1653
  function parseQueueHandoffEnvelope(value, requestId, kind) {
1819
1654
  let serializedPayloadBytes;
1820
1655
  try {
@@ -1826,7 +1661,7 @@ function parseQueueHandoffEnvelope(value, requestId, kind) {
1826
1661
  if (serializedPayloadBytes > TELEGRAM_QUEUE_HANDOFF_PAYLOAD_MAX_BYTES) {
1827
1662
  return undefined;
1828
1663
  }
1829
- const payload = parseQueueHandoffPayload(value.payload);
1664
+ const payload = parseTelegramQueueHandoffPayload(value.payload);
1830
1665
  if (!payload ||
1831
1666
  typeof value.recipientInstanceId !== "string" ||
1832
1667
  typeof value.recipientRegistrationGeneration !== "string" ||
@@ -2096,6 +1931,3 @@ function parseThreadTarget(value) {
2096
1931
  ? { chatId: target.chatId, threadId: target.threadId }
2097
1932
  : undefined;
2098
1933
  }
2099
- function isRecord(value) {
2100
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2101
- }
@@ -11,9 +11,6 @@ export interface TelegramChannelPostMediaIntent {
11
11
  sizeBytes: number;
12
12
  sha256: string;
13
13
  }
14
- export declare const TELEGRAM_CHANNEL_POST_MEDIA_MAX_BYTES: Record<TelegramChannelPostMediaKind, number>;
15
- export declare const TELEGRAM_CHANNEL_POST_CAPTION_MAX_LENGTH = 1024;
16
- export declare const TELEGRAM_CHANNEL_POST_MEDIA_FILE_NAME_MAX_LENGTH = 255;
17
14
  /** Safe, content-free local validation failure for channel media publication intent. */
18
15
  export declare class TelegramChannelPostValidationError extends Error {
19
16
  constructor(message: string);
@@ -70,6 +67,7 @@ export type TelegramChannelPostRecord = TelegramChannelPostRecordBase & ({
70
67
  } & TelegramPublishedChannelPostIdentity));
71
68
  export interface TelegramChannelPostJournalStoreOptions {
72
69
  path: string;
70
+ runtimeDir?: string;
73
71
  profileName: string;
74
72
  tokenSha256: string;
75
73
  maxRecords?: number;
@@ -6,21 +6,23 @@
6
6
  import { chmodSync, closeSync, constants, createReadStream, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
7
7
  import { lstat } from "node:fs/promises";
8
8
  import { basename, dirname } from "node:path";
9
+ import { getTelegramJournalPublicationPaths } from "./paths.js";
9
10
  import { createHash, randomUUID } from "node:crypto";
10
11
  import { Type } from "@sinclair/typebox";
11
12
  import { renameTelegramPathWithRetry, withTelegramFileTransaction } from "./locks.js";
13
+ import { isWireRecord as isRecord, hasOnlyWireKeys as hasOnlyKeys, isNonNegativeWireInteger as isSafeTime, } from "./wire.js";
12
14
  const CHANNEL_POST_JOURNAL_VERSION = 1;
13
15
  const DEFAULT_MAX_RECORDS = 256;
14
16
  const DEFAULT_MAX_BYTES = 4 * 1024 * 1024;
15
17
  const MAX_ID_LENGTH = 256;
16
18
  const MAX_MARKDOWN_LENGTH = 100_000;
17
19
  const MAX_CHANNEL_TITLE_LENGTH = 255;
18
- export const TELEGRAM_CHANNEL_POST_MEDIA_MAX_BYTES = {
20
+ const TELEGRAM_CHANNEL_POST_MEDIA_MAX_BYTES = {
19
21
  photo: 10 * 1024 * 1024,
20
22
  video: 50 * 1024 * 1024,
21
23
  };
22
- export const TELEGRAM_CHANNEL_POST_CAPTION_MAX_LENGTH = 1024;
23
- export const TELEGRAM_CHANNEL_POST_MEDIA_FILE_NAME_MAX_LENGTH = 255;
24
+ const TELEGRAM_CHANNEL_POST_CAPTION_MAX_LENGTH = 1024;
25
+ const TELEGRAM_CHANNEL_POST_MEDIA_FILE_NAME_MAX_LENGTH = 255;
24
26
  /** Safe, content-free local validation failure for channel media publication intent. */
25
27
  export class TelegramChannelPostValidationError extends Error {
26
28
  constructor(message) {
@@ -195,16 +197,6 @@ export function registerTelegramChannelPostListTool(pi, deps) {
195
197
  },
196
198
  });
197
199
  }
198
- function isRecord(value) {
199
- return !!value && typeof value === "object" && !Array.isArray(value);
200
- }
201
- function hasOnlyKeys(value, keys) {
202
- const allowed = new Set(keys);
203
- return Object.keys(value).every(key => allowed.has(key));
204
- }
205
- function isSafeTime(value) {
206
- return Number.isSafeInteger(value) && value >= 0;
207
- }
208
200
  function normalizeChannel(value) {
209
201
  if (Number.isSafeInteger(value) && value < 0)
210
202
  return value;
@@ -312,7 +304,21 @@ function validateRecord(value) {
312
304
  }
313
305
  throw new TelegramChannelPostJournalError("invalid", "Telegram channel post journal contains an invalid state.");
314
306
  }
307
+ function parseTelegramChannelPostJournalFile(value, profileName, tokenSha256) {
308
+ if (!isRecord(value) || !hasOnlyKeys(value, ["version", "profile", "tokenSha256", "records"]) ||
309
+ value.version !== CHANNEL_POST_JOURNAL_VERSION || value.profile !== profileName ||
310
+ value.tokenSha256 !== tokenSha256 || !Array.isArray(value.records)) {
311
+ throw new TelegramChannelPostJournalError("conflict", "Telegram channel post journal identity or schema does not match.");
312
+ }
313
+ const records = value.records.map(validateRecord);
314
+ if (new Set(records.map(record => record.operationId)).size !== records.length) {
315
+ throw new TelegramChannelPostJournalError("invalid", "Telegram channel post journal contains duplicate operation IDs.");
316
+ }
317
+ return { version: CHANNEL_POST_JOURNAL_VERSION, profile: profileName, tokenSha256, records };
318
+ }
315
319
  export function createTelegramChannelPostJournalStore(options) {
320
+ options = { ...options };
321
+ const publicationPaths = getTelegramJournalPublicationPaths(options.path, options.runtimeDir);
316
322
  const maxRecords = options.maxRecords ?? DEFAULT_MAX_RECORDS;
317
323
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
318
324
  const now = options.getNowMs ?? Date.now;
@@ -348,17 +354,7 @@ export function createTelegramChannelPostJournalStore(options) {
348
354
  throw new TelegramChannelPostJournalError("conflict", "Telegram channel post journal changed during inspection.");
349
355
  }
350
356
  const value = JSON.parse(readFileSync(fd, "utf8"));
351
- if (!isRecord(value) || !hasOnlyKeys(value, ["version", "profile", "tokenSha256", "records"]) ||
352
- value.version !== CHANNEL_POST_JOURNAL_VERSION || value.profile !== options.profileName ||
353
- value.tokenSha256 !== options.tokenSha256 || !Array.isArray(value.records)) {
354
- throw new TelegramChannelPostJournalError("conflict", "Telegram channel post journal identity or schema does not match.");
355
- }
356
- const records = value.records.map(validateRecord);
357
- if (new Set(records.map(record => record.operationId)).size !== records.length) {
358
- throw new TelegramChannelPostJournalError("invalid", "Telegram channel post journal contains duplicate operation IDs.");
359
- }
360
- return { version: CHANNEL_POST_JOURNAL_VERSION, profile: options.profileName,
361
- tokenSha256: options.tokenSha256, records };
357
+ return parseTelegramChannelPostJournalFile(value, options.profileName, options.tokenSha256);
362
358
  }
363
359
  catch (error) {
364
360
  if (error instanceof TelegramChannelPostJournalError)
@@ -378,8 +374,9 @@ export function createTelegramChannelPostJournalStore(options) {
378
374
  if (Buffer.byteLength(serialized) > maxBytes) {
379
375
  throw new TelegramChannelPostJournalError("capacity", "Telegram channel post journal byte limit reached.");
380
376
  }
381
- const temporaryPath = `${options.path}.${process.pid}.${randomUUID()}.tmp`;
377
+ const temporaryPath = `${publicationPaths.temporaryBasePath}.${process.pid}.${randomUUID()}.tmp`;
382
378
  mkdirSync(dirname(options.path), { recursive: true, mode: 0o700 });
379
+ mkdirSync(dirname(temporaryPath), { recursive: true, mode: 0o700 });
383
380
  try {
384
381
  writeFileSync(temporaryPath, serialized, { encoding: "utf8", mode: 0o600 });
385
382
  chmodSync(temporaryPath, 0o600);
@@ -397,7 +394,7 @@ export function createTelegramChannelPostJournalStore(options) {
397
394
  };
398
395
  const mutate = (operation) => {
399
396
  try {
400
- return withTelegramFileTransaction(`${options.path}.transaction`, () => operation(read()));
397
+ return withTelegramFileTransaction(publicationPaths.transactionPath, () => operation(read()));
401
398
  }
402
399
  catch (error) {
403
400
  if (error instanceof TelegramChannelPostJournalError)
@@ -52,15 +52,10 @@ export interface CommandTemplateExecResult {
52
52
  }
53
53
  export type CommandTemplateRiskLabel = "risk.shell" | "risk.eval" | "risk.broad_fs_write" | "risk.destructive_fs" | "risk.network" | "risk.external_side_effect" | "risk.long_running" | "risk.platform_specific" | "risk.secret_touching";
54
54
  export declare function normalizeCommandTemplateConfig(config: CommandTemplateConfig): CommandTemplateObjectConfig;
55
- export declare function resolveInheritedDefaultReferences(ownDefaults: Record<string, unknown> | undefined, inheritedDefaults: Record<string, unknown> | undefined, runtimeValues?: Record<string, unknown>): Record<string, unknown> | undefined;
56
- export declare function resolveCommandTemplateRepeat(value: number | string | undefined, values?: Record<string, unknown>): number | undefined;
57
- export declare function getCommandTemplateRepeatDefaults(index: number, repeat: number): Record<string, string>;
58
55
  export declare function expandCommandTemplateConfigs(config: CommandTemplateConfig, inherited?: Pick<CommandTemplateObjectConfig, "args" | "defaults">): CommandTemplateLeafConfig[];
59
56
  export declare function getCommandTemplateWarnings(config: CommandTemplateConfig): string[];
60
57
  export declare function getCommandTemplateRiskLabels(config: CommandTemplateConfig): CommandTemplateRiskLabel[];
61
- export declare function getCommandTemplateDefaults(config: CommandTemplateConfig | undefined): Record<string, string>;
62
58
  export declare function splitCommandTemplate(input: string): string[];
63
- export declare function expandCommandTemplateExecutable(command: string, cwd: string): string;
64
59
  export declare function shouldRunCommandTemplateNode(value: boolean | string | undefined, values: Record<string, unknown>): boolean;
65
60
  export declare function substituteCommandTemplateToken(token: string, values: Record<string, unknown>, missingLabel?: string, depth?: number): string;
66
61
  export declare function execCommandTemplate(command: string, args: string[], options?: CommandTemplateExecOptions): Promise<CommandTemplateExecResult>;
@@ -43,7 +43,7 @@ function normalizeCommandTemplateDefaults(defaults) {
43
43
  }
44
44
  return normalized;
45
45
  }
46
- export function resolveInheritedDefaultReferences(ownDefaults, inheritedDefaults, runtimeValues = {}) {
46
+ function resolveInheritedDefaultReferences(ownDefaults, inheritedDefaults, runtimeValues = {}) {
47
47
  if (!ownDefaults || !inheritedDefaults)
48
48
  return ownDefaults;
49
49
  const resolved = { ...ownDefaults };
@@ -59,7 +59,7 @@ export function resolveInheritedDefaultReferences(ownDefaults, inheritedDefaults
59
59
  }
60
60
  return resolved;
61
61
  }
62
- export function resolveCommandTemplateRepeat(value, values = {}) {
62
+ function resolveCommandTemplateRepeat(value, values = {}) {
63
63
  if (value === undefined)
64
64
  return undefined;
65
65
  if (typeof value === "number") {
@@ -228,7 +228,7 @@ function getLeafCommandTemplateWarnings(config) {
228
228
  function pad(value, width) {
229
229
  return String(value).padStart(width, "0");
230
230
  }
231
- export function getCommandTemplateRepeatDefaults(index, repeat) {
231
+ function getCommandTemplateRepeatDefaults(index, repeat) {
232
232
  const prev = (index - 1 + repeat) % repeat;
233
233
  const next = (index + 1) % repeat;
234
234
  const values = {
@@ -329,7 +329,7 @@ function parseCommandTemplatePlaceholderContent(content) {
329
329
  ...(match[2] !== undefined ? { inlineDefault: match[2] } : {}),
330
330
  };
331
331
  }
332
- export function getCommandTemplateDefaults(config) {
332
+ function getCommandTemplateDefaults(config) {
333
333
  const normalizedConfig = config
334
334
  ? normalizeCommandTemplateConfig(config)
335
335
  : undefined;
@@ -406,7 +406,7 @@ export function splitCommandTemplate(input) {
406
406
  words.push(current);
407
407
  return words;
408
408
  }
409
- export function expandCommandTemplateExecutable(command, cwd) {
409
+ function expandCommandTemplateExecutable(command, cwd) {
410
410
  if (command === "~")
411
411
  return homedir();
412
412
  if (command.startsWith("~/"))